diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d4465124d..2b72fd19b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Security/Audit: distinguish external webhooks (`hooks.enabled`) from internal hooks (`hooks.internal.enabled`) in attack-surface summaries to avoid false exposure signals when only internal hooks are enabled. (#13474) Thanks @mcaxtr. - Sandbox: pass configured `sandbox.docker.env` variables to sandbox containers at `docker create` time. (#15138) Thanks @stevebot-alive. - Onboarding/CLI: restore terminal state without resuming paused `stdin`, so onboarding exits cleanly after choosing Web UI and the installer returns instead of appearing stuck. - macOS Voice Wake: fix a crash in trigger trimming for CJK/Unicode transcripts by matching and slicing on original-string ranges instead of transformed-string indices. (#11052) Thanks @Flash-LHR. diff --git a/src/security/audit-extra.sync.test.ts b/src/security/audit-extra.sync.test.ts new file mode 100644 index 0000000000..88d374f2f3 --- /dev/null +++ b/src/security/audit-extra.sync.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import type { OpenClawConfig } from "../config/config.js"; +import { collectAttackSurfaceSummaryFindings } from "./audit-extra.sync.js"; + +describe("collectAttackSurfaceSummaryFindings", () => { + it("distinguishes external webhooks from internal hooks when only internal hooks are enabled", () => { + const cfg: OpenClawConfig = { + hooks: { internal: { enabled: true } }, + }; + + const [finding] = collectAttackSurfaceSummaryFindings(cfg); + expect(finding.checkId).toBe("summary.attack_surface"); + expect(finding.detail).toContain("hooks.webhooks: disabled"); + expect(finding.detail).toContain("hooks.internal: enabled"); + }); + + it("reports both hook systems as enabled when both are configured", () => { + const cfg: OpenClawConfig = { + hooks: { enabled: true, internal: { enabled: true } }, + }; + + const [finding] = collectAttackSurfaceSummaryFindings(cfg); + expect(finding.detail).toContain("hooks.webhooks: enabled"); + expect(finding.detail).toContain("hooks.internal: enabled"); + }); + + it("reports both hook systems as disabled when neither is configured", () => { + const cfg: OpenClawConfig = {}; + + const [finding] = collectAttackSurfaceSummaryFindings(cfg); + expect(finding.detail).toContain("hooks.webhooks: disabled"); + expect(finding.detail).toContain("hooks.internal: disabled"); + }); +}); diff --git a/src/security/audit-extra.sync.ts b/src/security/audit-extra.sync.ts index c2e9a635bb..45330dbfd2 100644 --- a/src/security/audit-extra.sync.ts +++ b/src/security/audit-extra.sync.ts @@ -303,7 +303,8 @@ function listGroupPolicyOpen(cfg: OpenClawConfig): string[] { export function collectAttackSurfaceSummaryFindings(cfg: OpenClawConfig): SecurityAuditFinding[] { const group = summarizeGroupPolicy(cfg); const elevated = cfg.tools?.elevated?.enabled !== false; - const hooksEnabled = cfg.hooks?.enabled === true; + const webhooksEnabled = cfg.hooks?.enabled === true; + const internalHooksEnabled = cfg.hooks?.internal?.enabled === true; const browserEnabled = cfg.browser?.enabled ?? true; const detail = @@ -311,7 +312,9 @@ export function collectAttackSurfaceSummaryFindings(cfg: OpenClawConfig): Securi `\n` + `tools.elevated: ${elevated ? "enabled" : "disabled"}` + `\n` + - `hooks: ${hooksEnabled ? "enabled" : "disabled"}` + + `hooks.webhooks: ${webhooksEnabled ? "enabled" : "disabled"}` + + `\n` + + `hooks.internal: ${internalHooksEnabled ? "enabled" : "disabled"}` + `\n` + `browser control: ${browserEnabled ? "enabled" : "disabled"}`;