

































@@ -7,6 +7,7 @@ import {
77createConfigWriteAuditRecordBase,
88finalizeConfigWriteAuditRecord,
99formatConfigOverwriteLogMessage,
10+redactConfigAuditArgv,
1011resolveConfigAuditLogPath,
1112} from "./io.audit.js";
1213@@ -195,6 +196,231 @@ describe("config io audit helpers", () => {
195196});
196197});
197198199+it("redacts argv values that follow known secret flag names", () => {
200+const argv = [
201+"node",
202+"openclaw",
203+"gateway",
204+"--token",
205+"super-secret-gateway-token-12345",
206+"--api-key",
207+"sk-very-real-looking-openai-api-key-AB12CD34",
208+"--port",
209+"8080",
210+];
211+const result = redactConfigAuditArgv(argv);
212+expect(result).toEqual([
213+"node",
214+"openclaw",
215+"gateway",
216+"--token",
217+"***",
218+"--api-key",
219+"***",
220+"--port",
221+"8080",
222+]);
223+});
224+225+it("redacts the value half of `--flag=value` for secret flags", () => {
226+const argv = ["openclaw", "--token=ghp_realgithubtoken1234567890ABCD", "--port=8080"];
227+expect(redactConfigAuditArgv(argv)).toEqual(["openclaw", "--token=***", "--port=8080"]);
228+});
229+230+it("redacts standalone token shapes via the shared logging redaction patterns", () => {
231+const argv = [
232+"node",
233+"openclaw",
234+"ghp_realgithubtoken1234567890ABCD",
235+"AIzaSyD-very-real-looking-google-api-key-123",
236+"987654321:AAAAAAAAAAAAAAAAAAAAAAAAAAAA",
237+];
238+const result = redactConfigAuditArgv(argv);
239+expect(result[0]).toBe("node");
240+expect(result[1]).toBe("openclaw");
241+for (const masked of result.slice(2)) {
242+expect(masked).not.toContain("ghp_realgithubtoken");
243+expect(masked).not.toContain("AIzaSyD-very-real-looking");
244+expect(masked).not.toMatch(/AAAAAAAAAAAAAA/);
245+}
246+});
247+248+it("leaves non-secret arguments untouched", () => {
249+const argv = ["node", "openclaw", "gateway", "--port", "8080", "--bind", "lan"];
250+expect(redactConfigAuditArgv(argv)).toEqual(argv);
251+});
252+253+it("redacts unknown but credential-suffixed flags via the heuristic classifier", () => {
254+const argv = [
255+"node",
256+"openclaw",
257+"--custom-api-key",
258+"real-tenant-key-AB12CD34EF56GH78",
259+"--alibaba-model-studio-api-key=plain-value-xyz-12345",
260+"--app-token",
261+"another-secret-value",
262+"--frobnicate-credential=hidden",
263+];
264+const result = redactConfigAuditArgv(argv);
265+expect(result).toEqual([
266+"node",
267+"openclaw",
268+"--custom-api-key",
269+"***",
270+"--alibaba-model-studio-api-key=***",
271+"--app-token",
272+"***",
273+"--frobnicate-credential=***",
274+]);
275+});
276+277+it("redacts key-valued secret flags (Nostr --private-key, Matrix --recovery-key)", () => {
278+const argv = [
279+"node",
280+"openclaw",
281+"channels",
282+"add",
283+"--channel",
284+"nostr",
285+"--private-key",
286+"nsec1realnostrprivatekeyvaluexyz1234567890",
287+"--recovery-key=EsTb-ABCD-1234-EFGH-5678-IJKL-9012-MNOP",
288+];
289+const result = redactConfigAuditArgv(argv);
290+expect(result).toEqual([
291+"node",
292+"openclaw",
293+"channels",
294+"add",
295+"--channel",
296+"nostr",
297+"--private-key",
298+"***",
299+"--recovery-key=***",
300+]);
301+});
302+303+it("redacts unknown *-key flags via the heuristic classifier (private/signing/master/etc.)", () => {
304+const argv = [
305+"node",
306+"openclaw",
307+"--my-plugin-private-key",
308+"tenant-private-key-material-zzz",
309+"--rotated-signing-key=PEM-LIKE-MATERIAL",
310+"--ops-master-key",
311+"ABCDEF1234567890",
312+];
313+const result = redactConfigAuditArgv(argv);
314+expect(result).toEqual([
315+"node",
316+"openclaw",
317+"--my-plugin-private-key",
318+"***",
319+"--rotated-signing-key=***",
320+"--ops-master-key",
321+"***",
322+]);
323+});
324+325+it("masks the next arg after a secret flag even when it looks like another option", () => {
326+const argv = ["openclaw", "--token", "--port", "8080"];
327+expect(redactConfigAuditArgv(argv)).toEqual(["openclaw", "--token", "***", "8080"]);
328+});
329+330+it("redacts dash-leading secret values after bare secret flags", () => {
331+const argv = ["openclaw", "--password", "-secret-value"];
332+expect(redactConfigAuditArgv(argv)).toEqual(["openclaw", "--password", "***"]);
333+});
334+335+it("does not mask when a secret flag is the final arg with no value", () => {
336+const argv = ["openclaw", "--token"];
337+expect(redactConfigAuditArgv(argv)).toEqual(["openclaw", "--token"]);
338+});
339+340+it("caps caller-supplied processInfo argv at 8 entries before redaction", () => {
341+const longArgv = [
342+"node",
343+"openclaw",
344+"--api-key",
345+"secret",
346+"--port",
347+"8080",
348+"--bind",
349+"lan",
350+"--leaks-here-token",
351+"this-must-not-land-in-audit-1234567890",
352+];
353+const base = createConfigWriteAuditRecordBase({
354+configPath: "/tmp/openclaw.json",
355+env: {} as NodeJS.ProcessEnv,
356+existsBefore: true,
357+previousHash: "prev",
358+nextHash: "next",
359+previousBytes: 1,
360+nextBytes: 2,
361+previousMetadata: {
362+dev: null,
363+ino: null,
364+mode: null,
365+nlink: null,
366+uid: null,
367+gid: null,
368+},
369+changedPathCount: 0,
370+hasMetaBefore: true,
371+hasMetaAfter: true,
372+gatewayModeBefore: "local",
373+gatewayModeAfter: "local",
374+suspicious: [],
375+now: "2026-04-30T00:00:00.000Z",
376+processInfo: {
377+pid: 1,
378+ppid: 1,
379+cwd: "/work",
380+argv: longArgv,
381+execArgv: [],
382+},
383+});
384+expect(base.argv).toHaveLength(8);
385+expect(base.argv).not.toContain("this-must-not-land-in-audit-1234567890");
386+expect(base.argv).not.toContain("--leaks-here-token");
387+});
388+389+it("redacts processInfo.argv when explicitly supplied to createConfigWriteAuditRecordBase", () => {
390+const base = createConfigWriteAuditRecordBase({
391+configPath: "/tmp/openclaw.json",
392+env: {} as NodeJS.ProcessEnv,
393+existsBefore: true,
394+previousHash: "prev",
395+nextHash: "next",
396+previousBytes: 1,
397+nextBytes: 2,
398+previousMetadata: {
399+dev: null,
400+ino: null,
401+mode: null,
402+nlink: null,
403+uid: null,
404+gid: null,
405+},
406+changedPathCount: 0,
407+hasMetaBefore: true,
408+hasMetaAfter: true,
409+gatewayModeBefore: "local",
410+gatewayModeAfter: "local",
411+suspicious: [],
412+now: "2026-04-30T00:00:00.000Z",
413+processInfo: {
414+pid: 1,
415+ppid: 1,
416+cwd: "/work",
417+argv: ["node", "openclaw", "--token", "leaked-but-not-anymore-12345"],
418+execArgv: [],
419+},
420+});
421+expect(base.argv).toEqual(["node", "openclaw", "--token", "***"]);
422+});
423+198424it("also accepts flattened audit record params from legacy call sites", async () => {
199425const home = await suiteRootTracker.make("append-flat");
200426const record = createRenameAuditRecord(home);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。