Redact quoted JSON secret tokens in processing logs
This commit is contained in:
@@ -28,6 +28,9 @@ SENSITIVE_KEY_MARKERS = (
|
|||||||
"cookie",
|
"cookie",
|
||||||
)
|
)
|
||||||
SENSITIVE_TEXT_PATTERNS = (
|
SENSITIVE_TEXT_PATTERNS = (
|
||||||
|
re.compile(r"(?i)[\"']authorization[\"']\s*:\s*[\"']bearer\s+[^\"']+[\"']"),
|
||||||
|
re.compile(r"(?i)[\"']bearer[\"']\s*:\s*[\"'][^\"']+[\"']"),
|
||||||
|
re.compile(r"(?i)[\"'](?:api[_-]?key|token|secret|password)[\"']\s*:\s*[\"'][^\"']+[\"']"),
|
||||||
re.compile(r"(?i)\bauthorization\b\s*[:=]\s*bearer\s+[a-z0-9._~+/\-]+=*"),
|
re.compile(r"(?i)\bauthorization\b\s*[:=]\s*bearer\s+[a-z0-9._~+/\-]+=*"),
|
||||||
re.compile(r"(?i)\bbearer\s+[a-z0-9._~+/\-]+=*"),
|
re.compile(r"(?i)\bbearer\s+[a-z0-9._~+/\-]+=*"),
|
||||||
re.compile(r"\b[a-z0-9_-]{8,}\.[a-z0-9_-]{8,}\.[a-z0-9_-]{8,}\b", flags=re.IGNORECASE),
|
re.compile(r"\b[a-z0-9_-]{8,}\.[a-z0-9_-]{8,}\.[a-z0-9_-]{8,}\b", flags=re.IGNORECASE),
|
||||||
|
|||||||
@@ -237,6 +237,30 @@ class ProcessingLogRedactionTests(unittest.TestCase):
|
|||||||
self.assertNotIn(bearer_token, sanitized_text)
|
self.assertNotIn(bearer_token, sanitized_text)
|
||||||
self.assertNotIn(jwt_token, sanitized_text)
|
self.assertNotIn(jwt_token, sanitized_text)
|
||||||
|
|
||||||
|
def test_text_redaction_removes_json_formatted_secret_values(self) -> None:
|
||||||
|
"""JSON-formatted quoted secrets are fully removed from redacted log text."""
|
||||||
|
|
||||||
|
api_key_secret = "json-api-key-secret"
|
||||||
|
token_secret = "json-token-secret"
|
||||||
|
authorization_secret = "json-auth-secret"
|
||||||
|
bearer_secret = "json-bearer-secret"
|
||||||
|
json_text = (
|
||||||
|
"{"
|
||||||
|
f"\"api_key\":\"{api_key_secret}\","
|
||||||
|
f"\"token\":\"{token_secret}\","
|
||||||
|
f"\"authorization\":\"Bearer {authorization_secret}\","
|
||||||
|
f"\"bearer\":\"{bearer_secret}\""
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
sanitized = sanitize_processing_log_text(json_text)
|
||||||
|
self.assertIsNotNone(sanitized)
|
||||||
|
sanitized_text = sanitized or ""
|
||||||
|
self.assertIn("[REDACTED]", sanitized_text)
|
||||||
|
self.assertNotIn(api_key_secret, sanitized_text)
|
||||||
|
self.assertNotIn(token_secret, sanitized_text)
|
||||||
|
self.assertNotIn(authorization_secret, sanitized_text)
|
||||||
|
self.assertNotIn(bearer_secret, sanitized_text)
|
||||||
|
|
||||||
def test_response_schema_applies_redaction_to_existing_entries(self) -> None:
|
def test_response_schema_applies_redaction_to_existing_entries(self) -> None:
|
||||||
"""API schema validators redact sensitive fields from legacy stored rows."""
|
"""API schema validators redact sensitive fields from legacy stored rows."""
|
||||||
|
|
||||||
@@ -268,6 +292,50 @@ class ProcessingLogRedactionTests(unittest.TestCase):
|
|||||||
self.assertNotIn(bearer_token, response.prompt_text or "")
|
self.assertNotIn(bearer_token, response.prompt_text or "")
|
||||||
self.assertNotIn(jwt_token, response.response_text or "")
|
self.assertNotIn(jwt_token, response.response_text or "")
|
||||||
|
|
||||||
|
def test_response_schema_redacts_json_formatted_secret_values(self) -> None:
|
||||||
|
"""Response schema redacts quoted JSON secret forms from legacy text fields."""
|
||||||
|
|
||||||
|
api_key_secret = "legacy-json-api-key"
|
||||||
|
token_secret = "legacy-json-token"
|
||||||
|
authorization_secret = "legacy-json-auth"
|
||||||
|
bearer_secret = "legacy-json-bearer"
|
||||||
|
prompt_text = (
|
||||||
|
"{"
|
||||||
|
f"\"api_key\":\"{api_key_secret}\","
|
||||||
|
f"\"token\":\"{token_secret}\""
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
response_text = (
|
||||||
|
"{"
|
||||||
|
f"\"authorization\":\"Bearer {authorization_secret}\","
|
||||||
|
f"\"bearer\":\"{bearer_secret}\""
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
|
||||||
|
response = ProcessingLogEntryResponse.model_validate(
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"created_at": datetime.now(UTC),
|
||||||
|
"level": "info",
|
||||||
|
"stage": "summary",
|
||||||
|
"event": "response",
|
||||||
|
"document_id": None,
|
||||||
|
"document_filename": "sample-json.txt",
|
||||||
|
"provider_id": "provider",
|
||||||
|
"model_name": "model",
|
||||||
|
"prompt_text": prompt_text,
|
||||||
|
"response_text": response_text,
|
||||||
|
"payload_json": {"trace_id": "trace-2"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIn("[REDACTED]", response.prompt_text or "")
|
||||||
|
self.assertIn("[REDACTED]", response.response_text or "")
|
||||||
|
self.assertNotIn(api_key_secret, response.prompt_text or "")
|
||||||
|
self.assertNotIn(token_secret, response.prompt_text or "")
|
||||||
|
self.assertNotIn(authorization_secret, response.response_text or "")
|
||||||
|
self.assertNotIn(bearer_secret, response.response_text or "")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user