Skip to content

fix: redact URL query secrets in error text shown by the web and TUI clients - #2588

Merged
cliffhall merged 3 commits into
v2/mainfrom
v2/fix/2490-redact-displayed-error-urls
Oct 5, 2026
Merged

cliffhall merged 3 commits into
v2/mainfrom
v2/fix/2490-redact-displayed-error-urls

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #2490

What

Error text the web and TUI clients draw on screen is now URL-query-redacted, the same guarantee the CLI's stderr envelope got in #2488 and the web Network log already had: a server or SDK error quoting https://…?code=… shows code=%5BREDACTED%5D instead of the live value, while the path and non-sensitive params stay readable.

Follows the issue's suggested shape:

  • Core helper. redactUrlsInText (and its private EMBEDDED_URL_PATTERN / trailing-punctuation scan) moved verbatim from clients/cli/src/error-handler.ts into core/mcp/fetchTracking.ts, next to redactUrlQuery. The CLI now imports it from core; its envelope behavior is unchanged and its existing tests still pass. The helper's own tests live in clients/web/src/test/core/mcp/fetchTracking.test.ts.
  • Web: one display boundary. utils/errorFormat's errorMessage() and formatErrorDetails() now redact. Every site that put err instanceof Error ? err.message : String(err) (or a bare err.message) from a caught error into a toast or inline error now calls errorMessage(err) instead: App.tsx, useConnectionLifecycle, useOAuthRecovery, useServerCommands, useMcpApps, useExportActions, useImportClientConfig, useServerJsonImport, SkillsScreen, ServerConfigModal, ServerRemoveConfirmModal, ResourceLink, ListLoadError, AppsScreen. The mid-session lastError toast and the re-auth banner detail (formatOAuthFailureDetail) are redacted as well.
  • TUI: one display boundary. New clients/tui/src/utils/errorText.ts (errorMessage, redactErrorText, redactedJson). Used for the connect/OAuth/disconnect errors in App.tsx (plus core's lastError where it reaches InfoTab), AuthTab, ResourcesTab, PromptsTab, SkillsTab. The three *TestModals redact at render, both the red error line and the pretty-printed error-details JSON.

Judgment calls

  • Redaction is applied only to displayed text. Classification (instanceof, isEmaClientNotConfiguredError, the deep-link "already exists" check) still reads the original error.
  • Not changed, because they are out of scope: the Network/Requests log error field and the Protocol panel's raw JSON-RPC messages. Those are recorded traffic, not error text formatted for display. Messages the app writes itself with no server text in them (e.g. UrlElicitationLoopError) were also left alone.
  • No screenshots. The change only rewrites the text of an error that has to embed a secret-bearing URL, and none of the showcase servers produce one. The tests assert the redacted strings.

Tests

  • fetchTracking.test.ts: redactUrlsInText cases (prose, trailing punctuation, the CLI error-envelope URL redaction uses a quadratic regex on server-controlled text (CodeQL, ReDoS) #2540 long-run case, mixed-case schemes, comma-joined URLs, apostrophes and quotes, URLs inside serialized JSON, untouched text).
  • errorFormat.test.ts: errorMessage / formatErrorDetails redact.
  • clients/tui/__tests__/errorText.test.ts: tests for the new helper.
  • Local gate: every stage passed except as noted below. local:validate, verify:skills:cli, coverage:{web,cli,tui,launcher} (all changed files ≥90 on all four dimensions), verify:build-gate and verify:bundle-externals are green. The run was in a shared sandbox with many concurrent agent gates. Three things to know:
    • secret-store-selection.test.ts > isOnMountPoint > is false when the path itself can't be resolved fails only in this sandbox. The worktree sits under a bind mount (/proc/self/mountinfo), so a cwd-relative path is legitimately "on a mount". It is unrelated to this diff.
    • inspectorClient-timeout-diagnostics failed under load. This is the known flake tracked in Flaky integration test: inspectorClient-timeout-diagnostics 'emits connectionDiagnosticsChange' reads an undefined GET log entry #2580. It passes when run alone.
    • The sandbox's HTTP proxy tunnels loopback test fetches, so the integration suites were run with HTTP(S)_PROXY unset. With the proxy set, transport.test.ts and server-extra-coverage.test.ts fail with Proxy response (502).
    • smoke, smoke:web:firefox and local:storybook were not run locally. I queued them behind the machine-wide gate lease three times, and each attempt hit the lease's 45-minute cap with 10+ gates ahead of it. CI runs all three. The diff only changes the text of error messages, so it shouldn't affect the smokes or stories.

🤖 Generated with Claude Code

…clients

Move the CLI's redactUrlsInText into core/mcp/fetchTracking.ts beside
redactUrlQuery and import it from there in the CLI. Route on-screen error
text through it at one display boundary per client: the web client's
utils/errorFormat errorMessage/formatErrorDetails (now used by every toast
and inline error that rendered err.message), and a new TUI
utils/errorText helper used by App, the tabs and the test modals.

Closes #2490

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Oct 5, 2026
@cliffhall
cliffhall requested a balanced review from Copilot October 5, 2026 09:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Several display paths still bypass redaction, and deep-link classification now uses altered text.

Review effort: Balanced
Findings: 5 High severity · 1 Medium severity

Open (6)
What changed in this PR

Centralizes URL-query secret redaction and applies it to displayed web/TUI errors while preserving CLI behavior.

Changes:

  • Moves free-text URL redaction into shared core code.
  • Adds web and TUI display-boundary helpers.
  • Adds helper-focused regression tests.
File Description
core/​mcp/​fetchTracking.ts Adds shared free-text URL redaction.
clients/​cli/​src/​error-handler.ts Uses the shared redactor.
clients/​web/​src/​utils/​errorFormat.ts Redacts formatted display errors.
clients/​web/​src/​utils/​errorFormat.test.ts Tests web error redaction.
clients/​web/​src/​test/​core/​mcp/​fetchTracking.test.ts Tests shared redaction behavior.
clients/​web/​src/​App.tsx Redacts application-level toasts.
clients/​web/​src/​hooks/​useConnectionLifecycle.ts Redacts connection errors.
clients/​web/​src/​hooks/​useExportActions.ts Redacts replay failures.
clients/​web/​src/​hooks/​useImportClientConfig.ts Redacts import failures.
clients/​web/​src/​hooks/​useMcpApps.ts Redacts app resource failures.
clients/​web/​src/​hooks/​useOAuthRecovery.ts Redacts OAuth recovery errors.
clients/​web/​src/​hooks/​useServerCommands.tsx Redacts command errors.
clients/​web/​src/​hooks/​useServerJsonImport.ts Redacts server-import errors.
clients/​web/​src/​components/​screens/​AppsScreen/​AppsScreen.tsx Redacts rendered app errors.
clients/​web/​src/​components/​screens/​SkillsScreen/​SkillsScreen.tsx Redacts skill-operation errors.
clients/​web/​src/​components/​elements/​ListLoadError/​ListLoadError.tsx Redacts list errors.
clients/​web/​src/​components/​groups/​ResourceLink/​ResourceLink.tsx Redacts resource errors.
clients/​web/​src/​components/​groups/​ServerConfigModal/​ServerConfigModal.tsx Redacts configuration failures.
clients/​web/​src/​components/​groups/​ServerRemoveConfirmModal/​ServerRemoveConfirmModal.tsx Redacts removal failures.
clients/​tui/​src/​utils/​errorText.ts Adds TUI display-redaction helpers.
clients/​tui/​__tests__/​errorText.test.ts Tests TUI helpers.
clients/​tui/​src/​App.tsx Redacts primary TUI errors.
clients/​tui/​src/​components/​AuthTab.tsx Redacts authorization errors.
clients/​tui/​src/​components/​PromptsTab.tsx Redacts prompt failures.
clients/​tui/​src/​components/​PromptTestModal.tsx Redacts prompt test details.
clients/​tui/​src/​components/​ResourcesTab.tsx Redacts resource failures.
clients/​tui/​src/​components/​ResourceTestModal.tsx Redacts resource test details.
clients/​tui/​src/​components/​SkillsTab.tsx Redacts skill verification errors.
clients/​tui/​src/​components/​ToolTestModal.tsx Redacts tool test details.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread clients/tui/src/App.tsx
Comment thread clients/tui/src/components/SkillsTab.tsx
Comment thread clients/web/src/components/screens/SkillsScreen/SkillsScreen.tsx
Comment thread clients/web/src/hooks/useOAuthRecovery.ts
Comment thread clients/web/src/hooks/useServerCommands.tsx
Comment thread clients/web/src/hooks/useConnectionLifecycle.ts Outdated
Redact the TUI's nested OAuth failure and skills-list error, the web
skills-list alert, the failed step-up outcome and the pagination
ServerListReloadError toast. Classify the deep-link 409 on the raw
message and redact only the recorded copy, with a regression test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1: 6 findings, all in scope, all fixed in 411bb7f.

  • Nested TUI OAuth catch: authMsg is now redacted and reused in the EMA branch.
  • TUI skills-list error: now renders errorMessage(loadError).
  • Web skills-list alert: now renders errorMessage(loadError).
  • useOAuthRecovery failed step-up outcome: redacted the same way as the thrown path.
  • useServerCommands pagination ServerListReloadError toast: redacted.
  • Deep-link 409: classified on the raw message, only the recorded copy is redacted. Regression test added.

I also swept the remaining direct .message renders. What's left is app-authored copy (EmaClientNotConfiguredError), values already redacted upstream, or server-supplied non-error content (elicitation, progress and log messages), which is outside this issue's scope.

Re-ran local:validate, coverage:tui and coverage:web: green apart from the sandbox-only isOnMountPoint test described in the PR body.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread clients/tui/src/App.tsx
Comment thread clients/tui/src/App.tsx
Comment thread clients/tui/src/App.tsx
…eview

Redact the TUI's interactive-reauth and standard step-up OAuth catches
and the failed-revocation detail in both clients' clear warnings, with
regression tests for each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 2: 3 findings, all in scope, all fixed in 55a1fe8.

  • TUI interactive-reauth OAuth catch: redacted, with a regression test.
  • TUI clear flow revocation.detail: redacted, with a regression test. The web revocationSuffix had the same gap and is fixed and tested in the same commit.
  • TUI non-EMA step-up OAuth catch: redacted, with a regression test.

Round 1 missed these because they were multi-line ternaries. A multi-line sweep now finds no remaining x instanceof Error ? x.message : String(x) in either client.

Re-ran local:validate, coverage:tui and coverage:web. The only failures were the sandbox-only isOnMountPoint test and the known flake #2580.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Trailing punctuation can remain verbatim from sensitive query values, potentially exposing all or part of a secret.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (3)

Comment thread core/mcp/fetchTracking.ts
Comment on lines +163 to +165
return text.replace(EMBEDDED_URL_PATTERN, (match) => {
const end = trailingPunctuationStart(match);
return redactUrlQuery(match.slice(0, end)) + match.slice(end);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declining as out of scope for #2490. This PR moves redactUrlsInText into core unchanged. Splitting off trailing punctuation was a deliberate choice in #2488/#2540, so a URL that ends a sentence keeps its full stop, and the CLI's error envelope behaves the same way today. Changing that trade-off is new hardening of the shared helper, not a defect in what this PR added. It's also low-risk in practice: OAuth codes, access tokens and client secrets are base64url or hex, so they don't end in .,;:!?)]' (a JWT's dots are internal, never trailing). If we want to change it, it should be its own issue covering all three consumers.

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 3: 1 finding, declined as out of scope (reply in thread). It asks to change how the shared redactUrlsInText treats trailing punctuation. That behavior predates this PR (#2488/#2540), the helper was moved unchanged, and changing it would be new hardening across all three consumers.

Copilot review loop closed. The round held only an out-of-scope finding, so nothing was changed and another round would only re-argue scope. Rounds: 1 (6 fixed), 2 (3 fixed), 3 (1 declined).

@cliffhall
cliffhall merged commit 33323de into v2/main Oct 5, 2026
6 checks passed
@cliffhall
cliffhall deleted the v2/fix/2490-redact-displayed-error-urls branch October 5, 2026 14:28
cliffhall added a commit that referenced this pull request Oct 5, 2026
v2/main gained the #2568 CLI rollup PRs (incl. #2582 `--output`) and
#2587/#2588. Conflicts were import blocks in the TUI Prompts/Resources/
Skills tabs and ToolTestModal (#2430 / #2571 imports next to #2588's
errorText helpers) — both sides kept — and the CLI test README, where
v2/main's table is kept minus the `open-url.test.ts` row #2533 moved to
core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Redact URL query secrets in error text displayed by the web and TUI clients

2 participants