fix(agents): redact secrets in tool call output to Control UI#72319
Conversation
Greptile SummaryThis 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 Confidence Score: 5/5Safe 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 |
steipete
left a comment
There was a problem hiding this comment.
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[].textinsidesanitizeToolResult(). - The actual event plumbing has at least two remaining plaintext paths.
Findings:
-
Tool input arguments are still emitted raw to the Control UI tool card.
handleToolExecutionStart()sendsdata.argsunchanged on thetoolstream, the UI copiesdata.argsintoentry.args, thenbuildToolCardSidebarContent()renderscard.inputTextas the “Tool input” section. So any tool invocation that carries a secret in args, for exampleapiKey,token,Authorization, config writes, credential setup, or provider calls, still exposes it before any result exists. This PR only redacts final/partial result text. Seesrc/agents/pi-embedded-subscribe.handlers.tools.ts:630,ui/src/ui/app-tool-stream.ts:491,ui/src/ui/app-tool-stream.ts:519, andui/src/ui/chat/tool-cards.ts:217. -
Exec final output still uses raw
details.aggregated.sanitizeToolResult()redactscontent[].text, buthandleToolExecutionEnd()choosesexecDetails.aggregatedfrom the original rawresult.detailsbefore emitting command item summaries andcommand_outputevents.bash/execresults carry command stdout indetails.aggregated, socat ~/.openclaw/openclaw.jsoncan still leave the redacted content path and leak through the command-output path. Seesrc/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, andsrc/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.aggregatedbefore using it asoutput, command itemsummary, orcommand_output.output. - Either redact
detailsrecursively insanitizeToolResult()or drop details from UI-facing tool events if the UI only needs content. This should cover details-only andcontent: []tool results too. - Add regression tests in
pi-embedded-subscribe.handlers.tools.test.ts, not onlypi-embedded-subscribe.tools.test.ts: one for secret args in a start event, one for execdetails.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.
|
@steipete I fixed my pr, could you please review? |
|
@greptile-app |
|
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. |
|
@steipete could you please review? |
5e4fb7d to
4b20478
Compare
|
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 One remaining security-boundary gap before this should close #72283: CI note: the current |
4b20478 to
0584184
Compare
|
@steipete please review |
BunsDev
left a comment
There was a problem hiding this comment.
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-
detailsstrings. - Exec
command_outputnow reads from the sanitized result path. redactToolPayloadText()forces tool-surface redaction while preserving configuredlogging.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.tspnpm 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
left a comment
There was a problem hiding this comment.
Re-approved current head 254305002fbb27b43294911bebacd2c80bc7f42e after adding the remaining handler-level regression requested in review.
Additional coverage added:
- configured
logging.redactPatternsare applied to Control UI tool payload redaction even when normal logging redaction is off - details-only tool results are redacted before the
toolresult 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 passedpnpm 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.
3ca112e to
64cb78d
Compare
steipete
left a comment
There was a problem hiding this comment.
Approved current head 64cb78d1cf after the final maintainer fixup.
Verified code paths:
- tool start args are redacted before the Control UI
toolstart event - partial/final tool results are deep-redacted before Control UI emission
- exec
details.aggregatedand patch summaries read from the sanitized result path - configured
logging.redactPatternsare 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 passedpnpm check:changed— passed on the rebased head
|
Landed via squash after rebasing onto current
Thanks @volcano303 and @BunsDev. |
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]>
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]>
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]>
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]>
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]>
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]>
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) andskills.updateresponses — 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 (
readof a config file,bash/execrunningcat, plugin/MCP tools dumping their state) emits the plaintext over the agent-event websocket.Fix
sanitizeToolResult()insrc/agents/pi-embedded-subscribe.tools.tsis the chokepoint every tool result passes through on its way to the Control UI — bothhandleToolExecutionUpdate(partial) andhandleToolExecutionEnd(final) call it beforeemitAgentEvent.This PR runs each text content block through the existing
redactSensitiveText(..., { mode: "tools" })helper fromsrc/logging/redact.tsbefore truncation. That utility already covers the patterns called out in the issue — JSONapiKey, 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.tscovering:"apiKey": "…"fieldOPENROUTER_API_KEY=…assignmentsk-…,ghp_…)Authorization: Bearer …headerVerification
pnpm exec vitest run src/agents/pi-embedded-subscribe.tools.test.ts— 8/8 passedpnpm tsgo:core:test— exit 0, no diagnostics