mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-16 01:20:45 +00:00
* fix(security): stop implicit tool grants from config sections (#47487) Configured tool sections (tools.exec, tools.fs) no longer implicitly widen restrictive profiles (messaging, minimal). Previously, having a tools.exec section anywhere in config — even just safety settings like security: "allowlist" — would automatically add exec and process to the profile's allowed tools, defeating the purpose of the restrictive profile. The same pattern existed in tool-fs-policy.ts where tools.fs presence would add read/write/edit to the profile allowlist for root expansion. Changes: - pi-tools.policy.ts: Stop merging implicit grants into profileAlsoAllow. Renamed resolveImplicitProfileAlsoAllow → detectImplicitProfileGrants and use it only for a startup warning that tells users to add explicit alsoAllow entries. - tool-fs-policy.ts: Remove the implicit read/write/edit grant from resolveEffectiveToolFsRootExpansionAllowed when tools.fs is present. Root expansion now requires actual read access via profile or alsoAllow. - Updated 4 existing tests and added 3 new regression tests. Migration: users who relied on tools.exec or tools.fs implicitly granting access under a restrictive profile should add explicit alsoAllow entries: tools: profile: "messaging" alsoAllow: ["exec", "process"] # was implicit, now required exec: { security: "allowlist" } Fixes #47487 * fix: address tool policy review feedback
61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
|
import { resolveAgentConfig } from "./agent-scope.js";
|
|
import { pickSandboxToolPolicy } from "./sandbox-tool-policy.js";
|
|
import { isToolAllowedByPolicies } from "./tool-policy-match.js";
|
|
import { mergeAlsoAllowPolicy, resolveToolProfilePolicy } from "./tool-policy.js";
|
|
|
|
export type ToolFsPolicy = {
|
|
workspaceOnly: boolean;
|
|
};
|
|
|
|
export function createToolFsPolicy(params: { workspaceOnly?: boolean }): ToolFsPolicy {
|
|
return {
|
|
workspaceOnly: params.workspaceOnly === true,
|
|
};
|
|
}
|
|
|
|
export function resolveToolFsConfig(params: { cfg?: OpenClawConfig; agentId?: string }): {
|
|
workspaceOnly?: boolean;
|
|
} {
|
|
const cfg = params.cfg;
|
|
const globalFs = cfg?.tools?.fs;
|
|
const agentFs =
|
|
cfg && params.agentId ? resolveAgentConfig(cfg, params.agentId)?.tools?.fs : undefined;
|
|
return {
|
|
workspaceOnly: agentFs?.workspaceOnly ?? globalFs?.workspaceOnly,
|
|
};
|
|
}
|
|
|
|
export function resolveEffectiveToolFsWorkspaceOnly(params: {
|
|
cfg?: OpenClawConfig;
|
|
agentId?: string;
|
|
}): boolean {
|
|
return resolveToolFsConfig(params).workspaceOnly === true;
|
|
}
|
|
|
|
export function resolveEffectiveToolFsRootExpansionAllowed(params: {
|
|
cfg?: OpenClawConfig;
|
|
agentId?: string;
|
|
}): boolean {
|
|
const cfg = params.cfg;
|
|
if (!cfg) {
|
|
return true;
|
|
}
|
|
const agentTools = params.agentId ? resolveAgentConfig(cfg, params.agentId)?.tools : undefined;
|
|
const globalTools = cfg.tools;
|
|
const profile = agentTools?.profile ?? globalTools?.profile;
|
|
const profileAlsoAllow = new Set(agentTools?.alsoAllow ?? globalTools?.alsoAllow ?? []);
|
|
const fsConfig = resolveToolFsConfig(params);
|
|
if (fsConfig.workspaceOnly === true) {
|
|
return false;
|
|
}
|
|
// tools.fs presence does not grant access; require profile or alsoAllow (#47487).
|
|
const profilePolicy = mergeAlsoAllowPolicy(
|
|
resolveToolProfilePolicy(profile),
|
|
profileAlsoAllow.size > 0 ? Array.from(profileAlsoAllow) : undefined,
|
|
);
|
|
const globalPolicy = pickSandboxToolPolicy(globalTools);
|
|
const agentPolicy = pickSandboxToolPolicy(agentTools);
|
|
return isToolAllowedByPolicies("read", [profilePolicy, globalPolicy, agentPolicy]);
|
|
}
|