3.3 KiB
Credential scrubbing in error/log text
local_deep_research.security.log_sanitizer scrubs credentials out of
exception/error strings before they reach a client (HTTP/SSE/JSON
responses) or the logs. It is a runtime, defense-in-depth sanitizer — not a
git/CI secret scanner (use gitleaks
for that, which this repo already runs in pre-commit/CI).
The two layers
sanitize_error_message(text)— a regex first-pass over_CREDENTIAL_PATTERNSfor common credential shapes (API-key query params,Authorization:/x-api-key:headers,user:pass@host, and well-known token prefixes:sk-/pk-, GoogleAIza/ya29., GitHubghp_/github_pat_, AWSAKIA/ASIA, Slackxox*-, JWTs).sanitize_error_for_client()composes it with control-char stripping + length capping.redact_secrets(text, *known_literals)— the backstop. When you hold the actual secret value (e.g. the configured API key), pass it here so it is scrubbed regardless of shape. This is the real guarantee for known secrets; the regex layer is best-effort for unknown ones.
Always pair them ("dual-scrub") on any path that surfaces secret-adjacent
text — via scrub_error(error, *known_literals) (security.log_sanitizer),
which composes both passes plus the defensive guards (a raising __str__
or a non-string secret cannot crash the except handler). Engine
subclasses use self._scrub_error(error), which resolves the engine's
_secret_attrs and delegates to the same helper. Don't hand-inline the
two calls: per-site copies are how scrub passes drift apart.
Design constraints (why it is curated, not exhaustive)
This runs on arbitrary, possibly attacker-influenced strings at runtime, so:
- No ReDoS. Every pattern must scale linearly — prefer prefix-anchored, single-quantifier regexes; avoid nested quantifiers / ambiguous alternations. Spot-check new patterns on a 200k-char adversarial input.
- Over-redaction is the safe failure; under-redaction (a leak) is not — but gratuitous over-redaction harms log readability, so patterns are anchored/length-floored to avoid eating prose.
- Keep the set small and high-signal. Matching gitleaks' full 100+ rules
at runtime is overkill and raises false positives; the
redact_secretsbackstop covers the long tail for known secrets.
Keeping the patterns current
The prefix regexes mirror the canonical, actively-maintained gitleaks
ruleset — gitleaks' own upstream config/gitleaks.toml
(https://github.com/gitleaks/gitleaks), not this repo's root
.gitleaks.toml (which only configures the gitleaks scan run in
pre-commit/CI). To refresh when a provider introduces a new token format:
- Find the rule in gitleaks' upstream
config/gitleaks.toml(or detect-secrets). - Adapt the regex to a prefix-anchored, ReDoS-safe form here; redact to
[REDACTED_KEY]. - Add a positive (redacts) and a negative (prose not over-redacted) case
to
tests/security/test_log_sanitizer.py. - Run
pytest tests/security/and a 200k-char ReDoS spot-check.
A periodic (e.g. quarterly) glance at gitleaks' changelog for new widely-used token prefixes is enough — this layer only needs the formats that plausibly appear in this app's error/log text.