Skip to content

fix(auto-reply): redact secrets in /debug show and /debug set output#93333

Merged
vincentkoc merged 1 commit into
openclaw:mainfrom
Alix-007:fix/debug-show-redact-secrets
Jun 16, 2026
Merged

fix(auto-reply): redact secrets in /debug show and /debug set output#93333
vincentkoc merged 1 commit into
openclaw:mainfrom
Alix-007:fix/debug-show-redact-secrets

Conversation

@Alix-007

@Alix-007 Alix-007 commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Summary

PR #88496 routed /config show and /config set chat output through the shared schema-aware config redaction path, but the sibling /debug commands in the very same handler (handleDebugCommand in src/auto-reply/reply/commands-config.ts) were left untouched. This is the asymmetric remainder of that fix:

  • /debug show JSON-stringifies the entire runtime override tree verbatim, so every secret previously stashed via /debug set is printed in plaintext to chat-visible output.
  • /debug set echoes the raw value it just stored.

/debug set accepts any config path (setConfigOverride -> parseConfigPath), so a secret-shaped override such as gateway.auth.token or channels.<id>.botToken set 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 with redactConfigObject(overrides, loadGatewayRuntimeConfigSchema().uiHints) before rendering, matching the /config show contract.
  • /debug set: reuse the existing formatConfigSetValueLabel helper (the same one /config set uses) so the acknowledgement value is redacted.
  • All imports were already present in the file (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 show and /debug set must not render plaintext config secrets stored in the process-local runtime override tree to chat-visible output.

Real environment tested: A standalone tsx script run against this PR's source checkout. It drives the real exported setConfigOverride / getConfigOverrides (src/config/runtime-overrides.ts) and the real exported handleDebugCommand (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 by setConfigOverride, read back by getConfigOverrides, then rendered by the real handler.

Exact steps or command run after this patch:

node --import tsx ./debug-redact-proof.mts

The script: (1) calls the real setConfigOverride("gateway.auth.token", <secret>), setConfigOverride("channels.telegram.botToken", <secret>), and a non-secret messages.ackReaction; (2) reads the raw tree back via the real getConfigOverrides() to confirm plaintext is actually stored in memory; (3) drives the real handleDebugCommand("/debug show") and handleDebugCommand("/debug set ...").

Evidence after fix (terminal capture):

[readback] Raw getConfigOverrides() (proves plaintext IS stored in memory):
{
  "gateway": { "auth": { "token": "SK_DEBUG_PROOF_LIVE_TOKEN_0000111122223333" } },
  "channels": { "telegram": { "botToken": "TG_DEBUG_PROOF_LIVE_BOTTOKEN_4444555566667777" } },
  "messages": { "ackReaction": ":)" }
}
[readback] raw tree contains plaintext secrets: true

[/debug show] Real handler chat-visible output:
⚙️ Debug overrides (memory-only):
```json
{
  "gateway": { "auth": { "token": "__OPENCLAW_REDACTED__" } },
  "channels": { "telegram": { "botToken": "__OPENCLAW_REDACTED__" } },
  "messages": { "ackReaction": ":)" }
}
```

[/debug set] Real handler chat-visible acknowledgement:
⚙️ Debug override set: gateway.auth.token="__OPENCLAW_REDACTED__"

========== RESULT (PATCHED) ==========
raw memory stored plaintext secrets   : true (expect true)
/debug show leaked any secret          : false (expect false)
/debug show redacted sentinel present  : true (expect true)
/debug show kept non-secret ackReaction: true (expect true)
/debug set leaked secret               : false (expect false)
/debug set redacted sentinel present   : true (expect true)

PROOF PASS

Negative control (same script against pre-fix code, git stash of the source change):

[/debug show] Real handler chat-visible output:
⚙️ Debug overrides (memory-only):
```json
{
  "gateway": { "auth": { "token": "SK_DEBUG_PROOF_LIVE_TOKEN_0000111122223333" } },
  ...

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-secret messages.ackReaction is preserved; /debug set acknowledgement 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).
  • Negative control: with the source change stashed, the 2 new tests fail (19 passed), confirming they actually exercise the fix.
  • oxlint on changed files: 0 warnings, 0 errors.
  • oxfmt --check on changed files: correct format.
  • tsgo -p tsconfig.json --noEmit: no type errors.
  • git diff --check: clean.

Risk checklist

  • Scope: one source file (commands-config.ts) plus its test. No behavior change for non-secret fields or for the empty-overrides path.
  • Reuses the existing, already-imported redaction helpers (redactConfigObject, formatConfigSetValueLabel); no new dependencies.
  • Env-var placeholders are preserved by the existing redaction logic, so ${ENV}-style references in overrides are not mangled.
  • No competing open PR touches commands-config.ts, runtime-overrides.ts, or redact-snapshot.ts (scanned all open PRs).

@openclaw-barnacle openclaw-barnacle Bot added size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 15, 2026
@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 15, 2026, 11:36 AM ET / 15:36 UTC.

Summary
The branch redacts /debug show runtime override output and /debug set acknowledgement values, with regression coverage in commands-gating.test.ts.

PR surface: Source +8, Tests +64. Total +72 across 2 files.

Reproducibility: yes. at source level: current main serializes getConfigOverrides() directly for /debug show and echoes raw string values for /debug set. The PR body also includes terminal after-fix proof and a negative control for the pre-fix leak.

Review metrics: 1 noteworthy metric.

  • Debug reply secret surfaces: 2 redacted. Both /debug show and /debug set are chat-visible paths that can expose runtime override secrets before merge.

Stored data model
Persistent data-model change detected: serialized state: src/auto-reply/reply/commands-config.ts, serialized state: src/auto-reply/reply/commands-gating.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Next step before merge

  • [P2] No repair lane is needed because the branch already contains the focused fix; remaining action is ordinary maintainer review and merge gating.

Security
Cleared: The diff narrows chat-visible secret exposure and adds no dependency, workflow, package, credential-handling, or supply-chain change.

Review details

Best 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 getConfigOverrides() directly for /debug show and echoes raw string values for /debug set. The PR body also includes terminal after-fix proof and a negative control for the pre-fix leak.

Is this the best way to solve the issue?

Yes. Reusing redactConfigObject and formatConfigSetValueLabel is the narrowest maintainable fix because it keeps /debug aligned with the existing /config redaction contract instead of adding a debug-only redactor.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against da92615816b6.

Label changes

Label justifications:

  • P1: The PR fixes chat-visible plaintext exposure of secret-shaped runtime override values, a security-sensitive bug in an operator workflow.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes terminal proof using the real exported runtime override APIs and command handler after the fix, plus a negative control showing the pre-fix leak.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal proof using the real exported runtime override APIs and command handler after the fix, plus a negative control showing the pre-fix leak.
Evidence reviewed

PR surface:

Source +8, Tests +64. Total +72 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 11 3 +8
Tests 1 64 0 +64
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 75 3 +72

What I checked:

Likely related people:

  • RomneyDa: Current-main blame attributes the debug command body to merge commit 1f8c4d3, which recreated the present command module in this checkout. (role: recent area contributor; confidence: high; commits: 1f8c4d3958b2; files: src/auto-reply/reply/commands-config.ts, src/auto-reply/reply/commands-gating.test.ts, src/config/redact-snapshot.ts)
  • jason-allen-oneal: Authored the merged /config show redaction PR that this PR explicitly extends to the sibling /debug command surface. (role: adjacent fix author; confidence: medium; commits: a776de25e8b0; files: src/auto-reply/reply/commands-config.ts, src/auto-reply/reply/commands-gating.test.ts)
  • steipete: Merged the prior config redaction PR and authored its /config set acknowledgement redaction commit, which is the helper path reused here. (role: adjacent merger and redaction contributor; confidence: medium; commits: a776de25e8b0, 8c64bb994ceb; files: src/auto-reply/reply/commands-config.ts, src/auto-reply/reply/commands-gating.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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 keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 High-priority user-facing bug, regression, or broken workflow. labels Jun 15, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label Jun 15, 2026
@Takhoffman

Copy link
Copy Markdown
Contributor

@clawsweeper automerge

@clawsweeper

clawsweeper Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@Alix-007

Copy link
Copy Markdown
Contributor Author

@vincentkoc — when you're next sweeping the ready queue, this one (#93333) is a P1 security fix: it redacts secrets in /debug show and /debug set output (the sibling of the already-shipped /config redaction). It's 🦞 diamond + proof: sufficient + MERGEABLE, and @Takhoffman flagged it for automerge earlier. A few of my other ready + mergeable fixes are parked alongside it too. No rush — would appreciate a look whenever convenient. 🦞

@vincentkoc vincentkoc self-assigned this Jun 16, 2026
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.
@vincentkoc
vincentkoc force-pushed the fix/debug-show-redact-secrets branch from 6041b7a to 34390d3 Compare June 16, 2026 05:46
@openclaw-barnacle openclaw-barnacle Bot added triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. and removed proof: sufficient ClawSweeper judged the real behavior proof convincing. triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 16, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Maintainer verification complete for 34390d3454cc25a54863980d71dbe94c12445c82.

  • Focused proof: node scripts/run-vitest.mjs src/auto-reply/reply/commands-gating.test.ts (21 tests passed)
  • Fresh branch review: no actionable findings
  • Exact-head code CI: green
  • Real behavior proof: the Check real behavior proof step succeeded on the exact head; the surrounding duplicate pull_request_target run was then cancelled by workflow concurrency
  • PR proof labels were normalized to the repository parser's exact field names

Land-ready. No network proof was required because the leak and redaction path is synchronous and process-local.

@vincentkoc
vincentkoc merged commit 01d3505 into openclaw:main Jun 16, 2026
187 of 193 checks passed
@Alix-007

Copy link
Copy Markdown
Contributor Author

@vincentkoc thank you for reviewing and merging this one 🙏 — glad the /debug secret redaction landed alongside the /config one. Really appreciate the review!

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 17, 2026
…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.
crh-code pushed a commit to crh-code/openclaw that referenced this pull request Jun 18, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants