5e32375f0e
架构变更: - 采用 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 - 配置管理
157 lines
4.9 KiB
TypeScript
157 lines
4.9 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { matchPattern, matchRules, parseCommand, generateAskPattern } from '../../../src/permission/wildcard.js';
|
|
|
|
describe('matchPattern - 通配符模式匹配', () => {
|
|
describe('* 通配符', () => {
|
|
it('匹配任意字符', () => {
|
|
expect(matchPattern('git diff', 'git diff*')).toBe(true);
|
|
expect(matchPattern('git diff --staged', 'git diff*')).toBe(true);
|
|
expect(matchPattern('git diff HEAD~1', 'git diff*')).toBe(true);
|
|
});
|
|
|
|
it('不匹配不同前缀', () => {
|
|
expect(matchPattern('git status', 'git diff*')).toBe(false);
|
|
expect(matchPattern('git pull', 'git diff*')).toBe(false);
|
|
});
|
|
|
|
it('匹配危险命令模式 rm -rf*', () => {
|
|
expect(matchPattern('rm -rf /', 'rm -rf*')).toBe(true);
|
|
expect(matchPattern('rm -rf /home', 'rm -rf*')).toBe(true);
|
|
expect(matchPattern('rm -rf .', 'rm -rf*')).toBe(true);
|
|
});
|
|
|
|
it('rm 普通命令不匹配 rm -rf*', () => {
|
|
expect(matchPattern('rm file.txt', 'rm -rf*')).toBe(false);
|
|
expect(matchPattern('rm -r dir', 'rm -rf*')).toBe(false);
|
|
});
|
|
|
|
it('中间位置的通配符', () => {
|
|
expect(matchPattern('git push origin main', 'git push * main')).toBe(true);
|
|
expect(matchPattern('git push upstream main', 'git push * main')).toBe(true);
|
|
});
|
|
|
|
it('多个通配符', () => {
|
|
expect(matchPattern('git push origin main', 'git * origin *')).toBe(true);
|
|
expect(matchPattern('git pull origin main', 'git * origin *')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('? 通配符', () => {
|
|
it('匹配单个字符', () => {
|
|
expect(matchPattern('ls -a', 'ls -?')).toBe(true);
|
|
expect(matchPattern('ls -l', 'ls -?')).toBe(true);
|
|
});
|
|
|
|
it('不匹配多个字符', () => {
|
|
expect(matchPattern('ls -la', 'ls -?')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('精确匹配', () => {
|
|
it('完全相同的字符串', () => {
|
|
expect(matchPattern('ls', 'ls')).toBe(true);
|
|
expect(matchPattern('pwd', 'pwd')).toBe(true);
|
|
});
|
|
|
|
it('不匹配带参数的命令', () => {
|
|
expect(matchPattern('ls -la', 'ls')).toBe(false);
|
|
expect(matchPattern('pwd /home', 'pwd')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('大小写不敏感', () => {
|
|
it('匹配不同大小写', () => {
|
|
expect(matchPattern('GIT DIFF', 'git diff*')).toBe(true);
|
|
expect(matchPattern('Git Diff', 'git diff*')).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('特殊字符转义', () => {
|
|
it('正确处理点号', () => {
|
|
expect(matchPattern('file.txt', 'file.txt')).toBe(true);
|
|
expect(matchPattern('fileatxt', 'file.txt')).toBe(false);
|
|
});
|
|
|
|
it('正确处理括号', () => {
|
|
expect(matchPattern('echo (test)', 'echo (test)')).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('matchRules - 规则匹配', () => {
|
|
const rules = [
|
|
{ pattern: 'ls *', action: 'allow' as const },
|
|
{ pattern: 'rm -rf*', action: 'deny' as const },
|
|
{ pattern: 'git *', action: 'ask' as const },
|
|
];
|
|
|
|
it('匹配 allow 规则', () => {
|
|
const result = matchRules('ls -la', rules, 'ask');
|
|
expect(result.action).toBe('allow');
|
|
});
|
|
|
|
it('匹配 deny 规则', () => {
|
|
const result = matchRules('rm -rf /', rules, 'ask');
|
|
expect(result.action).toBe('deny');
|
|
});
|
|
|
|
it('匹配 ask 规则', () => {
|
|
const result = matchRules('git push', rules, 'deny');
|
|
expect(result.action).toBe('ask');
|
|
});
|
|
|
|
it('无匹配时返回默认动作', () => {
|
|
const result = matchRules('npm install', rules, 'ask');
|
|
expect(result.action).toBe('ask');
|
|
});
|
|
|
|
it('返回匹配的模式', () => {
|
|
const result = matchRules('rm -rf /home', rules, 'ask');
|
|
expect(result.matchedPattern).toBe('rm -rf*');
|
|
});
|
|
|
|
it('空规则列表返回默认动作', () => {
|
|
const result = matchRules('any command', [], 'allow');
|
|
expect(result.action).toBe('allow');
|
|
});
|
|
});
|
|
|
|
describe('parseCommand - 命令解析', () => {
|
|
it('解析简单命令', () => {
|
|
const result = parseCommand('ls');
|
|
expect(result.head).toBe('ls');
|
|
expect(result.sub).toBeUndefined();
|
|
});
|
|
|
|
it('解析带子命令的命令', () => {
|
|
const result = parseCommand('git push');
|
|
expect(result.head).toBe('git');
|
|
expect(result.sub).toBe('push');
|
|
});
|
|
|
|
it('解析带参数的命令', () => {
|
|
const result = parseCommand('git push origin main');
|
|
expect(result.head).toBe('git');
|
|
expect(result.sub).toBe('push');
|
|
expect(result.args).toContain('origin');
|
|
expect(result.args).toContain('main');
|
|
});
|
|
});
|
|
|
|
describe('generateAskPattern - 生成询问模式', () => {
|
|
it('简单命令生成 cmd *', () => {
|
|
const pattern = generateAskPattern('ls -la');
|
|
expect(pattern).toBe('ls *');
|
|
});
|
|
|
|
it('带子命令生成 cmd sub *', () => {
|
|
const pattern = generateAskPattern('git push origin');
|
|
expect(pattern).toBe('git push *');
|
|
});
|
|
|
|
it('npm install 生成 npm install *', () => {
|
|
const pattern = generateAskPattern('npm install lodash');
|
|
expect(pattern).toBe('npm install *');
|
|
});
|
|
});
|