Skip to content

fix(agents): redact secrets in tool call output to Control UI#72319

Merged
steipete merged 6 commits into
openclaw:mainfrom
9876543210-tc-0123456789:fix/72283-redact-tool-call-output
Apr 27, 2026
Merged

fix(agents): redact secrets in tool call output to Control UI#72319
steipete merged 6 commits into
openclaw:mainfrom
9876543210-tc-0123456789:fix/72283-redact-tool-call-output

Conversation

@9876543210-tc-0123456789

Copy link
Copy Markdown
Contributor

Fixes #72283

Problem

Tool call results streamed to the Control UI render configuration JSON in plaintext, including provider API keys (OpenRouter, Ollama, Tavily…), channel credentials, and auth tokens. The same data is already redacted in /config show (config.get) and skills.update responses — tool call output was bypassing the redaction layer.

The leak is reproducible by asking the assistant to inspect plugin/model configuration: any tool whose text output happens to contain credentials (read of a config file, bash/exec running cat, plugin/MCP tools dumping their state) emits the plaintext over the agent-event websocket.

Fix

sanitizeToolResult() in src/agents/pi-embedded-subscribe.tools.ts is the chokepoint every tool result passes through on its way to the Control UI — both handleToolExecutionUpdate (partial) and handleToolExecutionEnd (final) call it before emitAgentEvent.

This PR runs each text content block through the existing redactSensitiveText(..., { mode: "tools" }) helper from src/logging/redact.ts before truncation. That utility already covers the patterns called out in the issue — JSON apiKey, ENV-style *_KEY=/*_TOKEN=/*_SECRET= assignments, Authorization: Bearer …, PEM blocks, and common token prefixes (sk-, ghp_, xox[baprs]-, AIza…, pplx-, gsk_, npm_, Telegram bot tokens) — and is the same utility used elsewhere for log redaction.

Production change: 1 import + 1 line. No new abstractions, no per-tool
patches.

Tests

Added 5 new cases to pi-embedded-subscribe.tools.test.ts covering:

  • JSON-style "apiKey": "…" field
  • ENV-style OPENROUTER_API_KEY=… assignment
  • Bare common-prefix tokens (sk-…, ghp_…)
  • Authorization: Bearer … header
  • Passthrough of non-sensitive text (no false positives)
  • Existing image-content stripping behavior preserved (regression check)

Verification

  • pnpm exec vitest run src/agents/pi-embedded-subscribe.tools.test.ts — 8/8 passed
  • pnpm tsgo:core:test — exit 0, no diagnostics
  • Manual repro of issue's steps (open Control UI, ask for plugin/model config, confirm credentials are masked in tool call output)

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S labels Apr 26, 2026
@greptile-apps

greptile-apps Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a credential-leak vulnerability where tool call results streamed to the Control UI were not being redacted, exposing API keys, tokens, and other secrets in plaintext. It introduces redactToolPayloadText (forces tools-mode regardless of logging.redactSensitive), a redactStringsDeep recursive walker for args sanitization, and extends sanitizeToolResult to cover the details field that was previously unredacted. The previous concern about user-configured redactPatterns being bypassed is resolved: the new helper explicitly merges cfg.redactPatterns with DEFAULT_REDACT_PATTERNS before calling redactSensitiveText.

Confidence Score: 5/5

Safe to merge — focused security fix with no P0/P1 findings and good test coverage.

The implementation correctly handles all identified edge cases: resolvePatterns(undefined) falls back to DEFAULT_REDACT_PATTERNS so defaults always apply; user patterns are merged explicitly; sanitizeToolResult now covers details in addition to content; handlers use sanitizedResult for exec and patch paths. No regressions introduced and tests verify both positive redaction and non-sensitive passthrough.

No files require special attention.

Reviews (2): Last reviewed commit: "fix(agents): redact tool args and exec d..." | Re-trigger Greptile

Comment thread src/agents/pi-embedded-subscribe.tools.ts Outdated

@steipete steipete left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Changes requested. This is the right bug to fix, but this patch is not complete enough for the security issue it claims to close.

Bug/behavior under review: #72283, sensitive credentials exposed through Control UI tool-call display and agent-event payloads when tools read or modify config/secrets.

What I verified:

  • PR head: a3a8f96490aaf083341cfeb682bf6c1c5896289b
  • Targeted tests pass: pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts -- -t 'sanitizeToolResult|emits command output events for exec results'
  • The new tests only prove redaction of result.content[].text inside sanitizeToolResult().
  • The actual event plumbing has at least two remaining plaintext paths.

Findings:

  1. Tool input arguments are still emitted raw to the Control UI tool card. handleToolExecutionStart() sends data.args unchanged on the tool stream, the UI copies data.args into entry.args, then buildToolCardSidebarContent() renders card.inputText as the “Tool input” section. So any tool invocation that carries a secret in args, for example apiKey, token, Authorization, config writes, credential setup, or provider calls, still exposes it before any result exists. This PR only redacts final/partial result text. See src/agents/pi-embedded-subscribe.handlers.tools.ts:630, ui/src/ui/app-tool-stream.ts:491, ui/src/ui/app-tool-stream.ts:519, and ui/src/ui/chat/tool-cards.ts:217.

  2. Exec final output still uses raw details.aggregated. sanitizeToolResult() redacts content[].text, but handleToolExecutionEnd() chooses execDetails.aggregated from the original raw result.details before emitting command item summaries and command_output events. bash/exec results carry command stdout in details.aggregated, so cat ~/.openclaw/openclaw.json can still leave the redacted content path and leak through the command-output path. See src/agents/pi-embedded-subscribe.handlers.tools.ts:819, src/agents/pi-embedded-subscribe.handlers.tools.ts:1007, src/agents/pi-embedded-subscribe.handlers.tools.ts:1024, and src/agents/pi-embedded-subscribe.handlers.tools.ts:1029.

Best fix shape:

  • Redact or structurally sanitize every string-bearing field that is emitted to UI/event subscribers, not just content[].text.
  • At minimum, add a sanitizeToolArgs() for the start event and use redacted args for Control UI display while keeping raw args only for local hook/runtime logic.
  • Redact execDetails.aggregated before using it as output, command item summary, or command_output.output.
  • Either redact details recursively in sanitizeToolResult() or drop details from UI-facing tool events if the UI only needs content. This should cover details-only and content: [] tool results too.
  • Add regression tests in pi-embedded-subscribe.handlers.tools.test.ts, not only pi-embedded-subscribe.tools.test.ts: one for secret args in a start event, one for exec details.aggregated, and one details-only result that would otherwise stringify raw details in the UI formatter.

Until those paths are covered, #72283 should stay open and this PR should not be treated as fixing the security issue.

@9876543210-tc-0123456789

Copy link
Copy Markdown
Contributor Author

@steipete I fixed my pr, could you please review?

@9876543210-tc-0123456789

Copy link
Copy Markdown
Contributor Author

@greptile-app

@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Codex automated review: keeping this open.

Keep this PR open. Current main still sends Control UI tool-event args and several tool-result/output paths without secret redaction, while this PR is an active fix candidate for the linked open security report #72283.

Best possible solution:

Keep this PR open and review it as a plausible core security fix. The best path is to land this PR or a maintainer-approved variant that redacts sensitive text before agent-event payloads reach the Control UI, with regression coverage for tool start args, partial/final tool results, exec command output, and patch summary derivation. Close #72283 only after the fix merges to main.

What I checked:

Remaining risk / open question:

Codex Review notes: model gpt-5.5, reasoning high; reviewed against 348728c28c1e.

@9876543210-tc-0123456789

Copy link
Copy Markdown
Contributor Author

@steipete could you please review?

@steipete

Copy link
Copy Markdown
Contributor

Re-reviewed current head after the follow-up.

The two explicit plaintext paths from Peter's previous review look addressed now: start-event args go through sanitizeToolArgs(), and exec / patch derived output reads sanitizedResult. The added handler tests cover those paths.

One remaining security-boundary gap before this should close #72283: sanitizeToolResult() still only recursively redacts details plus text content blocks. The tool-result event emits the whole sanitizedResult as data.result, so any top-level or non-details nested string a tool result carries can still bypass redaction. Suggested shape: deep-redact the whole result object first, then reapply the existing content-specific truncation / image stripping semantics on top. Please add a regression with something like { output: "OPENROUTER_API_KEY=...", metadata: { token: "ghp_..." } } and assert the emitted tool result has no plaintext secret.

CI note: the current checks-node-channels red is from src/channels/plugins/bundled.shape-guard.test.ts finding extensions/matrix/src/runtime-api.ts; that looks unrelated to this PR's touched files.

@9876543210-tc-0123456789
9876543210-tc-0123456789 force-pushed the fix/72283-redact-tool-call-output branch from 4b20478 to 0584184 Compare April 27, 2026 20:32
@9876543210-tc-0123456789

Copy link
Copy Markdown
Contributor Author

@steipete please review

@BunsDev BunsDev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the current head and added one narrow regression test for the previously requested-change case.

What I verified:

  • Tool start args are redacted before Control UI emission.
  • Tool result/update payloads are deep-redacted before Control UI emission, including top-level and nested non-details strings.
  • Exec command_output now reads from the sanitized result path.
  • redactToolPayloadText() forces tool-surface redaction while preserving configured logging.redactPatterns; added coverage for that custom-pattern path.

Local validation on fda1221879:

  • pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts
  • pnpm check:changed

The remaining GitHub checks are fresh for this head and still running, but the code path and the prior review concern are addressed.

@BunsDev BunsDev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-approved current head 254305002fbb27b43294911bebacd2c80bc7f42e after adding the remaining handler-level regression requested in review.

Additional coverage added:

  • configured logging.redactPatterns are applied to Control UI tool payload redaction even when normal logging redaction is off
  • details-only tool results are redacted before the tool result event is emitted

Validation on this head:

  • pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts — 41 tests passed
  • pnpm check:changed — passed before the final test-only commit; final commit only adds the details-only handler regression test and the focused suite passed again after commit

This addresses the previously requested paths: start args, exec aggregated output, full result/details emission, and configured custom redaction patterns.

Previously sanitizeToolResult only deep-redacted `details` and the text
inside `content` blocks. The tool-result event emits the entire
sanitizedResult as `data.result`, so any top-level field (e.g. `output`)
or non-details nested string (e.g. `metadata.token`) bypassed redaction.

Strip image data first to avoid scanning base64 payloads, then run the
full deep redaction pass over the result, and re-apply text truncation
on top. Add a regression test covering top-level + nested-non-details
secrets.

Addresses post-review feedback on openclaw#72319.
@steipete
steipete force-pushed the fix/72283-redact-tool-call-output branch from 3ca112e to 64cb78d Compare April 27, 2026 22:25

@steipete steipete left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approved current head 64cb78d1cf after the final maintainer fixup.

Verified code paths:

  • tool start args are redacted before the Control UI tool start event
  • partial/final tool results are deep-redacted before Control UI emission
  • exec details.aggregated and patch summaries read from the sanitized result path
  • configured logging.redactPatterns are merged with defaults for tool/UI payload redaction even when normal log redaction is off
  • primitive string results are now redacted, and top-level array results keep array shape while redacting nested strings

Local validation:

  • pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts — 44 tests passed
  • pnpm check:changed — passed on the rebased head

@steipete
steipete merged commit f3e8c50 into openclaw:main Apr 27, 2026
66 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed via squash after rebasing onto current main.

  • Source head: 64cb78d
  • Landed commit: f3e8c50
  • Local gate: pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts — 44 tests passed
  • Local gate: pnpm check:changed — passed on the rebased head
  • GitHub checks: green on the exact source head before merge

Thanks @volcano303 and @BunsDev.

ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
Fixes openclaw#72283.

- Redacts Control UI tool start args, partial/final result payloads, derived exec output, and patch summaries before event emission.
- Forces tool/UI payload redaction to include built-in patterns plus configured custom `logging.redactPatterns`.
- Covers object, details-only, primitive string, and top-level array tool-result shapes.

Tests:
- `pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts`
- `pnpm check:changed`

Co-authored-by: volcano303 <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
Fixes openclaw#72283.

- Redacts Control UI tool start args, partial/final result payloads, derived exec output, and patch summaries before event emission.
- Forces tool/UI payload redaction to include built-in patterns plus configured custom `logging.redactPatterns`.
- Covers object, details-only, primitive string, and top-level array tool-result shapes.

Tests:
- `pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts`
- `pnpm check:changed`

Co-authored-by: volcano303 <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
Fixes openclaw#72283.

- Redacts Control UI tool start args, partial/final result payloads, derived exec output, and patch summaries before event emission.
- Forces tool/UI payload redaction to include built-in patterns plus configured custom `logging.redactPatterns`.
- Covers object, details-only, primitive string, and top-level array tool-result shapes.

Tests:
- `pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts`
- `pnpm check:changed`

Co-authored-by: volcano303 <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
Fixes openclaw#72283.

- Redacts Control UI tool start args, partial/final result payloads, derived exec output, and patch summaries before event emission.
- Forces tool/UI payload redaction to include built-in patterns plus configured custom `logging.redactPatterns`.
- Covers object, details-only, primitive string, and top-level array tool-result shapes.

Tests:
- `pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts`
- `pnpm check:changed`

Co-authored-by: volcano303 <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
Fixes openclaw#72283.

- Redacts Control UI tool start args, partial/final result payloads, derived exec output, and patch summaries before event emission.
- Forces tool/UI payload redaction to include built-in patterns plus configured custom `logging.redactPatterns`.
- Covers object, details-only, primitive string, and top-level array tool-result shapes.

Tests:
- `pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts`
- `pnpm check:changed`

Co-authored-by: volcano303 <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Fixes openclaw#72283.

- Redacts Control UI tool start args, partial/final result payloads, derived exec output, and patch summaries before event emission.
- Forces tool/UI payload redaction to include built-in patterns plus configured custom `logging.redactPatterns`.
- Covers object, details-only, primitive string, and top-level array tool-result shapes.

Tests:
- `pnpm test src/agents/pi-embedded-subscribe.tools.test.ts src/agents/pi-embedded-subscribe.handlers.tools.test.ts`
- `pnpm check:changed`

Co-authored-by: volcano303 <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Security] Sensitive credentials exposed in plain text in Control UI tool call display

3 participants