From 7e5c3753f6f797d4faf7e22eefe5a11f5a3a17ed Mon Sep 17 00:00:00 2001 From: "openclaw-clownfish[bot]" <280122609+openclaw-clownfish[bot]@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:34:55 -0700 Subject: [PATCH] fix(security): include dangerous commands in audit known commands (#73915) Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + src/security/audit-extra.sync.ts | 6 +++++ .../audit-node-command-findings.test.ts | 24 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71c4a30664f..99907886fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Security/audit: recognize dangerous node command IDs as valid `gateway.nodes.denyCommands` entries, so audit only warns on real typos or unsupported patterns. (#56923) Thanks @chziyue. - Telegram/exec approvals: stop treating general Telegram chat allowlists and `defaultTo` routes as native exec approvers; Telegram now uses explicit `execApprovals.approvers` or owner identity from `commands.ownerAllowFrom`, matching the first-pairing owner bootstrap path. Thanks @pashpashpash. - Chat commands: route sensitive group `/diagnostics` and `/export-trajectory` approvals and results to a private owner route, preferring same-surface DMs before falling back to the first configured owner route, so Discord group invocations can land in Telegram when that is the primary owner interface. Thanks @pashpashpash. - Plugin SDK/Discord: restore a deprecated `openclaw/plugin-sdk/discord` compatibility facade and the legacy compat group-policy warning export for the published `@openclaw/discord@2026.3.13` package, covering its config, account, directory, status, and thread-binding imports while keeping new plugins on generic SDK subpaths. Fixes #73685; supersedes #73703. Thanks @rderickson9 and @SymbolStar. diff --git a/src/security/audit-extra.sync.ts b/src/security/audit-extra.sync.ts index 85264a90497..b094e16c665 100644 --- a/src/security/audit-extra.sync.ts +++ b/src/security/audit-extra.sync.ts @@ -190,6 +190,12 @@ function listKnownNodeCommands(cfg: OpenClawConfig): Set { } } } + for (const cmd of DEFAULT_DANGEROUS_NODE_COMMANDS) { + const normalized = normalizeNodeCommand(cmd); + if (normalized) { + out.add(normalized); + } + } return out; } diff --git a/src/security/audit-node-command-findings.test.ts b/src/security/audit-node-command-findings.test.ts index ae069e3b124..04fc4ab2b73 100644 --- a/src/security/audit-node-command-findings.test.ts +++ b/src/security/audit-node-command-findings.test.ts @@ -56,6 +56,18 @@ describe("security audit node command findings", () => { detailIncludes: ["zzzzzzzzzzzzzz"], detailExcludes: ["did you mean"], }, + { + name: "keeps valid dangerous denyCommands entries out of unknown warnings", + cfg: { + gateway: { + nodes: { + denyCommands: ["camera.snap", "screen.record", "camera.snapp", "system.*"], + }, + }, + } satisfies OpenClawConfig, + detailIncludes: ["camera.snapp", "system.*", "did you mean", "camera.snap"], + detailExcludes: ["screen.record"], + }, ] as const; for (const testCase of cases) { @@ -73,6 +85,18 @@ describe("security audit node command findings", () => { } }); + it("does not flag valid dangerous gateway.nodes.denyCommands entries as ineffective", () => { + const findings = collectNodeDenyCommandPatternFindings({ + gateway: { + nodes: { + denyCommands: ["camera.snap", "camera.clip", "screen.record", "sms.send"], + }, + }, + } satisfies OpenClawConfig); + + expect(findings).toEqual([]); + }); + it("evaluates dangerous gateway.nodes.allowCommands findings", () => { const cases: Array<{ name: string;