diff --git a/src/config/io.audit.test.ts b/src/config/io.audit.test.ts index fba2a66cd8d..7de6fc89322 100644 --- a/src/config/io.audit.test.ts +++ b/src/config/io.audit.test.ts @@ -274,6 +274,54 @@ describe("config io audit helpers", () => { ]); }); + it("redacts key-valued secret flags (Nostr --private-key, Matrix --recovery-key)", () => { + const argv = [ + "node", + "openclaw", + "channels", + "add", + "--channel", + "nostr", + "--private-key", + "nsec1realnostrprivatekeyvaluexyz1234567890", + "--recovery-key=EsTb-ABCD-1234-EFGH-5678-IJKL-9012-MNOP", + ]; + const result = redactConfigAuditArgv(argv); + expect(result).toEqual([ + "node", + "openclaw", + "channels", + "add", + "--channel", + "nostr", + "--private-key", + "***", + "--recovery-key=***", + ]); + }); + + it("redacts unknown *-key flags via the heuristic classifier (private/signing/master/etc.)", () => { + const argv = [ + "node", + "openclaw", + "--my-plugin-private-key", + "tenant-private-key-material-zzz", + "--rotated-signing-key=PEM-LIKE-MATERIAL", + "--ops-master-key", + "ABCDEF1234567890", + ]; + const result = redactConfigAuditArgv(argv); + expect(result).toEqual([ + "node", + "openclaw", + "--my-plugin-private-key", + "***", + "--rotated-signing-key=***", + "--ops-master-key", + "***", + ]); + }); + it("does not mask the next arg when a secret flag is followed by another option", () => { const argv = ["openclaw", "--token", "--port", "8080"]; expect(redactConfigAuditArgv(argv)).toEqual(["openclaw", "--token", "--port", "8080"]); diff --git a/src/config/io.audit.ts b/src/config/io.audit.ts index 23c9a3646fd..808712017b3 100644 --- a/src/config/io.audit.ts +++ b/src/config/io.audit.ts @@ -38,14 +38,20 @@ const SECRET_FLAG_NAMES = new Set([ "--identity-token", "--session-token", "--service-token", + "--private-key", + "--recovery-key", + "--gateway-key", + "--session-key", + "--active-key", ]); // Suffix-based heuristic. Any `--…-(token|secret|password|passwd|api-key| -// apikey|api-secret|webhook|credential|bearer|pat)` is treated as a secret -// flag in addition to the explicit list. The leading `--` is required so we -// don't mismatch arbitrary positional arguments. +// apikey|api-secret|webhook|credential|bearer|pat|private-key|recovery-key| +// signing-key|encryption-key|master-key|session-key|gateway-key|service-key| +// hook-key)` is treated as a secret flag in addition to the explicit list. +// The leading `--` is required so we don't mismatch arbitrary positional args. const SECRET_FLAG_SUFFIX_PATTERN = - /^--(?:[a-z0-9]+(?:-[a-z0-9]+)*-)?(?:token|secret|password|passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat)$/; + /^--(?:[a-z0-9]+(?:-[a-z0-9]+)*-)?(?:token|secret|password|passwd|api[-_]?key|api[-_]?secret|webhook|credential|bearer|pat|private[-_]?key|recovery[-_]?key|signing[-_]?key|encryption[-_]?key|master[-_]?key|session[-_]?key|gateway[-_]?key|service[-_]?key|hook[-_]?key)$/; function isSecretFlagName(flagName: string | null): boolean { if (flagName === null) {