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.
Uh oh!
There was an error while loading. Please reload this page.
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.