4dde61e8dbcc8402f4dc245de120f514b1a2dc21
15
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5b9674987f |
fix(backup): restore mihomo-config.yaml from backup zip
Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase fix(backup): restore mihomo-config.yaml from backup zip Backup zip contains 3m-ui.db + mihomo-config.yaml + backup-meta.txt. RestoreDatabase only extracted 3m-ui.db — mihomo-config.yaml was silently dropped. After restoring a backup, the mihomo config file on disk was stale (from before the backup was taken). Fix: - extractDBFromZip now returns both dbContent and mihomoCfg - RestoreDatabase takes mihomoCfgPath parameter - After restoring the DB, also writes mihomo-config.yaml if the zip contained it and the path is configured - Mihomo config write failure does NOT fail the whole restore (DB is already restored; operator can manually restore config) - api.go passes h.mihomoCfg to RestoreDatabase |
||
|
|
da5a0f67d1 |
fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback
Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. fix(audit-r1r2): visual config JSON inline, batch name check, monthly reset, credential sync, restore temp, access profile fallback Round 1+2 exhaustive audit found 6 bugs. All fixed. HIGH: - VisualConfig ProxyEntry: Go JSON doesn't support yaml inline. Added custom UnmarshalJSON/MarshalJSON to capture/emit flat fields. - BatchCreate: name uniqueness only checked enabled listeners. Removed filter to match UNIQUE index scope. - Monthly reset: could fire multiple times per day if DB locked. Now claims the day in a transaction BEFORE resetting. - Batch/delete ops: silently ignored notifyCredentialsChanged errors. Now logs them. MEDIUM: - Global AccessProfile: PublicPort/SNI/ClientFingerprint/ALPN loaded but never used as fallbacks. Now applied when per-listener empty. - RestoreDatabase: temp file not cleaned on failure. Added defer remove. go build/vet/test: all PASS. gofmt: clean. |
||
|
|
266b59bee1 |
fix(backup): RestoreDatabase now handles zip format (the actual backup format)
ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works fix(backup): RestoreDatabase now handles zip format (the actual backup format) ROOT CAUSE: ExportBackup produces a .zip file (3m-ui-backup-*.zip containing 3m-ui.db + mihomo-config.yaml + backup-meta.txt). But RestoreDatabase treated the uploaded file as a raw SQLite database — it io.Copy'd the entire zip file into the DB path without extracting. On restart, GORM/SQLite opened the zip file as a DB, found it wasn't a valid SQLite format, and the panel failed to start. This is why 'restoring backup and restarting 3m-ui' made the panel unable to boot — the DB file was actually a zip archive. Fix: RestoreDatabase now: 1. Reads the upload into memory (128 MiB limit enforced by API handler) 2. Checks the PK\x03\x04 zip magic bytes 3. If zip: extracts 3m-ui.db from the archive via zip.NewReader 4. If not zip: treats as raw SQLite DB (backward compatible) 5. Writes extracted DB content to the temp file + atomic rename This fixes both scenarios: - User downloads .zip backup → uploads it for restore → now works - User has a raw .db file → uploads it for restore → still works |
||
|
|
7addcdc5c7 |
fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup
Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK fix(non-listener): enforcement retry, DB restore warning, security headers, config placeholder, sensitive key gaps, dead UI options, bot API server, update.sh dedup Non-listener comprehensive audit found 4 Medium + 4 Low issues. All fixed. VLESS+REALITY and listener schema untouched. == Medium == M-NEW-C1: Enforcement doesn't retry failed config regen traffic/enforcement.go CheckAndEnforce updated lastBlocked BEFORE calling RegenerateConfig. If regen failed, lastBlocked was already set, so next tick saw no change and never retried — over-quota users stayed connected. Fix: update lastBlocked only after successful regen; preserve old state on failure for retry. M-NEW-C2: DB restore orphans running process's SQLite handle system/api.go RestoreDatabase renames the DB file, but the running panel keeps writing to the old (unlinked) inode. Added prominent [WARNING] log + restart_required:true + *** RESTART REQUIRED *** message in response. In-place GORM reconnect not feasible; restart is the correct approach. M-NEW-G1: Backend doesn't send CSP/security HTTP headers CSP was only in index.html meta tag. Added SecurityHeaders() middleware setting X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: same-origin, X-XSS-Protection: 1; mode=block on all responses. CSP NOT set on API responses (meta CSP handles the SPA shell). M-NEW-A1: Default config.yaml has placeholder secrets backend/config/config.yaml contained DefaultJWTSecret and DefaultCredentialKey (rejected by Validate → startup failure for developers). Replaced with CHANGE_ME placeholders + comments explaining use install.sh or openssl rand -hex 32. == Low == L-NEW-B1: isSensitiveSettingKey heuristic gaps panel_settings.go blocklist missing 'key' and 'auth' substrings. Added (api deliberately excluded — too broad). L-NEW-D1: enabled_events UI has dead options Settings.tsx Select included node_down/node_up/block/unblock which have no backend effect. Removed. Kept only implemented events: login/cpu/memory/crash/expiry/traffic. L-NEW-D3: Bot loop hardcodes api.telegram.org bot.go getUpdates/deleteWebhook hardcoded the official Telegram API URL. Now takes apiBase parameter; loop() passes tgClient.apiBase() so custom api_server setting is respected for long-polling. L-NEW-H1: update.sh duplicate CONFIG_DIR Line 11-12 had CONFIG_DIR defined twice. Removed duplicate. == Verification == - go build ./... : PASS - go vet ./... : PASS - go test ./... : 16 packages all PASS - npm run build : PASS - gofmt : clean - sh -n update.sh : OK |
||
|
|
c852f0266a |
fix(backend): path traversal, mihomo secret, template injection, acme path
- H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). fix(backend): path traversal, mihomo secret, template injection, acme path - H-3: subpage loadTemplate accepted arbitrary theme_dir, reachable from the unauthenticated /api/v1/client/sub/:token?html=1 endpoint. Add sanitizeThemeDir() restricting to /var/lib/3m-ui/themes, /etc/3m-ui/themes, /usr/local/share/3m-ui/themes. Validated on both load and save. - M-1: Mihomo external-controller shipped with an empty secret. Even bound to loopback, any co-located unprivileged process could drive the REST API. Generate a process-scoped random secret via crypto/rand (ControllerSecret()); traffic collector already reads it from the generated config. - H-4: ACMECommand / WARPTemplate / ReverseProxyTemplate used fmt.Sprintf with unescaped user input (shell / YAML / nginx injection if admin account is hijacked). Add isValidDomain() and shellQuote(). ACMECommand and WARPTemplate now return (string, error). WARP uses yaml.Marshal for structured output. api.go handlers updated. - LOW-12: ACME cert_file/key_file paths now restricted to an allowlist (/etc/ssl/, /etc/letsencrypt/, /etc/3m-ui/, /var/lib/3m-ui/, etc.). |
||
|
|
475c1fc172 |
style: gofmt for CI
style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI style: gofmt for CI |
||
|
|
885a68e57f | chore: remove internal parity wording from comments and UI | ||
|
|
31e12d56db |
fix: dashboard CPU/memory/disk metrics units and sampling
- Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping fix: dashboard CPU/memory/disk metrics units and sampling - Memory and disk used/total now returned as bytes (frontend formatBytes) - Clamp percents to 0–100; improve CPU sampling (200ms + fallback) - Disk usage probes /, HOME, panel data dirs - Dashboard: safer formatBytes and Progress clamping |
||
|
|
479ef4e0fa |
feat: more parity — sing-box sub, login TG notify, WARP, search
- Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more 3x-ui parity — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more 3x-ui parity — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more 3x-ui parity — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more 3x-ui parity — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search feat: more panel features — sing-box sub, login TG notify, WARP, search - Native sing-box JSON subscription (?target=singbox) + UA auto-detect - Enhanced ?format=info with multi-format links and blocked flag - Telegram notify on panel login (settings toggle) - Telegram /backup command; listener list ?q= / enabled filters - System WARP WireGuard YAML template endpoint - Frontend: Sing-box share link, login notify switch, listener search |
||
|
|
af555c9dcc |
feat: parity — batch users, sub headers, telegram cmds, geofiles
- User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: 3x-ui parity — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: 3x-ui parity — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: 3x-ui parity — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: 3x-ui parity — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition (3x-ui style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n feat: panel features — batch users, sub headers, telegram cmds, geofiles - User list filters (?q/enabled/online/blocked) and POST /users/batch - Subscription Profile-Title / Content-Disposition ( style) - Telegram: /restart /deldepleted /search - System: POST /system/geofiles/update (MetaCubeX geoip/geosite) - Frontend: batch toolbar + geo update button + i18n |
||
|
|
99a4cb6587 | fix: cap database restore upload size | ||
|
|
26bbce128b | feat: system reverse-proxy/ACME template API handlers | ||
|
|
d238ba470e | feat: migrate PanelSetting/RemoteServer; ACME/nginx templates | ||
|
|
ce9b718c2e | feat: system backup/restore API and OpenAPI embed helper | ||
|
|
5b4cb63a73 |
fix: align listener generation with mihomo official spec
- Port now supports mihomo ports format (single/range/list) - Add explicit RoutingMark field support - Auto-propagate UDP flag for vmess/vless/trojan/anytls/trusttunnel - Update validator to accept port ranges - Sync visual config and client converter port types - Rename module from dzx941 to kazeyukiro - Fix missing ExpiresAt in JWTClaims - Fix nodeDTO Port type to string - Fix frontend tsconfig and add missing dependencies |