feat(tools): enforce skill capabilities and secret scopes
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { AutonomyLevel, ToolsConfig, ToolProfile } from '../config/schema.js';
|
||||
import type { Tool } from './types.js';
|
||||
import type { SkillPermissions } from '../skills/types.js';
|
||||
|
||||
// ── Profile definitions ─────────────────────────────────────────────
|
||||
|
||||
@@ -142,6 +143,41 @@ export interface ToolPolicyContext {
|
||||
tier?: string;
|
||||
/** Autonomy level for tool execution (affects confirmation requirements). */
|
||||
autonomyLevel?: AutonomyLevel;
|
||||
|
||||
/** Optional active skill name (for capability scoping and audit). */
|
||||
skillName?: string;
|
||||
/** Optional active skill permissions (capability declarations). */
|
||||
skillPermissions?: SkillPermissions;
|
||||
|
||||
/** Execution environment for high-risk operations. */
|
||||
executionEnvironment?: 'host' | 'sandbox';
|
||||
|
||||
/** Secret scopes allowed for this context (used by executor). */
|
||||
allowedSecretScopes?: string[];
|
||||
|
||||
/** True when untrusted content has been introduced in this run. */
|
||||
untrustedContent?: boolean;
|
||||
}
|
||||
|
||||
function resolveSkillAllowedNames(allToolNames: string[], permissions?: SkillPermissions): Set<string> | null {
|
||||
if (!permissions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const explicitTools = Array.isArray(permissions.tools) ? permissions.tools : undefined;
|
||||
const toolGroups = Array.isArray(permissions.tool_groups) ? permissions.tool_groups : undefined;
|
||||
|
||||
const patterns = (explicitTools && explicitTools.length > 0)
|
||||
? expandGroups(explicitTools)
|
||||
: (toolGroups && toolGroups.length > 0)
|
||||
? expandGroups(toolGroups)
|
||||
: [];
|
||||
|
||||
if (patterns.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Set(allToolNames.filter((name) => matchesAnyPattern(name, patterns)));
|
||||
}
|
||||
|
||||
// ── ToolPolicy engine ───────────────────────────────────────────────
|
||||
@@ -225,6 +261,17 @@ export class ToolPolicy {
|
||||
allowed = intersect(allowed, providerAllowed);
|
||||
}
|
||||
|
||||
// Step 6: Apply skill capability restrictions (intersection)
|
||||
// Safe-by-default: if a skill is active but has no permissions manifest, deny all tools.
|
||||
if (context?.skillName && !context.skillPermissions) {
|
||||
allowed = new Set();
|
||||
} else {
|
||||
const skillAllowed = resolveSkillAllowedNames(allToolNames, context?.skillPermissions);
|
||||
if (skillAllowed) {
|
||||
allowed = intersect(allowed, skillAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
return allowed;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user