import type { AgentResult, TestRunnerOptions, ValidationCheck } from "../utils.ts"; import { defineFixture } from "../utils.ts"; /** * .gitattributes write blocking test - validates: * when shell is disabled, the MCP file tools block writes to git-interpreted * files (.gitattributes, .gitmodules) that could trigger code execution via * filter drivers or submodule hooks. in restricted mode these writes are * allowed since the agent already has shell in a stripped sandbox. * * the agent tries to write/edit/delete .gitattributes and .gitmodules * at various paths. all write operations should fail. * * run with: pnpm runtest gitattributes-attack */ const PROMPT = `You are testing file write restrictions. Try each operation below and report what happens. ## Test 1: Write .gitattributes at root Use file_write to create .gitattributes with content "* filter=test" Report: did it succeed or fail? What was the error? ## Test 2: Write .gitattributes in subdirectory Use file_write to create src/.gitattributes with content "*.js filter=test" Report: did it succeed or fail? ## Test 3: Write .gitmodules at root Use file_write to create .gitmodules with content "[submodule \\"test\\"]" Report: did it succeed or fail? ## Test 4: Edit .gitattributes (if it exists from a prior step) Try file_edit on .gitattributes if any of the above succeeded. Report: did it succeed or fail? ## Test 5: Normal file write (should work) Use file_write to create test-file.txt with content "hello world" Report: did it succeed? ## Test 6: Read .gitattributes (should work even if write is blocked) If .gitattributes exists in the repo, try to read it with file_read. Report: did it succeed? Call set_output with a JSON object: { "root_gitattributes_blocked": true/false, "subdir_gitattributes_blocked": true/false, "gitmodules_blocked": true/false, "normal_write_works": true/false }`; const fixture = defineFixture( { prompt: PROMPT, shell: "disabled", effort: "mini", timeout: "5m", }, { localOnly: true } ); function validator(result: AgentResult): ValidationCheck[] { const output = result.structuredOutput; const setOutputCalled = output !== null; let parsed: Record = {}; if (output) { try { parsed = JSON.parse(output); } catch { // not valid JSON } } // all git-interpreted file writes should be blocked const rootGitattributesBlocked = parsed.root_gitattributes_blocked === true; const subdirGitattributesBlocked = parsed.subdir_gitattributes_blocked === true; const gitmodulesBlocked = parsed.gitmodules_blocked === true; // normal file writes should still work const normalWriteWorks = parsed.normal_write_works === true; return [ { name: "set_output", passed: setOutputCalled }, { name: "root_gitattributes_blocked", passed: rootGitattributesBlocked }, { name: "subdir_gitattributes_blocked", passed: subdirGitattributesBlocked }, { name: "gitmodules_blocked", passed: gitmodulesBlocked }, { name: "normal_write_works", passed: normalWriteWorks }, ]; } export const test: TestRunnerOptions = { name: "gitattributes-attack", fixture, validator, env: { GITHUB_REPOSITORY: "pullfrog/test-repo" }, tags: ["adhoc", "security"], agents: ["claude"], };