godcrm/backend/services/agent-tools/__tests__/capability-gate.test.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

83 lines
3.2 KiB
JavaScript

// @vitest-environment node
/**
* ADR-165 WP-1 — Unified capability gate (pure decision layer).
*
* Pins:
* - No agent scope in context (system caller: MCP userId=1, human owner) → unrestricted.
* - Agent scope present + tool in allowlist → permitted, no violation.
* - Agent scope present + tool NOT in allowlist → violation.
* · warn-only (enforce=false): permitted (proceeds) but flagged for telemetry.
* · enforce (enforce=true): blocked.
* - Allowlist names read from BOTH OpenAI-shape ({function:{name}}) and flat ({name}) defs,
* since callers thread the same resolved `allowedTools` list they built for the LLM.
*/
import { describe, it, expect } from 'vitest';
import { resolveAllowlistNames, evaluateToolGate } from '../capability-gate.js';
const oaiTools = [
{ type: 'function', function: { name: 'query_table_data' } },
{ type: 'function', function: { name: 'get_table_schema' } },
];
describe('resolveAllowlistNames', () => {
it('returns null when no context is provided (system caller)', () => {
expect(resolveAllowlistNames(undefined)).toBeNull();
expect(resolveAllowlistNames(null)).toBeNull();
});
it('returns null when context carries no allowedTools (system caller)', () => {
expect(resolveAllowlistNames({ spaceId: 11, source: 'mcp' })).toBeNull();
});
it('returns null for an empty allowedTools array', () => {
expect(resolveAllowlistNames({ allowedTools: [] })).toBeNull();
});
it('extracts names from OpenAI-shape tool defs', () => {
const names = resolveAllowlistNames({ allowedTools: oaiTools });
expect(names).toBeInstanceOf(Set);
expect(names.has('query_table_data')).toBe(true);
expect(names.has('get_table_schema')).toBe(true);
expect(names.size).toBe(2);
});
it('also accepts flat {name} tool defs', () => {
const names = resolveAllowlistNames({ allowedTools: [{ name: 'delete_table' }] });
expect(names.has('delete_table')).toBe(true);
});
it('ignores malformed entries without a name', () => {
const names = resolveAllowlistNames({ allowedTools: [null, {}, { function: {} }, { name: 'ok' }] });
expect(names.size).toBe(1);
expect(names.has('ok')).toBe(true);
});
});
describe('evaluateToolGate', () => {
const scoped = { allowedTools: oaiTools };
it('permits any tool when no agent scope is present (unrestricted system caller)', () => {
for (const enforce of [false, true]) {
const gate = evaluateToolGate('delete_table', { spaceId: 11 }, enforce);
expect(gate).toEqual({ permitted: true, violation: false });
}
});
it('permits an in-allowlist tool with no violation', () => {
for (const enforce of [false, true]) {
const gate = evaluateToolGate('query_table_data', scoped, enforce);
expect(gate).toEqual({ permitted: true, violation: false });
}
});
it('warn-only: flags an out-of-allowlist tool as a violation but still permits it', () => {
const gate = evaluateToolGate('delete_table', scoped, false);
expect(gate).toEqual({ permitted: true, violation: true });
});
it('enforce: blocks an out-of-allowlist tool', () => {
const gate = evaluateToolGate('delete_table', scoped, true);
expect(gate).toEqual({ permitted: false, violation: true });
});
});