Skip to content

fix(webui): stop leaking full auth token to stdout at startup (AUDIT-C4)#259

Merged
xlabtg merged 3 commits intoxlabtg:mainfrom
konard:issue-258-e9283fa33870
Apr 22, 2026
Merged

fix(webui): stop leaking full auth token to stdout at startup (AUDIT-C4)#259
xlabtg merged 3 commits intoxlabtg:mainfrom
konard:issue-258-e9283fa33870

Conversation

@konard
Copy link
Copy Markdown

@konard konard commented Apr 22, 2026

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. Any
centralized log sink — journalctl, Docker log driver, tsx --log-file, CI
artifacts, teleton --debug > log.txt — therefore persisted a valid 7-day
session 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/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 — 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 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 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)

  1. Start the WebUI: npm start
  2. Observe stdout:
    [WebUI] URL: http://127.0.0.1:7777/auth/exchange?token=<PLAINTEXT_TOKEN>
    [WebUI] Token: abcd...wxyz (use Bearer header for API access)
    
  3. grep "$AUTH_TOKEN" logs/*.log returns the full token.

Verification (after fix)

  • Logger output now shows:
    [WebUI] URL:   http://127.0.0.1:7777/auth/exchange
    [WebUI] Token: abcd...wxyz (use Bearer header for API access)
    [WebUI] One-time exchange link printed to stderr below (not logged).
    
  • Raw stderr (not captured by pino) shows:
    >>> One-time link: http://127.0.0.1:7777/auth/exchange?token=<PLAINTEXT_TOKEN>
    
  • grep "$AUTH_TOKEN" against any log.* output returns zero matches.

Acceptance criteria

  • Full token no longer appears in any log.* output.
  • grep "$AUTH_TOKEN" logs/*.log returns zero matches.
  • Unit test captures logger output during boot and asserts the full token
    is absent (stderr is allowed because it is explicitly not-logged).
  • AUDIT_REPORT.md updated.
  • One-shot exchange token invalidation (deferred — separate issue, as
    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.
  • Manual smoke test: the exchange link is still usable from an interactive
    terminal (raw stderr), but never captured by the logger.

Fixes #258

konard added 2 commits April 22, 2026 19:57
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).
@konard konard changed the title [WIP] audit-c4-auth-token-in-stdout fix(webui): stop leaking full auth token to stdout at startup (AUDIT-C4) Apr 22, 2026
@konard konard marked this pull request as ready for review April 22, 2026 20:06
@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

Solution summary

PR is clean and mergeable. No CI checks yet reported — likely just triggered, or requires a repo-owner approval label for external-fork pull_request_target. Nothing more I can do on CI from here. The work is complete.

Summary:

  • Bug: src/webui/server.ts logged URL: .../auth/exchange?token=<plaintext> via log.info, leaking a 7-day session token to journalctl/Docker/CI logs.
  • Fix: Logger now emits only the /auth/exchange URL (no token) and the masked token. The one-time link with the full token goes out via raw process.stderr.write, which bypasses pino entirely.
  • Regression test: src/webui/__tests__/server-auth-token-log.test.ts boots the server on an ephemeral port, captures everything through addLogListener, and asserts the plaintext token never appears in any log message.
  • Quality gates: 2932 tests pass, lint clean, typecheck clean, prettier clean.
  • PR: fix(webui): stop leaking full auth token to stdout at startup (AUDIT-C4) #259 — now marked ready for review.

This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $3.331991

📊 Context and tokens usage:

  • 97.2K / 1M (10%) input tokens, 19.7K / 128K (15%) output tokens

Total: (96.8K + 4.5M cached) input tokens, 19.7K output tokens, $3.331991 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (1713KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

🔄 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.

@konard
Copy link
Copy Markdown
Author

konard commented Apr 22, 2026

🔄 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:

  • 45.7K / 1M (5%) input tokens, 5.6K / 128K (4%) output tokens

Total: (40.1K + 970.5K cached) input tokens, 5.6K output tokens, $0.876650 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (2382KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

audit-c4-auth-token-in-stdout

2 participants