This commit is contained in:
Oleg Maslov
2026-09-02 10:14:22 +02:00
parent 0c3e2ead3b
commit b20b138fe4
771 changed files with 161561 additions and 9027 deletions

View File

@@ -0,0 +1,92 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { createWorkerExecutionContext } from '../src/execution-policy.js';
describe('worker execution policy', () => {
let dataDir: string;
beforeEach(() => {
dataDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-worker-policy-'));
vi.stubEnv('WAGGLE_DATA_DIR', dataDir);
});
afterEach(() => {
vi.unstubAllEnvs();
fs.rmSync(dataDir, { recursive: true, force: true });
});
it('derives a canonical tenant workspace from WAGGLE_DATA_DIR and teamId', () => {
const context = createWorkerExecutionContext('team-123');
const expected = fs.realpathSync.native(path.join(dataDir, 'teams', 'team-123', 'files'));
expect(context.workspaceDir).toBe(expected);
expect(fs.statSync(context.workspaceDir).isDirectory()).toBe(true);
});
it('rejects a team directory link that aliases another tenant', () => {
const teamsDir = path.join(dataDir, 'teams');
const otherTeamDir = path.join(teamsDir, 'team-other');
fs.mkdirSync(path.join(otherTeamDir, 'files'), { recursive: true });
fs.symlinkSync(
otherTeamDir,
path.join(teamsDir, 'team-requested'),
process.platform === 'win32' ? 'junction' : 'dir',
);
expect(() => createWorkerExecutionContext('team-requested')).toThrow('link or junction');
});
it('rejects a files directory link that widens the tenant root', () => {
const teamDir = path.join(dataDir, 'teams', 'team-123');
fs.mkdirSync(teamDir, { recursive: true });
fs.symlinkSync(
teamDir,
path.join(teamDir, 'files'),
process.platform === 'win32' ? 'junction' : 'dir',
);
expect(() => createWorkerExecutionContext('team-123')).toThrow('link or junction');
});
it('exposes exactly the read-only system tool pool', () => {
const context = createWorkerExecutionContext('team-123');
expect(context.tools.map(tool => tool.name)).toEqual([
'read_file',
'search_files',
'search_content',
'web_search',
'web_fetch',
]);
});
it('truthfully instructs the model to propose changes instead of mutating', () => {
const context = createWorkerExecutionContext('team-123');
expect(context.systemPrompt).toContain('non-interactive read-only worker');
expect(context.systemPrompt).toContain('cannot run shell commands, execute code, or write, edit, or delete files');
expect(context.systemPrompt).toContain('proposed patch');
});
it('fails closed without WAGGLE_DATA_DIR', () => {
vi.stubEnv('WAGGLE_DATA_DIR', ' ');
expect(() => createWorkerExecutionContext('team-123')).toThrow('WAGGLE_DATA_DIR');
});
it.each([
['empty', ''],
['current directory', '.'],
['parent directory', '..'],
['forward-slash traversal', '../outside'],
['backslash traversal', '..\\outside'],
['absolute Windows path', 'C:\\outside'],
['overlong identifier', 'a'.repeat(129)],
['missing runtime identifier', undefined as unknown as string],
['null runtime identifier', null as unknown as string],
])('rejects an unsafe %s teamId', (_label, teamId) => {
expect(() => createWorkerExecutionContext(teamId)).toThrow('Invalid teamId');
});
});