Files
metona-ai-desktop/electron/harness/agent-loop/__tests__/memory-md-gate.test.ts
T
thzxx 4cd6e997b5
CI / 类型检查 + Lint + 单元测试 (push) Failing after 9m45s
CI / 全量测试 (Electron ABI) (push) Failing after 6m28s
CI / 产物编译验证 (push) Successful in 11m18s
feat: v0.8.2 安全纵深补全 · 协议保真 · 断链修复 — 图片SSRF/根MEMORY.md保护根治 · Anthropic thinking回传+pause_turn续传 · 2523 用例全量回归 + E2E 扩充
2026-09-08 14:30:27 +08:00

103 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* v0.8.2 P0-2: 根 MEMORY.md 保护闸门(工具无关的路径形参数匹配)
*
* 锁定契约:
* - delete_file / file_movesource_path / destination_path)等旧名单遗漏的工具
* 对根 MEMORY.md 的操作被拦截(读/写/删/移动/改名任一方向)
* - 子目录 MEMORY.md 不受保护(H-5 语义保持)
* - 相对路径以 workspacePath 为基解析
* - MCP 工具(任意带路径形参数的工具)同样纳入
* - 非根 MEMORY.md 的路径不误伤
*/
import { describe, it, expect, vi } from 'vitest';
import { tmpdir } from 'os';
import { join } from 'path';
vi.mock('electron-log', () => ({
default: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
}));
import { AgentLoopEngine } from '../engine';
const WORKSPACE = join(tmpdir(), 'metona-memory-gate-test');
const ROOT_MEMORY = join(WORKSPACE, 'MEMORY.md');
function makeEngine(): AgentLoopEngine {
const engine = new AgentLoopEngine({}, {
providerId: 'fake',
supportedModels: [],
supportsToolCalling: true,
supportsThinking: false,
send: vi.fn(),
sendStream: vi.fn(),
} as never);
engine.setWorkspacePath(WORKSPACE);
return engine;
}
/** 白盒调用(私有方法契约测试) */
function gate(engine: AgentLoopEngine, args: Record<string, unknown>): boolean {
return (
engine as unknown as {
isTargetingRootMemoryMd: (tc: { args: Record<string, unknown> }) => boolean;
}
).isTargetingRootMemoryMd({ args });
}
describe('根 MEMORY.md 保护闸门(P0-2', () => {
it('delete_file / file_move(旧名单遗漏工具)→ 拦截', () => {
const engine = makeEngine();
expect(gate(engine, { file_path: ROOT_MEMORY })).toBe(true);
expect(gate(engine, { source_path: ROOT_MEMORY, destination_path: join(WORKSPACE, 'x') })).toBe(
true,
);
expect(
gate(engine, { source_path: join(WORKSPACE, 'note.md'), destination_path: ROOT_MEMORY }),
).toBe(true);
});
it('路径参数别名矩阵:path / filePath / destination / dir_path 均命中', () => {
const engine = makeEngine();
expect(gate(engine, { path: ROOT_MEMORY })).toBe(true);
expect(gate(engine, { filePath: ROOT_MEMORY })).toBe(true);
expect(gate(engine, { destination: ROOT_MEMORY })).toBe(true);
expect(gate(engine, { dir_path: ROOT_MEMORY })).toBe(true);
});
it('相对路径以工作空间为基解析 → 拦截', () => {
const engine = makeEngine();
expect(gate(engine, { file_path: 'MEMORY.md' })).toBe(true);
expect(gate(engine, { file_path: './MEMORY.md' })).toBe(true);
});
it('子目录 MEMORY.md 不受保护(H-5 语义)', () => {
const engine = makeEngine();
expect(gate(engine, { file_path: join(WORKSPACE, 'notes', 'MEMORY.md') })).toBe(false);
});
it('MCP 工具的路径形参数同样纳入(任意工具生效)', () => {
const engine = makeEngine();
expect(gate(engine, { target_path: ROOT_MEMORY, options: { recursive: true } })).toBe(true);
});
it('其他文件路径不误伤', () => {
const engine = makeEngine();
expect(gate(engine, { file_path: join(WORKSPACE, 'src', 'main.ts') })).toBe(false);
expect(gate(engine, { file_path: ROOT_MEMORY + '.bak' })).toBe(false);
expect(gate(engine, { command: 'echo hello' })).toBe(false);
});
it('未设置工作空间时闸门放行(无根可保护)', () => {
const engine = new AgentLoopEngine({}, {
providerId: 'fake',
supportedModels: [],
supportsToolCalling: true,
supportsThinking: false,
send: vi.fn(),
sendStream: vi.fn(),
} as never);
expect(gate(engine, { file_path: ROOT_MEMORY })).toBe(false);
});
});