From 6fb48866b88a4d803b0e9212733d7652f5b93150 Mon Sep 17 00:00:00 2001 From: User Date: Fri, 21 Aug 2026 07:09:11 +0200 Subject: [PATCH] fix: bound journal credential scan --- docs/install/dwh-auth-server.md | 24 ++++++++++++++++++------ scripts/test-verify-dwh-auth-docs.sh | 13 ++++++++++++- scripts/verify-dwh-auth-docs.sh | 15 +++++++++++---- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/docs/install/dwh-auth-server.md b/docs/install/dwh-auth-server.md index 9f5f40cb..cfa85518 100644 --- a/docs/install/dwh-auth-server.md +++ b/docs/install/dwh-auth-server.md @@ -276,21 +276,33 @@ emette solo PASS/FAIL. since=$(date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%SZ) if sudo python3 -c ' import pathlib, subprocess, sys +max_journal_bytes = 1_048_576 +max_journal_lines = 10_000 try: actual_keys = {pathlib.Path(path).read_bytes() for path in sys.argv[2:]} - result = subprocess.run( + process = subprocess.Popen( ["journalctl", "-u", "dwh-auth", "--since", sys.argv[1], "--no-pager", "--output=cat"], - check=False, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, ) except OSError: raise SystemExit(2) -if result.returncode != 0: +bytes_seen = 0 +try: + for line_number, line in enumerate(process.stdout, 1): + if line_number > max_journal_lines or bytes_seen + len(line) > max_journal_bytes: + process.kill() + process.wait() + raise SystemExit(2) + bytes_seen += len(line) + if b"thtdwh_v1" in line or b"secret_sha256" in line or any(key in line for key in actual_keys): + raise SystemExit(1) +finally: + if process.poll() is None: + process.kill() + process.wait() +if process.returncode != 0: raise SystemExit(2) -for line in result.stdout.splitlines(keepends=True): - if b"thtdwh_v1" in line or b"secret_sha256" in line or any(key in line for key in actual_keys): - raise SystemExit(1) ' "$since" "$v1_key_file" "$legacy_key_file"; then printf 'journal_actual_key_scan=PASS\n' else diff --git a/scripts/test-verify-dwh-auth-docs.sh b/scripts/test-verify-dwh-auth-docs.sh index d18eb60c..56af3ed6 100755 --- a/scripts/test-verify-dwh-auth-docs.sh +++ b/scripts/test-verify-dwh-auth-docs.sh @@ -178,7 +178,18 @@ expect_global_replacement_rejected missing_exact_header_bytes docs/install/dwh-a expect_replacement_rejected missing_actual_key_journal_scan docs/install/dwh-auth-server.md "journal_actual_key_scan=PASS" "journal_generic_scan=PASS" expect_replacement_rejected missing_journal_actual_key_match docs/install/dwh-auth-server.md "any(key in line for key in actual_keys)" "False" -expect_replacement_rejected missing_journal_returncode_guard docs/install/dwh-auth-server.md "if result.returncode != 0:" "if result.returncode == 0:" +expect_replacement_rejected missing_journal_returncode_guard docs/install/dwh-auth-server.md "if process.returncode != 0:" "if process.returncode == 0:" +journal_contract_source="$fixture_root/docs/install/dwh-auth-server.md" +if grep -Fq 'result.stdout.splitlines' "$journal_contract_source" \ + || ! grep -Fq 'for line_number, line in enumerate(process.stdout, 1):' "$journal_contract_source" \ + || ! grep -Fq 'max_journal_bytes = 1_048_576' "$journal_contract_source" \ + || ! grep -Fq 'max_journal_lines = 10_000' "$journal_contract_source" \ + || ! grep -Fq 'bytes_seen + len(line) > max_journal_bytes' "$journal_contract_source" \ + || ! grep -Fq 'line_number > max_journal_lines' "$journal_contract_source" \ + || ! grep -Fq 'process.kill()' "$journal_contract_source"; then + report_fail journal_streaming_limits +fi +report_pass journal_streaming_limits expect_replacement_rejected missing_socket_legacy_probe docs/install/dwh-auth-server.md "socket_legacy=PASS" "socket_legacy=SKIPPED" expect_replacement_rejected missing_https_post_revoke_v1 docs/install/dwh-auth-server.md "https_v1_post_revoke=PASS" "https_v1_post_revoke=SKIPPED" expect_replacement_rejected missing_https_post_revoke_legacy docs/install/dwh-auth-server.md "https_legacy_post_revoke=PASS" "https_legacy_post_revoke=SKIPPED" diff --git a/scripts/verify-dwh-auth-docs.sh b/scripts/verify-dwh-auth-docs.sh index 48a9ddf2..38b4d155 100755 --- a/scripts/verify-dwh-auth-docs.sh +++ b/scripts/verify-dwh-auth-docs.sh @@ -34,7 +34,7 @@ for label, relative in docs.items(): text[label] = path.read_text(encoding="utf-8") requirements = { - "server": ["key_output=/root/dwh-auth-provision/psd-mac-primary.key", "legacy_key_id=legacy-shared", "v1_header_file", "legacy_header_file", "random_header_file", "--header \"@", "registry_staging", "registry_previous", "registry_restore_rollback", "journal_actual_key_scan=PASS", "sys.argv[2:]", "read_bytes()", "subprocess.run", "stderr=subprocess.DEVNULL", "returncode != 0", "socket_v1=PASS", "socket_legacy=PASS", "https_v1_post_revoke=PASS", "https_legacy_post_revoke=PASS", "key_file_bytes=PASS", "dd if=\"$1\" bs=65536 status=none", "manifest", "journalctl", "systemctl disable --now", "trap", "/var/lib/dwh-auth", "root:dwh-auth", "2750", ".writer.lock", "0640", "/run/dwh-auth/verify.sock", "0660", "systemd", "key create", "key list", "key status", "key revoke", "check", "backup", "rollback", "disinstallazione", "rest_api", "postgres_direct", "ssh_tunnel"], + "server": ["key_output=/root/dwh-auth-provision/psd-mac-primary.key", "legacy_key_id=legacy-shared", "v1_header_file", "legacy_header_file", "random_header_file", "--header \"@", "registry_staging", "registry_previous", "registry_restore_rollback", "journal_actual_key_scan=PASS", "sys.argv[2:]", "read_bytes()", "subprocess.Popen", "stderr=subprocess.DEVNULL", "returncode != 0", "max_journal_bytes = 1_048_576", "max_journal_lines = 10_000", "subprocess.Popen", "stdout=subprocess.PIPE", "for line_number, line in enumerate(process.stdout, 1):", "bytes_seen + len(line) > max_journal_bytes", "line_number > max_journal_lines", "process.kill()", "process.wait()", "socket_v1=PASS", "socket_legacy=PASS", "https_v1_post_revoke=PASS", "https_legacy_post_revoke=PASS", "key_file_bytes=PASS", "dd if=\"$1\" bs=65536 status=none", "manifest", "journalctl", "systemctl disable --now", "trap", "/var/lib/dwh-auth", "root:dwh-auth", "2750", ".writer.lock", "0640", "/run/dwh-auth/verify.sock", "0660", "systemd", "key create", "key list", "key status", "key revoke", "check", "backup", "rollback", "disinstallazione", "rest_api", "postgres_direct", "ssh_tunnel"], "client": ["Workspace management", "Validate workspace source", "Test workspace connections", "Save entered secrets", "Forget stored value", "API_KEY_FILE", "THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE", "TLS_CA_FILE", "/rpc/ping", "rest_api", "postgres_direct", "ssh_tunnel", "401", "503", "rotazione", "revoca"], "tls": ["self-issued", ".it", ".com", "SAN", "TLS_CA_FILE", "openssl x509 -noout -fingerprint -sha256", "fuori banda", "rinnovo", "curl -k"], "rollout": ["v1=2xx", "legacy=2xx", "v1=2xx post-revoca", "legacy=401 post-revoca", "file header curl protetti 0600", "PASS/FAIL", "no raw diff", "Task 9", "Task 10", "IN_DISCUSSION", "postgres_direct", "rest_api", "legacy-shared", "nginx -t", "204", "401", "503", "Qdrant", "Ollama", "rollback"], @@ -108,12 +108,19 @@ if re.search(r"sudo tar[^\n]*-C /var/lib[^\n]*(?:-x|--extract)[^\n]*registry_bac journal_steps = ( - "subprocess.run(", + "subprocess.Popen(", "[\"journalctl\", \"-u\", \"dwh-auth\", \"--since\", sys.argv[1]", "sys.argv[2:]", "stderr=subprocess.DEVNULL", - "if result.returncode != 0:", - "result.stdout.splitlines(keepends=True)", + "stdout=subprocess.PIPE", + "max_journal_bytes = 1_048_576", + "max_journal_lines = 10_000", + "for line_number, line in enumerate(process.stdout, 1):", + "bytes_seen + len(line) > max_journal_bytes", + "line_number > max_journal_lines", + "process.kill()", + "process.wait()", + "if process.returncode != 0:", "any(key in line for key in actual_keys)", ) if any(step not in text["server"] for step in journal_steps):