diff --git a/vulnhunter-agent/agent/_url.py b/vulnhunter-agent/agent/_url.py index 1ddbe17..3482123 100644 --- a/vulnhunter-agent/agent/_url.py +++ b/vulnhunter-agent/agent/_url.py @@ -23,6 +23,9 @@ _RAW_TOKEN_RE = re.compile( r"(ghp_|gho_|ghu_|ghs_|ghr_|github_pat_|sk-ant-)[A-Za-z0-9_-]+" ) +_FORM_SECRET_RE = re.compile( + r"(?i)(client[_-]?secret\"?\s*[=:]\s*)[\"']?[^\s,}&\"']+[\"']?" +) def redact(text: str) -> str: @@ -42,6 +45,7 @@ def redact(text: str) -> str: s = _BEARER_RE.sub(r"\1***", s) s = _QUERY_TOKEN_RE.sub(r"\1***", s) s = _RAW_TOKEN_RE.sub(r"\1***", s) + s = _FORM_SECRET_RE.sub(r"\1***", s) # Residual risk (VULN-012, CWE-532): redaction is pattern-based over # enumerated token formats; a novel/unknown secret format not in the pass # list above would still pass through to the audit stream. diff --git a/vulnhunter-agent/tests/verify_012_audit_redaction.py b/vulnhunter-agent/tests/verify_012_audit_redaction.py index 1e1d27f..9ef4cea 100644 --- a/vulnhunter-agent/tests/verify_012_audit_redaction.py +++ b/vulnhunter-agent/tests/verify_012_audit_redaction.py @@ -5,6 +5,8 @@ headers, access_token query params, and raw token prefixes. """ +import pytest + from agent._url import redact @@ -38,6 +40,27 @@ def test_raw_token_prefixes_redacted_prefix_preserved(): assert prefix in out, f"prefix {prefix} should be preserved for triage" +@pytest.mark.parametrize( + "text", + [ + "client_secret=SUPERSECRET&grant_type=x", + "client_secret: SUPERSECRET", + "CLIENT_SECRET=SUPERSECRET", + '{"client_secret": "SUPERSECRET"}', + '{"client_secret":"SUPERSECRET"}', + 'client_secret = "SUPERSECRET"', + "client_secret='SUPERSECRET'", + "client-secret=SUPERSECRET", + "clientSecret=SUPERSECRET", + ], +) +def test_client_secret_redacted(text): + out = redact(text) + assert "SUPERSECRET" not in out + assert "***" in out + assert redact(out) == out # idempotent + + def test_benign_text_unchanged(): assert redact("just a normal log line about issue #42") == ( "just a normal log line about issue #42"