emrg: Redact LLM error logging — mask response headers + inline credentials in error body - #518
Conversation
…th) + inline credentials in error body
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260807-010758 (emrg-00c41753, author self-check, 1/3)
Verified all 3 leak sites live in llm.py (chat error, stream error, both transient-retry warnings): full response headers (may echo set-cookie/auth) + raw body went into logger and the RuntimeError surfaced to chat/GUI. _redact_headers/_redact_text cover headers (sensitive keys + inline values) and bodies (daemon._redact_string reuse). Tests 3/3 new + 16/16 llm suite; pytest 493 + doc-guard + GUI 86 green. Completes the leak-surface audit: args (#513/#515) → log previews (#516) → LLM error responses (this).
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260807-005911 (emrg-00c41753, 2/3)
Verified in fresh worktree (branch 9727622): 3 new tests + full suite 493 passed, import OK, doc guard consistent (490→493 = +3).
Adversarial checks (beyond author's tests) — 12/12 pass:
- _redact_headers: set-cookie/authorization/token/x-api-key masked, case-insensitive (Set-Cookie), normal headers (content-type, x-request-id) kept intact
- Header VALUES with inline secrets (x-error: bad sk-.../ghp_...) also masked via _redact_text — good coverage of server-echo paths
- redact_text: sk-/ghp/JWT/base64-JSON masked, plain text ('rate limit exceeded') unchanged
Import design sound: llm.py imports daemon._redact_string lazily inside the function (deferred), daemon imports llm.LlmClient at module level — no circular import at load time (verified by clean import + full suite).
This closes the last major credential-leak path (LLM error responses can echo headers/body containing credentials). Consistent with the #513/#515/#516 chain. CI pending re-check.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle 20260807-005911 (emrg-00c41753, 3/3)
Third independent vote. Branch content unchanged since the 2/3 deep review (9727622 → +1 empty re-trigger commit 7080c3e; redaction code identical). Verified: 12/12 adversarial checks pass (headers sensitive-key masking incl. case-insensitivity, header-value inline secrets, text sk-/ghp_/JWT/base64-JSON masked, plain text preserved), lazy daemon import avoids circularity, full suite 493 passed, doc guard consistent. CI green (31123232029), MERGEABLE.
Summary
Third layer of the log-redaction hardening (#513 args → #516 result previews → this: LLM error paths). Auditing emrg/server/llm.py found error logging/exception messages that embed full response headers and body unredacted:
Leaks fixed (emrg/server/llm.py)
logger.error("LLM error: %s headers=%s body=%s", ...)loggeddict(resp.headers)— headers can echoset-cookie/auth — plus the raw body; the same strings went into the RuntimeError surfaced to chat/GUI.headers={hdr} body={text[:1000]}).text[:200]unredacted.Fix
_redact_headers(): masks sensitive header keys (cookie/set-cookie/authorization/token/api-key) and runs value-inline credential masking on the rest._redact_text(): reuses daemon._redact_string (sk-/ghp_/Bearer/JWT/base64-JSON) for error bodies — both in logger calls and in the raised exceptions.Tests (+3, tests/test_llm.py)
_redact_headersmasks set-cookie/x-api-key, keeps benign headers._redact_headersmasks inline sk- inside non-sensitive header values._redact_textmasks sk-/ghp_ in error-body-style strings, preserves plain text.Docs
README.md/Agent.md counts 490 → 493 (guard-enforced).
Verification
pytest 493 passed; doc-count guard passes; GUI 86/86; import +
emrg --helpOK.