fix(config-audit): redact CLI argv secrets before persisting to log#75095
fix(config-audit): redact CLI argv secrets before persisting to log#75095sallyom merged 5 commits intoopenclaw:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a credential-leak in the persistent config tamper-detection audit log by ensuring CLI/process details are snapshotted through a centralized, redacting boundary before being written to ~/.openclaw/logs/config-audit.jsonl.
Changes:
- Introduces
snapshotConfigAuditProcessInfo()andredactConfigAuditArgv()to redact secrets inargv/execArgvbefore audit persistence. - Updates config observe / observe-recovery audit write sites to use the centralized snapshot helper instead of inline
process.argv.slice(...). - Expands unit tests to cover flag-based and token-shape-based redaction, plus redaction of caller-supplied
processInfo.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/config/io.audit.ts | Adds argv/execArgv redaction + centralized process snapshot; ensures caller-supplied processInfo is also redacted |
| src/config/io.ts | Switches config observe audit writes to ...snapshotConfigAuditProcessInfo() |
| src/config/io.observe-recovery.ts | Switches recovery observe audit records to ...snapshotConfigAuditProcessInfo() |
| src/config/io.audit.test.ts | Adds coverage for new argv redaction behavior and processInfo redaction path |
| CHANGELOG.md | Documents the security fix for config-audit secret redaction |
|
Codex review: found issues before merge. What this changes: The PR adds config-audit argv/execArgv redaction helpers, routes write, observe, and recovery audit records through them, expands regression coverage, and adds a changelog entry for the security fix. Maintainer follow-up before merge: This is a security-sensitive PR with a concrete high-severity bypass; a maintainer or author should patch the exact PR head and rerun targeted validation before merge. Security review: Security review needs attention: The diff reduces the original audit-log credential exposure but still leaves a concrete argv redaction bypass on the latest PR head. Review findings:
Review detailsBest possible solution: Keep the centralized config-audit snapshot redaction, but change the argv state machine so a secret-looking flag reached while redacting a previous value also causes its own following value to be redacted; add regression coverage for chained secret flags before merge. Do we have a high-confidence way to reproduce the issue? Yes. Current main is source-reproducible because write, observe, and recovery audit paths persist raw argv; the PR-head blocker is source-reproducible with ['openclaw','--token','--api-key','plain-provider-key-12345']. Is this the best way to solve the issue? No. Redacting at the config-audit snapshot boundary is the narrow maintainable fix, but the current PR implementation still treats a later secret flag as only the previous flag's value and can expose the later value. Full review comments:
Overall correctness: patch is incorrect Security concerns:
What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 38da2ac6f8ee. |
Address PR openclaw#75095 review: - Add a suffix-based heuristic so any `--<...>-(token|secret|password| passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat)` is treated as a secret flag in addition to the explicit list. This catches `--custom-api-key`, `--alibaba-model-studio-api-key`, plugin-defined `cliFlag` values, and similar flags that ship in OpenClaw + bundled plugins today and were previously falling through to the per-element token-shape fallback (which can miss arbitrary provider keys with no recognizable prefix). - Expand the explicit list with adjacent secret flags surfaced by Copilot (`--app-token`, `--remote-token`, `--push-token`, `--bearer-token`, `--id-token`, `--identity-token`, `--session-token`, `--service-token`, `--pat`, `--personal-access-token`, `--oauth-token`, `--webhook-token`). - Stop masking the next arg when a secret flag is followed by another option (`--token --port 8080` should not mask `--port`). Treat that as a missing value and keep the following arg intact. - Cap caller-supplied processInfo argv/execArgv at 8 entries via a new capArgv helper so a future caller can't bypass the snapshot length bound and persist arbitrarily large argv to the audit log. 4 new tests cover: heuristic flag detection (`--custom-api-key`, `--alibaba-model-studio-api-key`, `--app-token`, `--frobnicate-credential`), no-mask when secret flag is followed by another option, no-mask when secret flag is the final arg without a value, and the slice(0, 8) cap with a long argv that smuggles a sensitive value past index 8 into a confirmed-not-present assertion.
…ery-key, etc.) Addresses clawsweeper P1 on PR openclaw#75095: the prior classifier missed `--private-key` (Nostr setup) and `--recovery-key` (Matrix), letting those values land in config-audit.jsonl. Add explicit names plus a `*-key` suffix family covering private/recovery/signing/encryption/ master/session/gateway/service/hook keys. Tests cover both the explicit flags and the heuristic family.
Address PR openclaw#75095 review: - Add a suffix-based heuristic so any `--<...>-(token|secret|password| passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat)` is treated as a secret flag in addition to the explicit list. This catches `--custom-api-key`, `--alibaba-model-studio-api-key`, plugin-defined `cliFlag` values, and similar flags that ship in OpenClaw + bundled plugins today and were previously falling through to the per-element token-shape fallback (which can miss arbitrary provider keys with no recognizable prefix). - Expand the explicit list with adjacent secret flags surfaced by Copilot (`--app-token`, `--remote-token`, `--push-token`, `--bearer-token`, `--id-token`, `--identity-token`, `--session-token`, `--service-token`, `--pat`, `--personal-access-token`, `--oauth-token`, `--webhook-token`). - Stop masking the next arg when a secret flag is followed by another option (`--token --port 8080` should not mask `--port`). Treat that as a missing value and keep the following arg intact. - Cap caller-supplied processInfo argv/execArgv at 8 entries via a new capArgv helper so a future caller can't bypass the snapshot length bound and persist arbitrarily large argv to the audit log. 4 new tests cover: heuristic flag detection (`--custom-api-key`, `--alibaba-model-studio-api-key`, `--app-token`, `--frobnicate-credential`), no-mask when secret flag is followed by another option, no-mask when secret flag is the final arg without a value, and the slice(0, 8) cap with a long argv that smuggles a sensitive value past index 8 into a confirmed-not-present assertion.
…ery-key, etc.) Addresses clawsweeper P1 on PR openclaw#75095: the prior classifier missed `--private-key` (Nostr setup) and `--recovery-key` (Matrix), letting those values land in config-audit.jsonl. Add explicit names plus a `*-key` suffix family covering private/recovery/signing/encryption/ master/session/gateway/service/hook keys. Tests cover both the explicit flags and the heuristic family.
e80420b to
dd881b7
Compare
Address PR openclaw#75095 review: - Add a suffix-based heuristic so any `--<...>-(token|secret|password| passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat)` is treated as a secret flag in addition to the explicit list. This catches `--custom-api-key`, `--alibaba-model-studio-api-key`, plugin-defined `cliFlag` values, and similar flags that ship in OpenClaw + bundled plugins today and were previously falling through to the per-element token-shape fallback (which can miss arbitrary provider keys with no recognizable prefix). - Expand the explicit list with adjacent secret flags surfaced by Copilot (`--app-token`, `--remote-token`, `--push-token`, `--bearer-token`, `--id-token`, `--identity-token`, `--session-token`, `--service-token`, `--pat`, `--personal-access-token`, `--oauth-token`, `--webhook-token`). - Stop masking the next arg when a secret flag is followed by another option (`--token --port 8080` should not mask `--port`). Treat that as a missing value and keep the following arg intact. - Cap caller-supplied processInfo argv/execArgv at 8 entries via a new capArgv helper so a future caller can't bypass the snapshot length bound and persist arbitrarily large argv to the audit log. 4 new tests cover: heuristic flag detection (`--custom-api-key`, `--alibaba-model-studio-api-key`, `--app-token`, `--frobnicate-credential`), no-mask when secret flag is followed by another option, no-mask when secret flag is the final arg without a value, and the slice(0, 8) cap with a long argv that smuggles a sensitive value past index 8 into a confirmed-not-present assertion.
…ery-key, etc.) Addresses clawsweeper P1 on PR openclaw#75095: the prior classifier missed `--private-key` (Nostr setup) and `--recovery-key` (Matrix), letting those values land in config-audit.jsonl. Add explicit names plus a `*-key` suffix family covering private/recovery/signing/encryption/ master/session/gateway/service/hook keys. Tests cover both the explicit flags and the heuristic family.
f63528a to
fedcf08
Compare
Address PR openclaw#75095 review: - Add a suffix-based heuristic so any `--<...>-(token|secret|password| passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat)` is treated as a secret flag in addition to the explicit list. This catches `--custom-api-key`, `--alibaba-model-studio-api-key`, plugin-defined `cliFlag` values, and similar flags that ship in OpenClaw + bundled plugins today and were previously falling through to the per-element token-shape fallback (which can miss arbitrary provider keys with no recognizable prefix). - Expand the explicit list with adjacent secret flags surfaced by Copilot (`--app-token`, `--remote-token`, `--push-token`, `--bearer-token`, `--id-token`, `--identity-token`, `--session-token`, `--service-token`, `--pat`, `--personal-access-token`, `--oauth-token`, `--webhook-token`). - Stop masking the next arg when a secret flag is followed by another option (`--token --port 8080` should not mask `--port`). Treat that as a missing value and keep the following arg intact. - Cap caller-supplied processInfo argv/execArgv at 8 entries via a new capArgv helper so a future caller can't bypass the snapshot length bound and persist arbitrarily large argv to the audit log. 4 new tests cover: heuristic flag detection (`--custom-api-key`, `--alibaba-model-studio-api-key`, `--app-token`, `--frobnicate-credential`), no-mask when secret flag is followed by another option, no-mask when secret flag is the final arg without a value, and the slice(0, 8) cap with a long argv that smuggles a sensitive value past index 8 into a confirmed-not-present assertion.
…ery-key, etc.) Addresses clawsweeper P1 on PR openclaw#75095: the prior classifier missed `--private-key` (Nostr setup) and `--recovery-key` (Matrix), letting those values land in config-audit.jsonl. Add explicit names plus a `*-key` suffix family covering private/recovery/signing/encryption/ master/session/gateway/service/hook keys. Tests cover both the explicit flags and the heuristic family.
fedcf08 to
365f2a9
Compare
…penclaw#60826) `config-audit.jsonl` is the persistent record of config writes/observes under `~/.openclaw/logs/`. It captures `process.argv.slice(0, 8)` raw at four sites (write-audit, async observe, sync observe, observe-recovery record builder), so any token passed as a CLI flag (gateway tokens, bot tokens, hook tokens, exec'd `op read`/op-style tokens) lands at rest in a mode-0600 file indefinitely. The mechanism designed to detect config tampering becomes the credential-leak vector itself. Centralize the snapshot and redact at the boundary: - New `snapshotConfigAuditProcessInfo()` in src/config/io.audit.ts wraps the `process.*` reads and routes argv/execArgv through a new `redactConfigAuditArgv()` helper. - redactConfigAuditArgv has three layers per element: 1. `--flag=value` for known secret flag names → mask the value half. 2. value following a bare `--flag` → emit `***`. 3. fall through to redactToolPayloadText (force-on tools mode using the shared logging.redactPatterns defaults) for the remaining standalone token shapes — sk-/ghp_/xox*/gsk_/AIza*/npm_, Telegram bot tokens, PEM blocks, Bearer headers, URL query secrets, `KEY=VALUE` env-style assignments. - `resolveConfigAuditProcessInfo` (write-base path) now also redacts a caller-supplied processInfo, so test/non-default callers can't bypass. - The three other inline `process.argv.slice(0, 8)` sites in io.ts (sync + async observe) and io.observe-recovery.ts now spread `...snapshotConfigAuditProcessInfo()` instead. Five new tests cover: secret-flag value redaction (space-separated and =-form), shared-pattern fallback for standalone token shapes, untouched non-secret args, and end-to-end via createConfigWriteAuditRecordBase with a crafted processInfo. Fixes openclaw#60826
Address PR openclaw#75095 review: - Add a suffix-based heuristic so any `--<...>-(token|secret|password| passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat)` is treated as a secret flag in addition to the explicit list. This catches `--custom-api-key`, `--alibaba-model-studio-api-key`, plugin-defined `cliFlag` values, and similar flags that ship in OpenClaw + bundled plugins today and were previously falling through to the per-element token-shape fallback (which can miss arbitrary provider keys with no recognizable prefix). - Expand the explicit list with adjacent secret flags surfaced by Copilot (`--app-token`, `--remote-token`, `--push-token`, `--bearer-token`, `--id-token`, `--identity-token`, `--session-token`, `--service-token`, `--pat`, `--personal-access-token`, `--oauth-token`, `--webhook-token`). - Stop masking the next arg when a secret flag is followed by another option (`--token --port 8080` should not mask `--port`). Treat that as a missing value and keep the following arg intact. - Cap caller-supplied processInfo argv/execArgv at 8 entries via a new capArgv helper so a future caller can't bypass the snapshot length bound and persist arbitrarily large argv to the audit log. 4 new tests cover: heuristic flag detection (`--custom-api-key`, `--alibaba-model-studio-api-key`, `--app-token`, `--frobnicate-credential`), no-mask when secret flag is followed by another option, no-mask when secret flag is the final arg without a value, and the slice(0, 8) cap with a long argv that smuggles a sensitive value past index 8 into a confirmed-not-present assertion.
…ery-key, etc.) Addresses clawsweeper P1 on PR openclaw#75095: the prior classifier missed `--private-key` (Nostr setup) and `--recovery-key` (Matrix), letting those values land in config-audit.jsonl. Add explicit names plus a `*-key` suffix family covering private/recovery/signing/encryption/ master/session/gateway/service/hook keys. Tests cover both the explicit flags and the heuristic family.
365f2a9 to
3dc54de
Compare
…penclaw#75095) Merged via squash. Prepared head SHA: 3dc54de Co-authored-by: koshaji <[email protected]> Co-authored-by: sallyom <[email protected]> Reviewed-by: @sallyom
What this fixes
Closes #60826.
~/.openclaw/logs/config-audit.jsonlis the persistent record of every config write and observe pass — designed as a tamper-detection control. It capturesprocess.argv.slice(0, 8)raw at four sites, so any secret passed as a CLI flag (gateway tokens, bot tokens, hook tokens,op read-piped tokens, etc.) lands at rest in a mode-0600 file indefinitely. The mechanism designed to detect tampering became the credential-leak vector itself.Two prior PRs (#60871 and #70468) closed unmerged. ClawSweeper's triage on the issue confirmed the gap is still on
mainand recommended centralizing process-info sanitization at the snapshot site.What changed
Centralize the snapshot and redact at the boundary:
src/config/io.audit.ts— newsnapshotConfigAuditProcessInfo()wraps theprocess.*reads. Argv and execArgv pass through a newredactConfigAuditArgv()helper with three layers per element:--flag=valuefor known secret flag names → mask the value half (--token=***).--flagform → emit***instead of the next arg.redactToolPayloadText(force-on tools mode using the sharedlogging.redactPatternsdefaults) for the remaining standalone token shapes:sk-,ghp_,xox*,gsk_,AIza*,npm_, Telegram bot tokens (<digits>:<base64>), PEM blocks, Bearer headers, URL query secrets, andKEY=VALUEenv-style assignments.resolveConfigAuditProcessInfo(write-base path) now also redacts a caller-suppliedprocessInfo, so test/non-default callers can't bypass.process.argv.slice(0, 8)sites insrc/config/io.ts(sync + async observe) andsrc/config/io.observe-recovery.tsnow spread...snapshotConfigAuditProcessInfo()instead.The redaction is force-on — it ignores
logging.redactSensitive: "off". That setting is for debug logs the operator opts into; the persistent audit log is a security control that must always redact.The known secret flag list is conservative and includes both OpenClaw-specific (
--gateway-token,--hook-token,--bot-token,--webhook-secret,--op-service-account-token) and generic (--token,--api-key,--secret,--password,--client-secret, etc.) names.Why this is the best fix
redactToolPayloadTextis the same primitive that protects tool output today, with the sameDEFAULT_REDACT_PATTERNSlibrary covering token shapes from every major provider in the repo.logging.redactSensitivefor debug verbosity does not get a credential leak in their persistent audit trail.snapshotConfigAuditProcessInfo.redactConfigAuditArgvis also exported but only for the test surface.Tests
pnpm test src/config/io.audit.test.ts— 11/11 passing (was 6, +5 new):--token VALUE)=-form (--token=VALUE)ghp_*,AIza*, Telegram bot tokens)createConfigWriteAuditRecordBasewith a craftedprocessInfopnpm test src/config/io.observe-recovery.test.ts— 11/11 still passingpnpm test src/config/io.write-config.test.ts src/config/io.write-prepare.test.ts src/config/io.runtime-snapshot-write.test.ts— 43/43 still passingpnpm tsgo:core— cleanpnpm tsgo:core:test— cleanpnpm exec oxfmt --checkon all 5 files — cleanTest plan
main: pass--token=<value>to a gateway invocation, trigger a config write, inspect~/.openclaw/logs/config-audit.jsonl→ see plaintext.--token=***in the audit JSONL.logging.redactSensitive: "off"does NOT re-enable the leak (audit log should redact regardless).