Skip to content

fix: redact sensitive CLI argv from config-audit.jsonl#60871

Closed
lml2468 wants to merge 1 commit intoopenclaw:mainfrom
lml2468:fix/issue-60826
Closed

fix: redact sensitive CLI argv from config-audit.jsonl#60871
lml2468 wants to merge 1 commit intoopenclaw:mainfrom
lml2468:fix/issue-60826

Conversation

@lml2468
Copy link
Copy Markdown
Contributor

@lml2468 lml2468 commented Apr 4, 2026

Summary

The config tamper detection audit log (config-audit.jsonl) was recording full CLI argument vectors including plaintext gateway tokens, bot tokens, and API keys — making a security control itself a credential leak vector.

Fix

Added a redactArgv() helper function and SENSITIVE_ARGV_FLAGS constant in src/config/io.ts that scrubs known secret-bearing flag values before they are written to the audit log.

Flags redacted: --token, --bot-token, --app-token, --access-token, --gateway-token, --password, --api-key, --secret, --secret-key, --secret-input

Both --flag value and --flag=value forms are handled.

Changes

  • src/config/io.ts — added SENSITIVE_ARGV_FLAGS set and redactArgv() function; replaced all 3 audit-record argv: process.argv.slice(0, 8) writes with argv: redactArgv(process.argv).slice(0, 8)

Fixes #60826

The config tamper detection audit log was recording full CLI argv
including plaintext gateway tokens, bot tokens, and API keys.

Now strips/redacts known secret patterns from argv before writing
to config-audit.jsonl to prevent credential exposure at rest.

Added redactArgv() helper function and SENSITIVE_ARGV_FLAGS set
that covers: --token, --bot-token, --app-token, --access-token,
--gateway-token, --password, --api-key, --secret, --secret-key,
--secret-input.

Both --flag value and --flag=value forms are handled.

Fixes openclaw#60826
@openclaw-barnacle openclaw-barnacle Bot added size: S r: too-many-prs Auto-close: author has more than twenty active PRs. labels Apr 4, 2026
@openclaw-barnacle
Copy link
Copy Markdown

Closing this PR because the author has more than 10 active PRs in this repo. Please reduce the active PR queue and reopen or resubmit once it is back under the limit. You can close your own PRs to get back under the limit.

@openclaw-barnacle openclaw-barnacle Bot closed this Apr 4, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b1967e7f5e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/config/io.ts
* be redacted before writing to config-audit.jsonl.
* Fixes openclaw/openclaw#60826.
*/
const SENSITIVE_ARGV_FLAGS = new Set([
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Include all secret CLI flags in argv redaction

The new redaction relies on a hardcoded allowlist, but several existing secret-bearing CLI flags are not included, so their values still get written in plaintext to config-audit.jsonl. For example, onboarding defines --custom-api-key, --gateway-password, and --remote-token (src/cli/program/register.onboard.ts:110-127), and webhooks defines --hook-token/--push-token (src/cli/webhooks-cli.ts:44-45); none are covered by SENSITIVE_ARGV_FLAGS, so redactArgv() leaves them unmasked. This means the credential leak path remains open for those command paths.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 4, 2026

Greptile Summary

This PR fixes a real security issue: the config tamper-detection audit log (config-audit.jsonl) was recording raw process.argv, which could expose gateway tokens, bot tokens, and API keys as plaintext in the log file. The fix adds a redactArgv() helper that scrubs known sensitive flag values (both --flag value and --flag=value forms) before the argv vector is written to the audit record, applied consistently at all three write sites.

Key points:

  • The redactArgv() logic is correct for both flag forms and is non-mutating.
  • Redaction runs on the full process.argv before .slice(0, 8), which is the correct ordering — reversing them could allow a sensitive flag near position 7 to slip through unredacted.
  • The helper function has no unit tests. Given its security role and non-trivial branching logic, adding colocated tests (after exporting or via the audit log path) would significantly improve confidence in the fix.
  • The three redactArgv(process.argv).slice(0, 8) call sites lack a comment explaining why redaction must precede the slice; a brief inline note would protect against inadvertent reversal in future refactors.

Confidence Score: 4/5

Safe to merge — the fix correctly addresses the credential-leak vector, but the absence of unit tests for the security-critical helper is a notable gap worth resolving.

The core logic is sound and applied consistently at all three audit write sites. No regressions are introduced — the only behavioral change is that known sensitive flag values are replaced with [REDACTED] in the log. Score is 4 rather than 5 because redactArgv() has no unit tests covering its edge cases (flag as last element, --flag=value form, chained flags), and the redact-before-slice ordering is undocumented, creating a subtle latent risk in future refactors.

src/config/io.ts — specifically the redactArgv() function (lines 114–136) and the three call sites; test coverage would reduce risk significantly.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/config/io.ts
Line: 128-131

Comment:
**Sensitive flag as the last argument is not redacted**

When a sensitive flag appears as the very last element of `argv` (no following value), the guard `i + 1 < argv.length` is `false`, so the code falls through to `out.push(arg)` — the flag name is written to the audit log without any indication that it may have carried a value in a different invocation. This is not a credential leak by itself (no value is present), but it means the logic silently does nothing for the malformed/truncated case rather than treating it uniformly.

More importantly, because `.slice(0, 8)` is applied to the redacted array **after** redaction, consider the following scenario:

```
process.argv = ["node", "openclaw", "a", "b", "c", "d", "e", "--token", "secretval", ...]
//              idx:    0          1    2    3    4    5    6    7          8
```

`redactArgv()` runs on the **full** array. At index 7 (`--token`), `i + 1 = 8 < len` is true, so both `--token` and `[REDACTED]` are pushed and `i` advances to 8 — the result array still has `--token` at index 7 and `[REDACTED]` at index 8. After `.slice(0, 8)` the entry at index 8 is cut off, leaving `--token` without `[REDACTED]`. No value is exposed (the slice removed it either way), but the flag name is still present in the record.

This is safe against leaking values, but it may be worth adding a comment near the `slice(0, 8)` call sites to document that redaction must run before slicing (to avoid accidentally re-ordering them in a future refactor):

```suggestion
    if (SENSITIVE_ARGV_FLAGS.has(arg) && i + 1 < argv.length) {
      out.push(arg, "[REDACTED]");
      i++;
      continue;
    }
    // Note: if a sensitive flag is the last element with no following value,
    // there is nothing to redact — pass it through unchanged.
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/config/io.ts
Line: 114-136

Comment:
**No unit tests for security-critical `redactArgv` helper**

`redactArgv()` is an untested security helper guarding against credential leaks in the audit log. The existing test files (`io.write-config.test.ts`, `io.observe-config.test.ts`) contain no coverage of the argv redaction behavior.

Given the security sensitivity and the two distinct code paths (`--flag value` and `--flag=value`), this function is a strong candidate for unit tests. Because it is currently module-private it cannot be tested in isolation — it either needs to be exported for a colocated test or covered via the full audit-log write path.

Suggested cases worth covering:
- `--flag value` form is redacted
- `--flag=value` form is redacted
- Non-sensitive flags pass through unchanged
- Sensitive flag as the last element (no following value) does not crash
- Chained sensitive flags are all independently redacted

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix: redact sensitive CLI argv from conf..." | Re-trigger Greptile

Comment thread src/config/io.ts
Comment on lines +128 to +131
if (SENSITIVE_ARGV_FLAGS.has(arg) && i + 1 < argv.length) {
out.push(arg, "[REDACTED]");
i++;
continue;
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.

P1 Sensitive flag as the last argument is not redacted

When a sensitive flag appears as the very last element of argv (no following value), the guard i + 1 < argv.length is false, so the code falls through to out.push(arg) — the flag name is written to the audit log without any indication that it may have carried a value in a different invocation. This is not a credential leak by itself (no value is present), but it means the logic silently does nothing for the malformed/truncated case rather than treating it uniformly.

More importantly, because .slice(0, 8) is applied to the redacted array after redaction, consider the following scenario:

process.argv = ["node", "openclaw", "a", "b", "c", "d", "e", "--token", "secretval", ...]
//              idx:    0          1    2    3    4    5    6    7          8

redactArgv() runs on the full array. At index 7 (--token), i + 1 = 8 < len is true, so both --token and [REDACTED] are pushed and i advances to 8 — the result array still has --token at index 7 and [REDACTED] at index 8. After .slice(0, 8) the entry at index 8 is cut off, leaving --token without [REDACTED]. No value is exposed (the slice removed it either way), but the flag name is still present in the record.

This is safe against leaking values, but it may be worth adding a comment near the slice(0, 8) call sites to document that redaction must run before slicing (to avoid accidentally re-ordering them in a future refactor):

Suggested change
if (SENSITIVE_ARGV_FLAGS.has(arg) && i + 1 < argv.length) {
out.push(arg, "[REDACTED]");
i++;
continue;
if (SENSITIVE_ARGV_FLAGS.has(arg) && i + 1 < argv.length) {
out.push(arg, "[REDACTED]");
i++;
continue;
}
// Note: if a sensitive flag is the last element with no following value,
// there is nothing to redact — pass it through unchanged.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/io.ts
Line: 128-131

Comment:
**Sensitive flag as the last argument is not redacted**

When a sensitive flag appears as the very last element of `argv` (no following value), the guard `i + 1 < argv.length` is `false`, so the code falls through to `out.push(arg)` — the flag name is written to the audit log without any indication that it may have carried a value in a different invocation. This is not a credential leak by itself (no value is present), but it means the logic silently does nothing for the malformed/truncated case rather than treating it uniformly.

More importantly, because `.slice(0, 8)` is applied to the redacted array **after** redaction, consider the following scenario:

```
process.argv = ["node", "openclaw", "a", "b", "c", "d", "e", "--token", "secretval", ...]
//              idx:    0          1    2    3    4    5    6    7          8
```

`redactArgv()` runs on the **full** array. At index 7 (`--token`), `i + 1 = 8 < len` is true, so both `--token` and `[REDACTED]` are pushed and `i` advances to 8 — the result array still has `--token` at index 7 and `[REDACTED]` at index 8. After `.slice(0, 8)` the entry at index 8 is cut off, leaving `--token` without `[REDACTED]`. No value is exposed (the slice removed it either way), but the flag name is still present in the record.

This is safe against leaking values, but it may be worth adding a comment near the `slice(0, 8)` call sites to document that redaction must run before slicing (to avoid accidentally re-ordering them in a future refactor):

```suggestion
    if (SENSITIVE_ARGV_FLAGS.has(arg) && i + 1 < argv.length) {
      out.push(arg, "[REDACTED]");
      i++;
      continue;
    }
    // Note: if a sensitive flag is the last element with no following value,
    // there is nothing to redact — pass it through unchanged.
```

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/config/io.ts
Comment on lines +114 to +136
function redactArgv(argv: string[]): string[] {
const out: string[] = [];
for (let i = 0; i < argv.length; i++) {
const arg = argv[i] ?? "";
// Handle --flag=value form
const eqIdx = arg.indexOf("=");
if (eqIdx !== -1) {
const flag = arg.slice(0, eqIdx);
if (SENSITIVE_ARGV_FLAGS.has(flag)) {
out.push(`${flag}=[REDACTED]`);
continue;
}
}
// Handle --flag <value> form
if (SENSITIVE_ARGV_FLAGS.has(arg) && i + 1 < argv.length) {
out.push(arg, "[REDACTED]");
i++;
continue;
}
out.push(arg);
}
return out;
}
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.

P1 No unit tests for security-critical redactArgv helper

redactArgv() is an untested security helper guarding against credential leaks in the audit log. The existing test files (io.write-config.test.ts, io.observe-config.test.ts) contain no coverage of the argv redaction behavior.

Given the security sensitivity and the two distinct code paths (--flag value and --flag=value), this function is a strong candidate for unit tests. Because it is currently module-private it cannot be tested in isolation — it either needs to be exported for a colocated test or covered via the full audit-log write path.

Suggested cases worth covering:

  • --flag value form is redacted
  • --flag=value form is redacted
  • Non-sensitive flags pass through unchanged
  • Sensitive flag as the last element (no following value) does not crash
  • Chained sensitive flags are all independently redacted
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/config/io.ts
Line: 114-136

Comment:
**No unit tests for security-critical `redactArgv` helper**

`redactArgv()` is an untested security helper guarding against credential leaks in the audit log. The existing test files (`io.write-config.test.ts`, `io.observe-config.test.ts`) contain no coverage of the argv redaction behavior.

Given the security sensitivity and the two distinct code paths (`--flag value` and `--flag=value`), this function is a strong candidate for unit tests. Because it is currently module-private it cannot be tested in isolation — it either needs to be exported for a colocated test or covered via the full audit-log write path.

Suggested cases worth covering:
- `--flag value` form is redacted
- `--flag=value` form is redacted
- Non-sensitive flags pass through unchanged
- Sensitive flag as the last element (no following value) does not crash
- Chained sensitive flags are all independently redacted

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

r: too-many-prs Auto-close: author has more than twenty active PRs. size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

config-audit.jsonl logs plaintext secrets in CLI argv

2 participants