Files
learningcircuit--local-deep…/docs/developing/credential-scrubbing.md
T
wehub-resource-sync 6e7352b6f7
Create Release / version-check (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / test-gate (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Failing after 0s
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Version Auto-Bump / version-bump (push) Failing after 0s
Backwards Compatibility / Verify Encryption Constants (push) Failing after 1s
Backwards Compatibility / PyPI Version Compatibility (push) Failing after 1s
Backwards Compatibility / Database Migration Tests (push) Failing after 0s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
CodeQL Advanced / Analyze (python) (push) Failing after 1s
Docker Tests (Consolidated) / detect-changes (push) Failing after 1s
Docker Tests (Consolidated) / Build Test Image (push) Failing after 1s
Sync repo labels / sync-labels (push) Failing after 0s
MCP Server Tests / MCP Server Tests (push) Failing after 1s
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been skipped
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:25:48 +08:00

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

  1. sanitize_error_message(text) — a regex first-pass over _CREDENTIAL_PATTERNS for common credential shapes (API-key query params, Authorization:/ x-api-key: headers, user:pass@host, and well-known token prefixes: sk-/pk-, Google AIza/ya29., GitHub ghp_/github_pat_, AWS AKIA/ASIA, Slack xox*-, JWTs). sanitize_error_for_client() composes it with control-char stripping + length capping.
  2. 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_secrets backstop 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:

  1. Find the rule in gitleaks' upstream config/gitleaks.toml (or detect-secrets).
  2. Adapt the regex to a prefix-anchored, ReDoS-safe form here; redact to [REDACTED_KEY].
  3. Add a positive (redacts) and a negative (prose not over-redacted) case to tests/security/test_log_sanitizer.py.
  4. 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.