Files
ai-terminal-assistant/packages/core/tests/unit/agent/permission-merger.test.ts
T
kurihada 5e32375f0e feat: 重构为 Monorepo 架构并实现 HTTP Server
架构变更:
- 采用 pnpm workspaces 实现 Monorepo 结构
- 将现有代码迁移到 packages/core
- 新增 packages/server HTTP 服务层

Server 功能:
- REST API: 会话管理、工具管理、配置管理
- WebSocket: 实时双向通信支持
- SSE: 服务端事件推送
- Hono + Bun 作为运行时

API 端点:
- GET/POST /api/sessions - 会话 CRUD
- GET/POST /api/sessions/:id/messages - 消息管理
- GET /api/sessions/:id/events - SSE 事件流
- WS /api/ws/:sessionId - WebSocket 连接
- GET/POST /api/tools - 工具管理
- GET/PUT /api/config - 配置管理
2025-12-12 10:42:20 +08:00

235 lines
7.8 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
mergePermissions,
matchRule,
checkBashPermission,
checkFilePathPermission,
SYSTEM_DEFAULT_PERMISSION,
} from '../../../src/agent/permission-merger.js';
import type { AgentPermission, AgentBashPermission } from '../../../src/agent/types.js';
describe('matchRule - 命令规则匹配', () => {
describe('通配符 * 匹配', () => {
it('git diff* 匹配 git diff --staged', () => {
expect(matchRule('git diff --staged', 'git diff*')).toBe(true);
});
it('git diff* 不匹配 git status', () => {
expect(matchRule('git status', 'git diff*')).toBe(false);
});
it('rm -rf* 匹配危险命令', () => {
expect(matchRule('rm -rf /', 'rm -rf*')).toBe(true);
expect(matchRule('rm -rf /home/user', 'rm -rf*')).toBe(true);
});
it('rm -rf* 不匹配普通 rm', () => {
expect(matchRule('rm file.txt', 'rm -rf*')).toBe(false);
});
});
describe('精确匹配', () => {
it('pwd 精确匹配', () => {
expect(matchRule('pwd', 'pwd')).toBe(true);
});
it('pwd 不匹配带参数', () => {
expect(matchRule('pwd /home', 'pwd')).toBe(false);
});
});
describe('大小写不敏感', () => {
it('忽略大小写', () => {
expect(matchRule('GIT DIFF', 'git diff*')).toBe(true);
});
});
});
describe('checkBashPermission - Bash 权限检查', () => {
it('禁用时返回 deny', () => {
const permission: AgentBashPermission = { enabled: false };
expect(checkBashPermission('ls', permission)).toBe('deny');
});
it('匹配 allow 规则', () => {
const permission: AgentBashPermission = {
enabled: true,
rules: [{ pattern: 'ls *', action: 'allow' }],
default: 'deny',
};
expect(checkBashPermission('ls -la', permission)).toBe('allow');
});
it('匹配 deny 规则', () => {
const permission: AgentBashPermission = {
enabled: true,
rules: [{ pattern: 'rm -rf*', action: 'deny' }],
default: 'allow',
};
expect(checkBashPermission('rm -rf /', permission)).toBe('deny');
});
it('无匹配时返回默认值', () => {
const permission: AgentBashPermission = {
enabled: true,
rules: [],
default: 'ask',
};
expect(checkBashPermission('npm install', permission)).toBe('ask');
});
it('规则优先级:先匹配的规则优先', () => {
const permission: AgentBashPermission = {
enabled: true,
rules: [
{ pattern: 'git push --force*', action: 'deny' },
{ pattern: 'git push*', action: 'ask' },
],
default: 'allow',
};
expect(checkBashPermission('git push --force origin', permission)).toBe('deny');
expect(checkBashPermission('git push origin', permission)).toBe('ask');
});
});
describe('checkFilePathPermission - 文件路径权限检查', () => {
it('无敏感路径规则返回 null', () => {
expect(checkFilePathPermission('/home/user/file.txt', undefined)).toBeNull();
expect(checkFilePathPermission('/home/user/file.txt', [])).toBeNull();
});
it('匹配敏感路径规则', () => {
const rules = [
{ pattern: '*.env', action: 'deny' as const },
{ pattern: '/etc/*', action: 'ask' as const },
];
expect(checkFilePathPermission('.env', rules)).toBe('deny');
expect(checkFilePathPermission('/etc/passwd', rules)).toBe('ask');
});
it('不匹配时返回 null', () => {
const rules = [{ pattern: '*.env', action: 'deny' as const }];
expect(checkFilePathPermission('config.json', rules)).toBeNull();
});
});
describe('mergePermissions - 权限合并', () => {
describe('优先级:Agent > Global > System', () => {
it('Agent 配置覆盖 Global 和 System', () => {
const system: AgentPermission = { file: { read: 'allow', write: 'ask' } };
const global: AgentPermission = { file: { write: 'allow' } };
const agent: AgentPermission = { file: { write: 'deny' } };
const merged = mergePermissions(system, global, agent);
expect(merged.file?.write).toBe('deny');
});
it('Global 配置覆盖 System', () => {
const system: AgentPermission = { file: { write: 'ask' } };
const global: AgentPermission = { file: { write: 'allow' } };
const merged = mergePermissions(system, global, undefined);
expect(merged.file?.write).toBe('allow');
});
it('无覆盖时使用 System 默认值', () => {
const merged = mergePermissions(SYSTEM_DEFAULT_PERMISSION, undefined, undefined);
expect(merged.file?.read).toBe('allow');
expect(merged.file?.write).toBe('ask');
});
});
describe('Bash 权限合并', () => {
it('Agent 禁用 bash 覆盖全局', () => {
const system: AgentPermission = { bash: { enabled: true } };
const global: AgentPermission = { bash: { enabled: true } };
const agent: AgentPermission = { bash: { enabled: false } };
const merged = mergePermissions(system, global, agent);
expect(merged.bash?.enabled).toBe(false);
});
it('Global 禁用 bash 且 Agent 未覆盖', () => {
const system: AgentPermission = { bash: { enabled: true } };
const global: AgentPermission = { bash: { enabled: false } };
const merged = mergePermissions(system, global, undefined);
expect(merged.bash?.enabled).toBe(false);
});
it('规则按优先级合并:Agent > Global > System', () => {
const system: AgentPermission = {
bash: { rules: [{ pattern: 'ls *', action: 'allow' }] },
};
const global: AgentPermission = {
bash: { rules: [{ pattern: 'cat *', action: 'allow' }] },
};
const agent: AgentPermission = {
bash: { rules: [{ pattern: 'rm *', action: 'deny' }] },
};
const merged = mergePermissions(system, global, agent);
// Agent 规则在前
expect(merged.bash?.rules?.[0].pattern).toBe('rm *');
expect(merged.bash?.rules?.[1].pattern).toBe('cat *');
expect(merged.bash?.rules?.[2].pattern).toBe('ls *');
});
});
describe('Git 权限合并', () => {
it('合并所有级别的 Git 权限', () => {
const system: AgentPermission = { git: { read: 'allow', write: 'ask', dangerous: 'deny' } };
const agent: AgentPermission = { git: { write: 'deny' } };
const merged = mergePermissions(system, undefined, agent);
expect(merged.git?.read).toBe('allow'); // 来自 system
expect(merged.git?.write).toBe('deny'); // 来自 agent
expect(merged.git?.dangerous).toBe('deny'); // 来自 system
});
});
describe('Web 权限合并', () => {
it('合并 Web 权限', () => {
const system: AgentPermission = { web: 'ask' };
const agent: AgentPermission = { web: 'deny' };
const merged = mergePermissions(system, undefined, agent);
expect(merged.web).toBe('deny');
});
});
});
describe('SYSTEM_DEFAULT_PERMISSION - 系统默认权限', () => {
it('文件读取默认允许', () => {
expect(SYSTEM_DEFAULT_PERMISSION.file?.read).toBe('allow');
});
it('文件写入默认询问', () => {
expect(SYSTEM_DEFAULT_PERMISSION.file?.write).toBe('ask');
});
it('Bash 默认启用', () => {
expect(SYSTEM_DEFAULT_PERMISSION.bash?.enabled).toBe(true);
});
it('包含安全命令白名单', () => {
const rules = SYSTEM_DEFAULT_PERMISSION.bash?.rules ?? [];
const lsRule = rules.find((r) => r.pattern === 'ls *');
expect(lsRule?.action).toBe('allow');
});
it('包含危险命令黑名单', () => {
const rules = SYSTEM_DEFAULT_PERMISSION.bash?.rules ?? [];
const rmRule = rules.find((r) => r.pattern === 'rm -rf *');
expect(rmRule?.action).toBe('deny');
});
it('Git 读取默认允许', () => {
expect(SYSTEM_DEFAULT_PERMISSION.git?.read).toBe('allow');
});
it('Git 危险操作默认拒绝', () => {
expect(SYSTEM_DEFAULT_PERMISSION.git?.dangerous).toBe('deny');
});
});