fix(webui): stop leaking full auth token to stdout at startup (AUDIT-C4)#259
fix(webui): stop leaking full auth token to stdout at startup (AUDIT-C4)#259xlabtg merged 3 commits intoxlabtg:mainfrom
Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: xlabtg#258
The WebUI `start()` handler previously logged the full `/auth/exchange?token=...` URL via `log.info`, so any centralized log sink (journalctl, Docker log driver, `tsx --log-file`, CI artifacts, `teleton --debug > log.txt`) permanently captured a valid 7-day session token — full API access to the WebUI, wallet, and autonomous endpoints. Log output now contains only the `/auth/exchange` URL (no token) and the masked token. The one-time exchange link with the full token is written via raw `process.stderr.write`, which bypasses the pino logger entirely — stdout, the WebUI SSE stream, pino-pretty, file redirection, and any `LogListener` never see it. Adds `src/webui/__tests__/server-auth-token-log.test.ts` — a regression test that boots the server on an ephemeral port, captures every log entry through `addLogListener`, and asserts the plaintext token never appears in any log message (while still being present on raw stderr).
Solution summaryPR is clean and mergeable. No CI checks yet reported — likely just triggered, or requires a repo-owner approval label for external-fork Summary:
This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $3.331991📊 Context and tokens usage:
Total: (96.8K + 4.5M cached) input tokens, 19.7K output tokens, $3.331991 cost 🤖 Models used:
📎 Log file uploaded as Gist (1713KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
🔄 Auto-restart triggered (iteration 1)Reason: Merge conflicts detected Starting new session to address the issues. Auto-restart-until-mergeable mode is active. Will continue until PR becomes mergeable. |
# Conflicts: # .gitkeep
🔄 Auto-restart-until-mergeable Log (iteration 1)This log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $0.876650📊 Context and tokens usage:
Total: (40.1K + 970.5K cached) input tokens, 5.6K output tokens, $0.876650 cost 🤖 Models used:
📎 Log file uploaded as Gist (2382KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
Summary
Fixes #258 (AUDIT-C4 — critical).
WebUIServer.start()used to print the full/auth/exchange?token=<plaintext>URL via
log.info, even though the next line already masked the token. Anycentralized log sink — journalctl, Docker log driver,
tsx --log-file, CIartifacts,
teleton --debug > log.txt— therefore persisted a valid 7-daysession token (
COOKIE_MAX_AGE). That token grants full WebUI API access,including the wallet and autonomous task endpoints.
Changes
src/webui/server.ts: logger output now contains only the/auth/exchangeURL (no token) and the masked token. The one-time exchange link with the full
token is written via raw
process.stderr.write(...), which bypasses the pinologger entirely — it never reaches stdout, the WebUI SSE stream, pino-pretty,
file redirection, or any
LogListener.src/webui/__tests__/server-auth-token-log.test.ts: regression test thatboots the server on an ephemeral port, captures every log entry through
addLogListener, and asserts the plaintext token never appears in any logmessage (while still being written to raw stderr so an interactive operator
can still click it).
AUDIT_REPORT.md: AUDIT-C4 marked as ✅ Fixed (issue audit-c4-auth-token-in-stdout #258).How to reproduce (before fix)
npm startgrep "$AUTH_TOKEN" logs/*.logreturns the full token.Verification (after fix)
grep "$AUTH_TOKEN"against anylog.*output returns zero matches.Acceptance criteria
log.*output.grep "$AUTH_TOKEN" logs/*.logreturns zero matches.is absent (stderr is allowed because it is explicitly not-logged).
AUDIT_REPORT.mdupdated.suggested in the original audit finding).
Test plan
npm test— 2932/2932 pass, including 2 new regression tests.npm run lint— clean.npm run typecheck— clean.npm run format:check— clean.terminal (raw stderr), but never captured by the logger.
Fixes #258