Security: trim audit policy import surfaces

This commit is contained in:
Vincent Koc 2026-03-15 21:39:48 -07:00
parent ebfd32efc3
commit 4cb46f223c
6 changed files with 81 additions and 79 deletions

View File

@ -7,7 +7,6 @@ import { normalizeAgentId } from "../routing/session-key.js";
import { resolveThreadParentSessionKey } from "../sessions/session-key-utils.js";
import { normalizeMessageChannel } from "../utils/message-channel.js";
import { resolveAgentConfig, resolveAgentIdFromSessionKey } from "./agent-scope.js";
import { compileGlobPatterns, matchesAnyGlobPattern } from "./glob-pattern.js";
import type { AnyAgentTool } from "./pi-tools.types.js";
import { pickSandboxToolPolicy } from "./sandbox-tool-policy.js";
import type { SandboxToolPolicy } from "./sandbox.js";
@ -15,34 +14,8 @@ import {
resolveStoredSubagentCapabilities,
type SubagentSessionRole,
} from "./subagent-capabilities.js";
import { expandToolGroups, normalizeToolName } from "./tool-policy.js";
function makeToolPolicyMatcher(policy: SandboxToolPolicy) {
const deny = compileGlobPatterns({
raw: expandToolGroups(policy.deny ?? []),
normalize: normalizeToolName,
});
const allow = compileGlobPatterns({
raw: expandToolGroups(policy.allow ?? []),
normalize: normalizeToolName,
});
return (name: string) => {
const normalized = normalizeToolName(name);
if (matchesAnyGlobPattern(normalized, deny)) {
return false;
}
if (allow.length === 0) {
return true;
}
if (matchesAnyGlobPattern(normalized, allow)) {
return true;
}
if (normalized === "apply_patch" && matchesAnyGlobPattern("exec", allow)) {
return true;
}
return false;
};
}
import { isToolAllowedByPolicies, isToolAllowedByPolicyName } from "./tool-policy-match.js";
import { normalizeToolName } from "./tool-policy.js";
/**
* Tools always denied for sub-agents regardless of depth.
@ -140,19 +113,11 @@ export function resolveSubagentToolPolicyForSession(
return { allow: mergedAllow, deny };
}
export function isToolAllowedByPolicyName(name: string, policy?: SandboxToolPolicy): boolean {
if (!policy) {
return true;
}
return makeToolPolicyMatcher(policy)(name);
}
export function filterToolsByPolicy(tools: AnyAgentTool[], policy?: SandboxToolPolicy) {
if (!policy) {
return tools;
}
const matcher = makeToolPolicyMatcher(policy);
return tools.filter((tool) => matcher(tool.name));
return tools.filter((tool) => isToolAllowedByPolicyName(tool.name, policy));
}
type ToolPolicyConfig = {
@ -381,9 +346,4 @@ export function resolveGroupToolPolicy(params: {
return pickSandboxToolPolicy(toolsConfig);
}
export function isToolAllowedByPolicies(
name: string,
policies: Array<SandboxToolPolicy | undefined>,
) {
return policies.every((policy) => isToolAllowedByPolicyName(name, policy));
}
export { isToolAllowedByPolicies, isToolAllowedByPolicyName } from "./tool-policy-match.js";

View File

@ -0,0 +1,44 @@
import { compileGlobPatterns, matchesAnyGlobPattern } from "./glob-pattern.js";
import type { SandboxToolPolicy } from "./sandbox/types.js";
import { expandToolGroups, normalizeToolName } from "./tool-policy.js";
function makeToolPolicyMatcher(policy: SandboxToolPolicy) {
const deny = compileGlobPatterns({
raw: expandToolGroups(policy.deny ?? []),
normalize: normalizeToolName,
});
const allow = compileGlobPatterns({
raw: expandToolGroups(policy.allow ?? []),
normalize: normalizeToolName,
});
return (name: string) => {
const normalized = normalizeToolName(name);
if (matchesAnyGlobPattern(normalized, deny)) {
return false;
}
if (allow.length === 0) {
return true;
}
if (matchesAnyGlobPattern(normalized, allow)) {
return true;
}
if (normalized === "apply_patch" && matchesAnyGlobPattern("exec", allow)) {
return true;
}
return false;
};
}
export function isToolAllowedByPolicyName(name: string, policy?: SandboxToolPolicy): boolean {
if (!policy) {
return true;
}
return makeToolPolicyMatcher(policy)(name);
}
export function isToolAllowedByPolicies(
name: string,
policies: Array<SandboxToolPolicy | undefined>,
) {
return policies.every((policy) => isToolAllowedByPolicyName(name, policy));
}

View File

@ -0,0 +1,24 @@
import { normalizeAgentId } from "../routing/session-key.js";
export function resolveAllowedAgentIds(raw: string[] | undefined): Set<string> | undefined {
if (!Array.isArray(raw)) {
return undefined;
}
const allowed = new Set<string>();
let hasWildcard = false;
for (const entry of raw) {
const trimmed = entry.trim();
if (!trimmed) {
continue;
}
if (trimmed === "*") {
hasWildcard = true;
break;
}
allowed.add(normalizeAgentId(trimmed));
}
if (hasWildcard) {
return undefined;
}
return allowed;
}

View File

@ -5,9 +5,10 @@ import { listChannelPlugins } from "../channels/plugins/index.js";
import type { ChannelId } from "../channels/plugins/types.js";
import type { OpenClawConfig } from "../config/config.js";
import { readJsonBodyWithLimit, requestBodyErrorToText } from "../infra/http-body.js";
import { normalizeAgentId, parseAgentSessionKey } from "../routing/session-key.js";
import { parseAgentSessionKey } from "../routing/session-key.js";
import { normalizeMessageChannel } from "../utils/message-channel.js";
import { type HookMappingResolved, resolveHookMappings } from "./hooks-mapping.js";
import { resolveAllowedAgentIds } from "./hooks-policy.js";
const DEFAULT_HOOKS_PATH = "/hooks";
const DEFAULT_HOOKS_MAX_BODY_BYTES = 256 * 1024;
@ -100,29 +101,6 @@ function resolveKnownAgentIds(cfg: OpenClawConfig, defaultAgentId: string): Set<
return known;
}
export function resolveAllowedAgentIds(raw: string[] | undefined): Set<string> | undefined {
if (!Array.isArray(raw)) {
return undefined;
}
const allowed = new Set<string>();
let hasWildcard = false;
for (const entry of raw) {
const trimmed = entry.trim();
if (!trimmed) {
continue;
}
if (trimmed === "*") {
hasWildcard = true;
break;
}
allowed.add(normalizeAgentId(trimmed));
}
if (hasWildcard) {
return undefined;
}
return allowed;
}
function resolveSessionKey(raw: string | undefined): string | undefined {
const value = raw?.trim();
return value ? value : undefined;

View File

@ -6,15 +6,13 @@
import fs from "node:fs/promises";
import path from "node:path";
import { resolveDefaultAgentId } from "../agents/agent-scope.js";
import { isToolAllowedByPolicies } from "../agents/pi-tools.policy.js";
import {
resolveSandboxConfigForAgent,
resolveSandboxToolPolicyForAgent,
} from "../agents/sandbox.js";
import { resolveSandboxConfigForAgent } from "../agents/sandbox/config.js";
import { SANDBOX_BROWSER_SECURITY_HASH_EPOCH } from "../agents/sandbox/constants.js";
import { execDockerRaw, type ExecDockerRawResult } from "../agents/sandbox/docker.js";
import { resolveSandboxToolPolicyForAgent } from "../agents/sandbox/tool-policy.js";
import type { SandboxToolPolicy } from "../agents/sandbox/types.js";
import { loadWorkspaceSkillEntries } from "../agents/skills.js";
import { isToolAllowedByPolicies } from "../agents/tool-policy-match.js";
import { resolveToolProfilePolicy } from "../agents/tool-policy.js";
import { listAgentWorkspaceDirs } from "../agents/workspace-dirs.js";
import { formatCliCommand } from "../cli/command-format.js";

View File

@ -1,16 +1,14 @@
import { isToolAllowedByPolicies } from "../agents/pi-tools.policy.js";
import {
resolveSandboxConfigForAgent,
resolveSandboxToolPolicyForAgent,
} from "../agents/sandbox.js";
import { resolveSandboxConfigForAgent } from "../agents/sandbox/config.js";
import { isDangerousNetworkMode, normalizeNetworkMode } from "../agents/sandbox/network-mode.js";
/**
* Synchronous security audit collector functions.
*
* These functions analyze config-based security properties without I/O.
*/
import { resolveSandboxToolPolicyForAgent } from "../agents/sandbox/tool-policy.js";
import type { SandboxToolPolicy } from "../agents/sandbox/types.js";
import { getBlockedBindReason } from "../agents/sandbox/validate-sandbox-security.js";
import { isToolAllowedByPolicies } from "../agents/tool-policy-match.js";
import { resolveToolProfilePolicy } from "../agents/tool-policy.js";
import { resolveBrowserConfig } from "../browser/config.js";
import { formatCliCommand } from "../cli/command-format.js";
@ -21,7 +19,7 @@ import {
} from "../config/model-input.js";
import type { AgentToolsConfig } from "../config/types.tools.js";
import { resolveGatewayAuth } from "../gateway/auth.js";
import { resolveAllowedAgentIds } from "../gateway/hooks.js";
import { resolveAllowedAgentIds } from "../gateway/hooks-policy.js";
import {
DEFAULT_DANGEROUS_NODE_COMMANDS,
resolveNodeCommandAllowlist,