fix: mask credentials and non-public API endpoints in proxy logs (#255) - #274
Conversation
bili.log and the launcher tmp log (bili-proxy-${port}.log) received
plaintext secrets and non-public upstream endpoints:
- 'forward GET/POST -> <upstreamUrl>' logged the full upstream URL on
every request (always-on, not debug-gated)
- '-> upstream headers:' / '<- upstream response headers:' logged
authorization / x-api-key / cookie / set-cookie values verbatim
- formatUpstreamError embedded the raw upstream URL and the failing
host (ECONNREFUSED 192.168.x.x, ENOTFOUND relay.internal)
- mitm/CONNECT logs exposed tunneled upstream host:port
New src/log-mask.ts:
- credential headers (key|auth|token|cookie) -> '<masked N chars>'
- non-public API hosts -> '<private-host>' (port kept); well-known
public endpoints (openai, chatgpt, anthropic, deepseek, googleapis,
azure, mistral, groq, ...) stay verbatim for debuggability
- userinfo/query/hash stripped from logged URLs (key-leak vectors)
- error message/address fields scrubbed of non-public hostnames
debug: true in the launcher is intentionally kept (per #255 triage);
dump-file gating and GC are tracked in separate issues.
📦 Built Package ArtifactBranch: Option A — Install from npm PR tag (recommended)npm install -g billion-context@pr-274Each push to this PR publishes a new version under the Option B — Download artifact
tar xzf billion-context-pr274.tgz
npm install -g packageThis comment is automatically updated on each push. |
tunnelThrough logged err.message verbatim; OS/undici error text embeds
the endpoint ("connect ECONNREFUSED 10.0.0.5:8443", "getaddrinfo
ENOTFOUND relay.internal"), re-leaking the host the template just
masked. Add maskHostInText() to log-mask.ts and apply it to the two
tunnel error log lines; formatUpstreamError now reuses the same helper
(also fixes the bracketed/bare IPv6 mismatch in its scrub).
+2 tests: unit coverage of maskHostInText, e2e CONNECT tunnel failure
asserting no raw target host in any captured log line.
|
[bot] Reviewed PR on branch Verification (as submitted): Found one residual leak — fixed and pushed as
Same leak class Re-verified after fix: typecheck clean · 670/670 tests pass · build OK. Scope notes (agree with the issue): dump files still write Everything else checked clean: no other log lines in server.ts/launcher.ts/client-config.ts/ca.ts carry URLs or header values unmasked; the ACP_DEBUG client-hdr prefix8/suffix4 display is the intentional exception. Ready for human merge — I can't merge PRs per AGENTS.md. 🤖 ework agent · vllm-qwen/qwen3.8-27b |
Part B of #255 (this round's scope per triage): guarantee no sensitive info in proxy logs —
bili.logand the launcher tmp logbili-proxy-${port}.log.debug: truein the launcher is intentionally kept; dump-file gating and GC are tracked in separate issues.What was leaking (verified on v0.1.55)
forward POST → <url>(always-on, not debug-gated)→ upstream headers:(debug)authorization: Bearer sk-...,x-api-key,cookieverbatim← upstream response headers:(debug)set-cookieverbatimformatUpstreamErrorECONNREFUSED 192.168.x.x,ENOTFOUND relay.internal)host:portChanges
src/log-mask.ts— single masking module:key|auth|token|cookie) →<masked N chars>(length hint kept for debuggability)<private-host>(port kept — host is the secret, port isn't); well-known public endpoints (openai, chatgpt, anthropic, deepseek, googleapis, azure, mistral, groq, cohere, together, fireworks, x.ai, openrouter, huggingface, moonshot, zhipu, volcengine, aliyuncs, baidu, minimax) stay verbatimmessage/addressfields scrubbed of non-public hostnamessrc/server.ts— forward line, ws-reject line, unrecognized-path line (clientreq.urlcan embed an absolute upstream URL via/bili/), hdrLog, respLog all routed through the maskersrc/upstream-proxy.ts—formatUpstreamError:url=masked; error text/address scrubbed;proxy=field unchanged (existing behavior: host visible, creds redacted — it's a debug signal for which proxy was used, not the user's API endpoint)src/mitm.ts— all 9 CONNECT/tunnel/mitm log lines maskedtests/log-mask.test.ts— 11 tests: unit coverage of every masker + 2 end-to-end tests that boot the proxy against a mock upstream and assert no credential, no non-public host in ANY captured log line (debug on, log on)Intentionally NOT changed (scope)
debug: trueinsrc/launcher.ts— kept per triagedumps/,raw/) — content masking + gating → new issuebili.log.old→ new issueloop/core.ts,server.ts) not URL-scanned — bodies aren't expected to carry endpoints; noted in issueVerification
typecheckclean · 668/668 tests pass · build OK (rebased onto master @ v0.1.57)