mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 07:07:39 +00:00
fix(security): flag open-group runtime/fs exposure in audit
This commit is contained in:
@@ -1041,5 +1041,67 @@ export function collectExposureMatrixFindings(cfg: OpenClawConfig): SecurityAudi
|
||||
});
|
||||
}
|
||||
|
||||
const contexts: Array<{
|
||||
label: string;
|
||||
agentId?: string;
|
||||
tools?: AgentToolsConfig;
|
||||
}> = [{ label: "agents.defaults" }];
|
||||
for (const agent of cfg.agents?.list ?? []) {
|
||||
if (!agent || typeof agent !== "object" || typeof agent.id !== "string") {
|
||||
continue;
|
||||
}
|
||||
contexts.push({
|
||||
label: `agents.list.${agent.id}`,
|
||||
agentId: agent.id,
|
||||
tools: agent.tools,
|
||||
});
|
||||
}
|
||||
|
||||
const riskyContexts: string[] = [];
|
||||
let hasRuntimeRisk = false;
|
||||
for (const context of contexts) {
|
||||
const sandboxMode = resolveSandboxConfigForAgent(cfg, context.agentId).mode;
|
||||
const policies = resolveToolPolicies({
|
||||
cfg,
|
||||
agentTools: context.tools,
|
||||
sandboxMode,
|
||||
agentId: context.agentId ?? null,
|
||||
});
|
||||
const runtimeTools = ["exec", "process"].filter((tool) =>
|
||||
isToolAllowedByPolicies(tool, policies),
|
||||
);
|
||||
const fsTools = ["read", "write", "edit", "apply_patch"].filter((tool) =>
|
||||
isToolAllowedByPolicies(tool, policies),
|
||||
);
|
||||
const fsWorkspaceOnly = context.tools?.fs?.workspaceOnly ?? cfg.tools?.fs?.workspaceOnly;
|
||||
const runtimeUnguarded = runtimeTools.length > 0 && sandboxMode !== "all";
|
||||
const fsUnguarded = fsTools.length > 0 && sandboxMode !== "all" && fsWorkspaceOnly !== true;
|
||||
if (!runtimeUnguarded && !fsUnguarded) {
|
||||
continue;
|
||||
}
|
||||
if (runtimeUnguarded) {
|
||||
hasRuntimeRisk = true;
|
||||
}
|
||||
riskyContexts.push(
|
||||
`${context.label} (sandbox=${sandboxMode}; runtime=[${runtimeTools.join(", ") || "off"}]; fs=[${fsTools.join(", ") || "off"}]; fs.workspaceOnly=${
|
||||
fsWorkspaceOnly === true ? "true" : "false"
|
||||
})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (riskyContexts.length > 0) {
|
||||
findings.push({
|
||||
checkId: "security.exposure.open_groups_with_runtime_or_fs",
|
||||
severity: hasRuntimeRisk ? "critical" : "warn",
|
||||
title: "Open groupPolicy with runtime/filesystem tools exposed",
|
||||
detail:
|
||||
`Found groupPolicy="open" at:\n${openGroups.map((p) => `- ${p}`).join("\n")}\n` +
|
||||
`Risky tool exposure contexts:\n${riskyContexts.map((line) => `- ${line}`).join("\n")}\n` +
|
||||
"Prompt injection in open groups can trigger command/file actions in these contexts.",
|
||||
remediation:
|
||||
'For open groups, prefer tools.profile="messaging" (or deny group:runtime/group:fs), set tools.fs.workspaceOnly=true, and use agents.defaults.sandbox.mode="all" for exposed agents.',
|
||||
});
|
||||
}
|
||||
|
||||
return findings;
|
||||
}
|
||||
|
||||
@@ -2150,6 +2150,63 @@ description: test skill
|
||||
);
|
||||
});
|
||||
|
||||
it("flags open groupPolicy when runtime/filesystem tools are exposed without guards", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: { whatsapp: { groupPolicy: "open" } },
|
||||
tools: { elevated: { enabled: false } },
|
||||
};
|
||||
|
||||
const res = await audit(cfg);
|
||||
|
||||
expect(res.findings).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
checkId: "security.exposure.open_groups_with_runtime_or_fs",
|
||||
severity: "critical",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("does not flag runtime/filesystem exposure for open groups when sandbox mode is all", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: { whatsapp: { groupPolicy: "open" } },
|
||||
tools: {
|
||||
elevated: { enabled: false },
|
||||
profile: "coding",
|
||||
},
|
||||
agents: {
|
||||
defaults: {
|
||||
sandbox: { mode: "all" },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const res = await audit(cfg);
|
||||
|
||||
expect(
|
||||
res.findings.some((f) => f.checkId === "security.exposure.open_groups_with_runtime_or_fs"),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("does not flag runtime/filesystem exposure for open groups when runtime is denied and fs is workspace-only", async () => {
|
||||
const cfg: OpenClawConfig = {
|
||||
channels: { whatsapp: { groupPolicy: "open" } },
|
||||
tools: {
|
||||
elevated: { enabled: false },
|
||||
profile: "coding",
|
||||
deny: ["group:runtime"],
|
||||
fs: { workspaceOnly: true },
|
||||
},
|
||||
};
|
||||
|
||||
const res = await audit(cfg);
|
||||
|
||||
expect(
|
||||
res.findings.some((f) => f.checkId === "security.exposure.open_groups_with_runtime_or_fs"),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
describe("maybeProbeGateway auth selection", () => {
|
||||
let envSnapshot: ReturnType<typeof captureEnv>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user