diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c6805..6600a84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Version numbers follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html - **Delta token status hid the source count** — the "Tokens saved" line under the Δ Delta scan toggle always showed the bare translation ("Tokens gemt") because the source count only existed in the JS fallback string, which is ignored whenever the lang key exists. The translations now carry a `{n}` placeholder ("Tokens gemt for {n} kilde(r)") substituted in `checkDeltaStatus()`, and the row gained a "?" hint bubble explaining what the saved change-tokens do and that "Clear tokens" forces the next scan to be a full scan. -- **Stale data-file paths in README** — delta tokens were documented as `~/.gdpr_scanner_delta.json` and the SMTP password/Fernet key as `~/.gdpr_scanner_smtp.json` / `~/.gdpr_scanner_machine_id`; the actual locations have long been `~/.gdprscanner/delta.json`, `~/.gdprscanner/smtp.json`, and `~/.gdprscanner/machine_id`. +- **Stale data-file paths in docs and UI text** — README, SECURITY.md, MAINTAINER.md, the `--headless` argparse help (`--settings`, `--reset-db`, epilog), the DB-import replace warning/confirm strings (all three languages), and two code comments still referenced the pre-1.x flat dotfile layout (`~/.gdpr_scanner_delta.json`, `~/.gdpr_scanner_smtp.json`, `~/.gdpr_scanner_machine_id`, `~/.gdpr_scanner.db`). All now point to the actual locations under `~/.gdprscanner/` (`delta.json`, `smtp.json`, `machine_id`, `scanner.db`). The legacy-migration rename tables in `gdpr_scanner.py` intentionally keep the old names. --- diff --git a/MAINTAINER.md b/MAINTAINER.md index 37da92d..eea6d73 100644 --- a/MAINTAINER.md +++ b/MAINTAINER.md @@ -102,7 +102,7 @@ tests/ pytest test suite — 112 tests, all should pass. **Settings stats show 0 (Scanned / Flagged / Scans)** → `routes/database.py` → `db_stats()` — queries `flagged_items` and `scans` directly → Stats populate from existing DB on app start — no re-scan needed -→ If still 0 after a completed scan: check `~/.gdpr_scanner.db` exists and is not empty +→ If still 0 after a completed scan: check `~/.gdprscanner/scanner.db` exists and is not empty **File scan results not persisting to DB** → `scan_engine.py` → `run_file_scan()` — must call `_db.begin_scan()` not `start_scan()` diff --git a/SECURITY.md b/SECURITY.md index 9bea20f..ff6b22e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -54,9 +54,9 @@ Out of scope: ## Data Handling Notes for Security Researchers - CPR numbers are stored in the SQLite database as **SHA-256 hashes only** — never in plaintext -- SMTP passwords are stored in `~/.gdpr_scanner_smtp.json` with chmod 600 +- SMTP passwords are stored in `~/.gdprscanner/smtp.json` with chmod 600 - Microsoft OAuth tokens are stored in the MSAL token cache in `~/.gdpr_scanner_config.json` -- Scan results are stored locally in `~/.gdpr_scanner.db` — never transmitted externally +- Scan results are stored locally in `~/.gdprscanner/scanner.db` — never transmitted externally - The web UI binds to `127.0.0.1` by default — it is not designed to be exposed to the internet --- diff --git a/app_config.py b/app_config.py index dc6c019..746e396 100644 --- a/app_config.py +++ b/app_config.py @@ -813,7 +813,7 @@ def clear_viewer_pin() -> None: # ── SMTP password encryption ───────────────────────────────────────────────── # The SMTP password is encrypted at rest using Fernet symmetric encryption. # The encryption key is derived from a stable machine-specific UUID stored in -# ~/.gdpr_scanner_machine_id. This key is only usable on the same machine — +# ~/.gdprscanner/machine_id. This key is only usable on the same machine — # the encrypted password cannot be decrypted if the config file is copied to # another host. diff --git a/gdpr_db.py b/gdpr_db.py index d0302cb..603a3b8 100644 --- a/gdpr_db.py +++ b/gdpr_db.py @@ -6,7 +6,7 @@ Stores scan results alongside the existing JSON cache. Neither replaces the other: JSON is fast and portable, SQLite enables querying, trending, and the data-subject index. -Database location: ~/.gdpr_scanner.db (configurable via DB_PATH) +Database location: ~/.gdprscanner/scanner.db (configurable via DB_PATH) Schema ------ diff --git a/gdpr_scanner.py b/gdpr_scanner.py index 6e3620a..8ce267f 100644 --- a/gdpr_scanner.py +++ b/gdpr_scanner.py @@ -1593,10 +1593,10 @@ Headless (scheduled) usage: environment variables: M365_CLIENT_ID, M365_TENANT_ID, M365_CLIENT_SECRET or a settings JSON: --settings /path/to/settings.json - Scan options are loaded from ~/.gdpr_scanner_settings.json (saved automatically + Scan options are loaded from ~/.gdprscanner/settings.json (saved automatically after any interactive scan), or overridden in the --settings file. - SMTP config is loaded from ~/.gdpr_scanner_smtp.json (saved in the UI) or from + SMTP config is loaded from ~/.gdprscanner/smtp.json (saved in the UI) or from an 'smtp' key in the --settings file. Example cron (weekly, Mondays at 06:00): @@ -1631,7 +1631,7 @@ Example --settings file with SMTP: parser.add_argument("--output", default=".", help="Output directory for Excel export in headless mode (default: .)") parser.add_argument("--settings", default=None, - help="Path to a JSON settings file (overrides ~/.gdpr_scanner_settings.json)") + help="Path to a JSON settings file (overrides ~/.gdprscanner/settings.json)") parser.add_argument("--email-to", default=None, help="Comma-separated recipient addresses — send Excel report by email (headless only)") parser.add_argument("--retention-years", type=int, default=None, @@ -1639,7 +1639,7 @@ Example --settings file with SMTP: parser.add_argument("--fiscal-year-end", default=None, help="Fiscal year end as MM-DD for retention cutoff (e.g. 12-31 for Bogforingsloven). Omit for rolling window.") parser.add_argument("--reset-db", action="store_true", - help="Reset the results database (~/.gdpr_scanner.db) — permanently deletes all scan history, " + help="Reset the results database (~/.gdprscanner/scanner.db) — permanently deletes all scan history, " "dispositions, and deletion log. Prompts for confirmation unless --yes is also passed.") parser.add_argument("--yes", action="store_true", help="Skip confirmation prompts (use with --reset-db for scripted resets)") @@ -2144,7 +2144,7 @@ Example --settings file with SMTP: email_to = getattr(args, "email_to", None) if email_to: recipients = [r.strip() for r in email_to.replace(";", ",").split(",") if r.strip()] - # SMTP config: --settings file takes priority, then saved ~/.gdpr_scanner_smtp.json + # SMTP config: --settings file takes priority, then saved ~/.gdprscanner/smtp.json smtp_cfg = _load_smtp_config() if cfg.get("smtp"): smtp_cfg = {**smtp_cfg, **cfg["smtp"]} diff --git a/lang/da.json b/lang/da.json index c3b80bb..753c692 100644 --- a/lang/da.json +++ b/lang/da.json @@ -560,8 +560,8 @@ "m365_db_import_mode": "Tilstand:", "m365_db_import_merge": "Sammenflet (sikker)", "m365_db_import_replace": "Erstat (fuld gendannelse)", - "m365_db_import_replace_warn": "⚠ Erstatningstilstand sletter alle eksisterende scanningsdata inden gendannelse. Sørg for at have en sikkerhedskopi af ~/.gdpr_scanner.db først.", - "m365_db_import_replace_confirm": "Erstatningstilstand sletter ALLE eksisterende scanningsdata og gendanner fra arkivet.\\n\\nSørg for at have en manuel sikkerhedskopi af ~/.gdpr_scanner.db.\\n\\nFortsæt?", + "m365_db_import_replace_warn": "⚠ Erstatningstilstand sletter alle eksisterende scanningsdata inden gendannelse. Sørg for at have en sikkerhedskopi af ~/.gdprscanner/scanner.db først.", + "m365_db_import_replace_confirm": "Erstatningstilstand sletter ALLE eksisterende scanningsdata og gendanner fra arkivet.\\n\\nSørg for at have en manuel sikkerhedskopi af ~/.gdprscanner/scanner.db.\\n\\nFortsæt?", "m365_db_import_no_file": "Vælg venligst en ZIP-fil først.", "m365_db_importing": "Importerer…", "m365_db_imported": "Importeret", diff --git a/lang/de.json b/lang/de.json index 9daea0e..dcb3b1f 100644 --- a/lang/de.json +++ b/lang/de.json @@ -560,8 +560,8 @@ "m365_db_import_mode": "Modus:", "m365_db_import_merge": "Zusammenführen (sicher)", "m365_db_import_replace": "Ersetzen (vollständige Wiederherstellung)", - "m365_db_import_replace_warn": "⚠ Der Ersetzungsmodus löscht alle vorhandenen Scandaten vor der Wiederherstellung. Stellen Sie sicher, dass Sie zuerst eine Sicherungskopie von ~/.gdpr_scanner.db haben.", - "m365_db_import_replace_confirm": "Der Ersetzungsmodus löscht ALLE vorhandenen Scandaten und stellt aus dem Archiv wieder her.\\n\\nStellen Sie sicher, dass Sie eine manuelle Sicherungskopie von ~/.gdpr_scanner.db haben.\\n\\nFortfahren?", + "m365_db_import_replace_warn": "⚠ Der Ersetzungsmodus löscht alle vorhandenen Scandaten vor der Wiederherstellung. Stellen Sie sicher, dass Sie zuerst eine Sicherungskopie von ~/.gdprscanner/scanner.db haben.", + "m365_db_import_replace_confirm": "Der Ersetzungsmodus löscht ALLE vorhandenen Scandaten und stellt aus dem Archiv wieder her.\\n\\nStellen Sie sicher, dass Sie eine manuelle Sicherungskopie von ~/.gdprscanner/scanner.db haben.\\n\\nFortfahren?", "m365_db_import_no_file": "Bitte wählen Sie zuerst eine ZIP-Datei aus.", "m365_db_importing": "Importiere…", "m365_db_imported": "Importiert", diff --git a/lang/en.json b/lang/en.json index 42b72e7..e6dbbcc 100644 --- a/lang/en.json +++ b/lang/en.json @@ -560,8 +560,8 @@ "m365_db_import_mode": "Mode:", "m365_db_import_merge": "Merge (safe)", "m365_db_import_replace": "Replace (full restore)", - "m365_db_import_replace_warn": "⚠ Replace mode will erase all existing scan data before restoring. Make sure you have a backup of ~/.gdpr_scanner.db first.", - "m365_db_import_replace_confirm": "Replace mode will erase ALL existing scan data and restore from the archive.\\n\\nMake sure you have a manual backup of ~/.gdpr_scanner.db.\\n\\nProceed?", + "m365_db_import_replace_warn": "⚠ Replace mode will erase all existing scan data before restoring. Make sure you have a backup of ~/.gdprscanner/scanner.db first.", + "m365_db_import_replace_confirm": "Replace mode will erase ALL existing scan data and restore from the archive.\\n\\nMake sure you have a manual backup of ~/.gdprscanner/scanner.db.\\n\\nProceed?", "m365_db_import_no_file": "Please select a ZIP file first.", "m365_db_importing": "Importing…", "m365_db_imported": "Imported", diff --git a/static/js/scan.js b/static/js/scan.js index 329b70d..5e9b4db 100644 --- a/static/js/scan.js +++ b/static/js/scan.js @@ -67,7 +67,7 @@ async function doImportDB() { } if (mode === 'replace') { if (!confirm(t('m365_db_import_replace_confirm', - 'Replace mode will erase ALL existing scan data and restore from the archive.\n\nMake sure you have a manual backup of ~/.gdpr_scanner.db.\n\nProceed?'))) return; + 'Replace mode will erase ALL existing scan data and restore from the archive.\n\nMake sure you have a manual backup of ~/.gdprscanner/scanner.db.\n\nProceed?'))) return; } btn.disabled = true; stat.style.color = 'var(--muted)'; diff --git a/templates/index.html b/templates/index.html index bb72bf5..dc2cf81 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1538,7 +1538,7 @@ document.addEventListener('DOMContentLoaded', applyI18n); - +