feat(egress): add location, context snippets, and token redaction to DLP logging

Each DLP block/warn now reports where the match was found (body,
authorization header, response body) and includes a context snippet:
SNIPPET_CONTEXT chars before and after the match, with the matched
value replaced by REDACT ("********").

scan_token_patterns/scan_known_secrets/scan_naive_injection all gain
`location` and `context` fields on their ScanResult returns. The
outbound scanner takes `auth_header` as a separate kwarg so the two
locations are scanned and reported independently.

redact_tokens() is added to dlp_detectors and used in egress_addon.py
to scrub token patterns and provisioned secrets from host/path fields
before they appear in any log output (level 1 and 2).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 14:47:42 -04:00
parent 79212481c9
commit 86b0a4d285
4 changed files with 210 additions and 35 deletions
+14 -2
View File
@@ -92,6 +92,8 @@ class Decision:
class ScanResult:
severity: str # "block" or "warn"
reason: str
location: str = "" # where the match was found, e.g. "body", "authorization header"
context: str = "" # surrounding text with the match replaced by REDACT
# ---------------------------------------------------------------------------
@@ -529,6 +531,8 @@ def scan_outbound(
route: Route,
body: str | bytes,
environ: typing.Mapping[str, str],
*,
auth_header: str = "",
) -> ScanResult | None:
# Lazy import to avoid circular deps and keep dlp_detectors optional
# at import time (the sidecar copies it flat alongside this file).
@@ -540,12 +544,20 @@ def scan_outbound(
text = body if isinstance(body, str) else body.decode("utf-8", errors="replace")
if _detector_enabled(route.outbound_detectors, "token_patterns"):
result = scan_token_patterns(text)
if auth_header:
result = scan_token_patterns(auth_header, location="authorization header")
if result is not None:
return result
result = scan_token_patterns(text, location="body")
if result is not None:
return result
if _detector_enabled(route.outbound_detectors, "known_secrets"):
result = scan_known_secrets(text, env=environ)
if auth_header:
result = scan_known_secrets(auth_header, location="authorization header", env=environ)
if result is not None:
return result
result = scan_known_secrets(text, location="body", env=environ)
if result is not None:
return result