Files
portal/tools/enforce-mcp-classification.test.mjs
T
Дмитрий ecca24f451 feat(m5): 7.3 mcp-classification +egress-скан исходящего (Пакет 7, Блок 4.3)
scanEgress(toolInput): даже разрешённый классификатором MCP-вызов не должен выносить
наружу — block при: секрет в аргументах (общий secret-scan, анти-дрейф с 7.1), data:base64
URI / file:// схема / IP-литерал URL (обход DNS/allowlist), раздутый payload (> maxBytes).
exfil-схемы узко-таргетированы (data:...;base64, и file://) — обычный текст не ловят.
decide: после allow-классификации запускает scanEgress → block с reason «egress: …».

+8 тестов. enforce-mcp-classification 10/10. Дыра «что уходит наружу» закрыта.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 15:15:49 +03:00

53 lines
2.6 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { decide } from './enforce-mcp-classification.mjs';
describe('enforce-mcp-classification decide()', () => {
it('allows non-mcp tools (no-op)', () => {
expect(decide({ toolName: 'Bash', toolInput: { command: 'ls' } }).block).toBe(false);
});
it('blocks an unknown mcp tool (default-deny)', () => {
const r = decide({ toolName: 'mcp__unknown__doSomething', toolInput: {} });
expect(r.block).toBe(true);
expect(r.reason).toMatch(/not in gate-config classification/);
});
});
// 7.3 (Блок 4.3) — скан ИСХОДЯЩЕГО (egress): даже разрешённый классификатором MCP-вызов не
// должен выносить наружу секреты (общий secret-scan) или использовать exfil-схемы
// (data:base64 / file:// / IP-литерал URL) или раздутый payload. fail-close на egress.
import { scanEgress } from './enforce-mcp-classification.mjs';
describe('scanEgress (7.3): скан исходящего MCP-payload на exfil', () => {
it('секрет в аргументах (AWS) → block', () => {
const r = scanEgress({ key: 'AKIAIOSFODNN7EXAMPLE' });
expect(r.block).toBe(true);
expect(r.reason).toMatch(/секрет/i);
});
it('data:base64 URI → block (exfil-вектор)', () => {
expect(scanEgress({ url: 'data:text/plain;base64,SGVsbG8=' }).block).toBe(true);
});
it('file:// схема → block', () => {
expect(scanEgress({ src: 'file:///etc/passwd' }).block).toBe(true);
});
it('IP-литерал URL → block (обход DNS/allowlist)', () => {
expect(scanEgress({ url: 'http://203.0.113.5/collect' }).block).toBe(true);
});
it('раздутый payload → block', () => {
expect(scanEgress({ blob: 'x'.repeat(50) }, { maxBytes: 20 }).block).toBe(true);
});
it('чистые аргументы → не block', () => {
expect(scanEgress({ key: 'user:42:profile' }).block).toBe(false);
});
});
describe('decide 7.3: egress-скан даже на разрешённом классификатором MCP-вызове', () => {
it('read_only mcp + секрет в аргументах → block (egress)', () => {
const r = decide({ toolName: 'mcp__redis__get', toolInput: { key: 'ghp_0123456789abcdefghijklmnopqrstuvwxyz' } });
expect(r.block).toBe(true);
expect(r.reason).toMatch(/egress/i);
});
it('read_only mcp + чистые аргументы → allow', () => {
expect(decide({ toolName: 'mcp__redis__get', toolInput: { key: 'session:abc' } }).block).toBe(false);
});
});