feat(tools): enforce skill capabilities and secret scopes
This commit is contained in:
@@ -160,4 +160,106 @@ describe('ToolExecutor', () => {
|
||||
const result = await resultPromise;
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('enforces skill filesystem write allowlist', async () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register({
|
||||
name: 'file.write',
|
||||
description: 'write',
|
||||
inputSchema: { type: 'object', properties: {} },
|
||||
execute: async () => ({ success: true, output: 'ok' }),
|
||||
});
|
||||
const hooks = new HookEngine({ confirm: [], log: [], silent: [] });
|
||||
const executor = new ToolExecutor(registry, hooks);
|
||||
|
||||
const allowed = await executor.execute(
|
||||
'file.write',
|
||||
{ path: '/tmp/flynn-skill-ok.txt', content: 'hello' },
|
||||
{
|
||||
skillName: 'test-skill',
|
||||
skillPermissions: {
|
||||
execution_environment: 'host',
|
||||
fs: { write: ['/tmp/**'] },
|
||||
},
|
||||
executionEnvironment: 'host',
|
||||
autonomyLevel: 'autonomous',
|
||||
},
|
||||
);
|
||||
expect(allowed.success).toBe(true);
|
||||
|
||||
const denied = await executor.execute(
|
||||
'file.write',
|
||||
{ path: '/etc/passwd', content: 'nope' },
|
||||
{
|
||||
skillName: 'test-skill',
|
||||
skillPermissions: {
|
||||
execution_environment: 'host',
|
||||
fs: { write: ['/tmp/**'] },
|
||||
},
|
||||
executionEnvironment: 'host',
|
||||
autonomyLevel: 'autonomous',
|
||||
},
|
||||
);
|
||||
expect(denied.success).toBe(false);
|
||||
expect(denied.error).toContain('path not allowed');
|
||||
});
|
||||
|
||||
it('enforces tool secret scopes for skill contexts', async () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register({
|
||||
name: 'gmail.list',
|
||||
description: 'gmail',
|
||||
requiredSecretScopes: ['gmail'],
|
||||
inputSchema: { type: 'object', properties: {} },
|
||||
execute: async () => ({ success: true, output: 'ok' }),
|
||||
});
|
||||
const hooks = new HookEngine({ confirm: [], log: [], silent: [] });
|
||||
const executor = new ToolExecutor(registry, hooks);
|
||||
|
||||
const result = await executor.execute('gmail.list', {}, {
|
||||
skillName: 'no-secrets-skill',
|
||||
skillPermissions: { secrets: [] },
|
||||
executionEnvironment: 'host',
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('missing secret scopes');
|
||||
});
|
||||
|
||||
it('blocks high-risk tool calls with injection markers when untrusted content is present', async () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register({
|
||||
name: 'shell.exec',
|
||||
description: 'shell',
|
||||
inputSchema: { type: 'object', properties: {} },
|
||||
execute: async () => ({ success: true, output: 'ok' }),
|
||||
});
|
||||
const hooks = new HookEngine({ confirm: [], log: [], silent: [] });
|
||||
const executor = new ToolExecutor(registry, hooks);
|
||||
|
||||
const result = await executor.execute('shell.exec', { command: 'rm -rf /' }, {
|
||||
untrustedContent: true,
|
||||
executionEnvironment: 'host',
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('blocked');
|
||||
});
|
||||
|
||||
it('blocks passing secret-like args to network tools when untrusted content is present', async () => {
|
||||
const registry = new ToolRegistry();
|
||||
registry.register({
|
||||
name: 'web.fetch',
|
||||
description: 'fetch',
|
||||
inputSchema: { type: 'object', properties: {} },
|
||||
execute: async () => ({ success: true, output: 'ok' }),
|
||||
});
|
||||
const hooks = new HookEngine({ confirm: [], log: [], silent: [] });
|
||||
const executor = new ToolExecutor(registry, hooks);
|
||||
|
||||
const result = await executor.execute('web.fetch', { url: 'https://example.com', authorization: 'Bearer abcdef' }, {
|
||||
untrustedContent: true,
|
||||
executionEnvironment: 'host',
|
||||
});
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('refusing to pass');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user