fix(auto-reply): redact secrets in /debug show and /debug set output#93333
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 15, 2026, 11:36 AM ET / 15:36 UTC. Summary PR surface: Source +8, Tests +64. Total +72 across 2 files. Reproducibility: yes. at source level: current main serializes Review metrics: 1 noteworthy metric.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Land the narrow shared-redaction fix so debug override storage remains unchanged while chat-visible debug replies redact sensitive override values. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main serializes Is this the best way to solve the issue? Yes. Reusing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against da92615816b6. Label changesLabel justifications:
Evidence reviewedPR surface: Source +8, Tests +64. Total +72 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper automerge |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
@vincentkoc — when you're next sweeping the ready queue, this one (#93333) is a P1 security fix: it redacts secrets in |
PR openclaw#88496 routed /config show and /config set chat output through the shared schema-aware redaction path, but the sibling /debug commands in the same handler were left untouched. /debug show JSON-stringified the full runtime override tree verbatim and /debug set echoed the raw value, so a secret-shaped override (e.g. gateway.auth.token, channels.*.botToken) set via /debug set was rendered in plaintext to chat-visible output. Apply redactConfigObject(overrides, schema.uiHints) to the override tree before rendering /debug show, and reuse formatConfigSetValueLabel for the /debug set acknowledgement, matching the existing /config redaction contract. Non-secret fields and env placeholders are preserved.
6041b7a to
34390d3
Compare
|
Maintainer verification complete for
Land-ready. No network proof was required because the leak and redaction path is synchronous and process-local. |
|
@vincentkoc thank you for reviewing and merging this one 🙏 — glad the |
…penclaw#93333) PR openclaw#88496 routed /config show and /config set chat output through the shared schema-aware redaction path, but the sibling /debug commands in the same handler were left untouched. /debug show JSON-stringified the full runtime override tree verbatim and /debug set echoed the raw value, so a secret-shaped override (e.g. gateway.auth.token, channels.*.botToken) set via /debug set was rendered in plaintext to chat-visible output. Apply redactConfigObject(overrides, schema.uiHints) to the override tree before rendering /debug show, and reuse formatConfigSetValueLabel for the /debug set acknowledgement, matching the existing /config redaction contract. Non-secret fields and env placeholders are preserved.
…penclaw#93333) PR openclaw#88496 routed /config show and /config set chat output through the shared schema-aware redaction path, but the sibling /debug commands in the same handler were left untouched. /debug show JSON-stringified the full runtime override tree verbatim and /debug set echoed the raw value, so a secret-shaped override (e.g. gateway.auth.token, channels.*.botToken) set via /debug set was rendered in plaintext to chat-visible output. Apply redactConfigObject(overrides, schema.uiHints) to the override tree before rendering /debug show, and reuse formatConfigSetValueLabel for the /debug set acknowledgement, matching the existing /config redaction contract. Non-secret fields and env placeholders are preserved.
Summary
PR #88496 routed
/config showand/config setchat output through the shared schema-aware config redaction path, but the sibling/debugcommands in the very same handler (handleDebugCommandinsrc/auto-reply/reply/commands-config.ts) were left untouched. This is the asymmetric remainder of that fix:/debug showJSON-stringifies the entire runtime override tree verbatim, so every secret previously stashed via/debug setis printed in plaintext to chat-visible output./debug setechoes the raw value it just stored./debug setaccepts any config path (setConfigOverride->parseConfigPath), so a secret-shaped override such asgateway.auth.tokenorchannels.<id>.botTokenset as a runtime override is leaked back into the chat surface, exactly the class of leak #88496 closed for/config.What changed
/debug show: redact the override tree withredactConfigObject(overrides, loadGatewayRuntimeConfigSchema().uiHints)before rendering, matching the/config showcontract./debug set: reuse the existingformatConfigSetValueLabelhelper (the same one/config setuses) so the acknowledgement value is redacted.redactConfigObject,loadGatewayRuntimeConfigSchema,getConfigOverrides); non-secret fields and env-var placeholders are preserved.Touches one source file plus its test (
commands-gating.test.ts).Real behavior proof (real runtime, outside tests)
Behavior addressed:
/debug showand/debug setmust not render plaintext config secrets stored in the process-local runtime override tree to chat-visible output.Real environment tested: A standalone
tsxscript run against this PR's source checkout. It drives the real exportedsetConfigOverride/getConfigOverrides(src/config/runtime-overrides.ts) and the real exportedhandleDebugCommand(src/auto-reply/reply/commands-config.ts). No vitest harness, no mocked redaction. The secret crosses a real boundary: it is persisted into the module-level process override tree bysetConfigOverride, read back bygetConfigOverrides, then rendered by the real handler.Exact steps or command run after this patch:
The script: (1) calls the real
setConfigOverride("gateway.auth.token", <secret>),setConfigOverride("channels.telegram.botToken", <secret>), and a non-secretmessages.ackReaction; (2) reads the raw tree back via the realgetConfigOverrides()to confirm plaintext is actually stored in memory; (3) drives the realhandleDebugCommand("/debug show")andhandleDebugCommand("/debug set ...").Evidence after fix (terminal capture):
Negative control (same script against pre-fix code,
git stashof the source change):The secret token is rendered in plaintext to chat-visible output before the fix, confirming the leak the patch closes.
Observed result after fix: Secret-shaped override fields (
gateway.auth.token,channels.telegram.botToken) render as__OPENCLAW_REDACTED__while the non-secretmessages.ackReactionis preserved;/debug setacknowledgement is also redacted.What was not tested: A live gateway restart with real channel credentials was not run; the leak path is entirely in the synchronous in-process command handler, so no network auth is needed to exercise it.
Tests and validation
node scripts/run-vitest.mjs src/auto-reply/reply/commands-gating.test.ts-> 21 passed (includes 2 new tests:redacts secret-shaped fields from /debug show replies,redacts secret-shaped values from /debug set acknowledgements).oxlinton changed files: 0 warnings, 0 errors.oxfmt --checkon changed files: correct format.tsgo -p tsconfig.json --noEmit: no type errors.git diff --check: clean.Risk checklist
commands-config.ts) plus its test. No behavior change for non-secret fields or for the empty-overrides path.redactConfigObject,formatConfigSetValueLabel); no new dependencies.${ENV}-style references in overrides are not mangled.commands-config.ts,runtime-overrides.ts, orredact-snapshot.ts(scanned all open PRs).