feat(cli): configure policy engine from existing settings (#8348)

This commit is contained in:
Allen Hutchison
2025-09-18 13:44:23 -07:00
committed by GitHub
parent ec0acc486e
commit afba59a953
7 changed files with 1149 additions and 2 deletions

View File

@@ -8,6 +8,8 @@
export * from './config/config.js';
export * from './output/types.js';
export * from './output/json-formatter.js';
export * from './policy/types.js';
export * from './policy/policy-engine.js';
// Export Core Logic
export * from './core/client.js';

View File

@@ -217,6 +217,75 @@ describe('PolicyEngine', () => {
});
});
describe('MCP server wildcard patterns', () => {
it('should match MCP server wildcard patterns', () => {
const rules: PolicyRule[] = [
{
toolName: 'my-server__*',
decision: PolicyDecision.ALLOW,
priority: 10,
},
{
toolName: 'blocked-server__*',
decision: PolicyDecision.DENY,
priority: 20,
},
];
engine = new PolicyEngine({ rules });
// Should match my-server tools
expect(engine.check({ name: 'my-server__tool1' })).toBe(
PolicyDecision.ALLOW,
);
expect(engine.check({ name: 'my-server__another_tool' })).toBe(
PolicyDecision.ALLOW,
);
// Should match blocked-server tools
expect(engine.check({ name: 'blocked-server__tool1' })).toBe(
PolicyDecision.DENY,
);
expect(engine.check({ name: 'blocked-server__dangerous' })).toBe(
PolicyDecision.DENY,
);
// Should not match other patterns
expect(engine.check({ name: 'other-server__tool' })).toBe(
PolicyDecision.ASK_USER,
);
expect(engine.check({ name: 'my-server-tool' })).toBe(
PolicyDecision.ASK_USER,
); // No __ separator
expect(engine.check({ name: 'my-server' })).toBe(PolicyDecision.ASK_USER); // No tool name
});
it('should prioritize specific tool rules over server wildcards', () => {
const rules: PolicyRule[] = [
{
toolName: 'my-server__*',
decision: PolicyDecision.ALLOW,
priority: 10,
},
{
toolName: 'my-server__dangerous-tool',
decision: PolicyDecision.DENY,
priority: 20,
},
];
engine = new PolicyEngine({ rules });
// Specific tool deny should override server allow
expect(engine.check({ name: 'my-server__dangerous-tool' })).toBe(
PolicyDecision.DENY,
);
expect(engine.check({ name: 'my-server__safe-tool' })).toBe(
PolicyDecision.ALLOW,
);
});
});
describe('complex scenarios', () => {
it('should handle multiple matching rules with different priorities', () => {
const rules: PolicyRule[] = [

View File

@@ -18,8 +18,16 @@ function ruleMatches(
stringifiedArgs: string | undefined,
): boolean {
// Check tool name if specified
if (rule.toolName && toolCall.name !== rule.toolName) {
return false;
if (rule.toolName) {
// Support wildcard patterns: "serverName__*" matches "serverName__anyTool"
if (rule.toolName.endsWith('__*')) {
const prefix = rule.toolName.slice(0, -3); // Remove "__*"
if (!toolCall.name || !toolCall.name.startsWith(prefix + '__')) {
return false;
}
} else if (toolCall.name !== rule.toolName) {
return false;
}
}
// Check args pattern if specified