forkoff 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -4
- package/dist/__tests__/cli-commands.test.d.ts +6 -0
- package/dist/__tests__/cli-commands.test.d.ts.map +1 -0
- package/dist/__tests__/cli-commands.test.js +213 -0
- package/dist/__tests__/cli-commands.test.js.map +1 -0
- package/dist/__tests__/startup.test.d.ts +11 -0
- package/dist/__tests__/startup.test.d.ts.map +1 -0
- package/dist/__tests__/startup.test.js +234 -0
- package/dist/__tests__/startup.test.js.map +1 -0
- package/dist/__tests__/tools/claude-process.test.js +221 -15
- package/dist/__tests__/tools/claude-process.test.js.map +1 -1
- package/dist/__tests__/tools/permission-hook.test.d.ts +17 -0
- package/dist/__tests__/tools/permission-hook.test.d.ts.map +1 -0
- package/dist/__tests__/tools/permission-hook.test.js +616 -0
- package/dist/__tests__/tools/permission-hook.test.js.map +1 -0
- package/dist/__tests__/tools/permission-ipc.test.d.ts +11 -0
- package/dist/__tests__/tools/permission-ipc.test.d.ts.map +1 -0
- package/dist/__tests__/tools/permission-ipc.test.js +612 -0
- package/dist/__tests__/tools/permission-ipc.test.js.map +1 -0
- package/dist/config.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1010 -898
- package/dist/index.js.map +1 -1
- package/dist/startup.d.ts.map +1 -1
- package/dist/startup.js +45 -15
- package/dist/startup.js.map +1 -1
- package/dist/tools/__tests__/claude-sessions.test.d.ts +2 -0
- package/dist/tools/__tests__/claude-sessions.test.d.ts.map +1 -0
- package/dist/tools/__tests__/claude-sessions.test.js +306 -0
- package/dist/tools/__tests__/claude-sessions.test.js.map +1 -0
- package/dist/tools/claude-process.d.ts +81 -4
- package/dist/tools/claude-process.d.ts.map +1 -1
- package/dist/tools/claude-process.js +332 -20
- package/dist/tools/claude-process.js.map +1 -1
- package/dist/tools/claude-sessions.d.ts +5 -0
- package/dist/tools/claude-sessions.d.ts.map +1 -1
- package/dist/tools/claude-sessions.js +16 -2
- package/dist/tools/claude-sessions.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/permission-hook.d.ts +41 -0
- package/dist/tools/permission-hook.d.ts.map +1 -0
- package/dist/tools/permission-hook.js +312 -0
- package/dist/tools/permission-hook.js.map +1 -0
- package/dist/tools/permission-ipc.d.ts +109 -0
- package/dist/tools/permission-ipc.d.ts.map +1 -0
- package/dist/tools/permission-ipc.js +295 -0
- package/dist/tools/permission-ipc.js.map +1 -0
- package/dist/websocket.d.ts +14 -0
- package/dist/websocket.d.ts.map +1 -1
- package/dist/websocket.js +34 -4
- package/dist/websocket.js.map +1 -1
- package/jest.config.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tests for Permission Hook Script (permission-hook.ts)
|
|
4
|
+
*
|
|
5
|
+
* The permission hook is a standalone script spawned by Claude Code as a
|
|
6
|
+
* PreToolUse hook. It reads JSON from stdin, decides whether to auto-approve
|
|
7
|
+
* (safe tools) or request approval via temp files (dangerous tools), and
|
|
8
|
+
* writes a JSON decision to stdout.
|
|
9
|
+
*
|
|
10
|
+
* These tests spawn the real hook script as a child process using ts-node,
|
|
11
|
+
* write JSON to its stdin, and verify stdout output and exit codes.
|
|
12
|
+
*
|
|
13
|
+
* For dangerous-tool tests, we also watch the temp directory for the
|
|
14
|
+
* .request.json file the hook writes, then write a .response.json file
|
|
15
|
+
* to simulate the IPC manager's response.
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const os = __importStar(require("os"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
// Give child processes plenty of time
|
|
56
|
+
jest.setTimeout(30000);
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Constants
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
const HOOK_SCRIPT = path.resolve(__dirname, '../../tools/permission-hook.ts');
|
|
61
|
+
const TEMP_DIR = path.join(os.tmpdir(), 'forkoff-permissions');
|
|
62
|
+
/** All tools the hook considers safe by default (must match DEFAULT_SAFE_TOOLS in the hook). */
|
|
63
|
+
const SAFE_TOOLS = [
|
|
64
|
+
'Read', 'Glob', 'Grep', 'WebSearch', 'WebFetch',
|
|
65
|
+
'TaskCreate', 'TaskUpdate', 'TaskGet', 'TaskList',
|
|
66
|
+
'TaskOutput', 'TaskStop', 'AskUserQuestion', 'Skill',
|
|
67
|
+
'EnterPlanMode', 'ExitPlanMode',
|
|
68
|
+
'mcp__ide__getDiagnostics', 'mcp__ide__executeCode',
|
|
69
|
+
];
|
|
70
|
+
/**
|
|
71
|
+
* Build a standard hook stdin payload.
|
|
72
|
+
*/
|
|
73
|
+
function makeInput(overrides = {}) {
|
|
74
|
+
return {
|
|
75
|
+
session_id: 'test-session-1',
|
|
76
|
+
cwd: '/home/user/project',
|
|
77
|
+
hook_event_name: 'PreToolUse',
|
|
78
|
+
tool_name: 'Read',
|
|
79
|
+
tool_input: { file_path: '/some/file.ts' },
|
|
80
|
+
tool_use_id: 'toolu_test_01',
|
|
81
|
+
...overrides,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Spawn the hook script via ts-node, write input to stdin, and collect
|
|
86
|
+
* stdout/stderr/exitCode.
|
|
87
|
+
*
|
|
88
|
+
* @param stdinData - The string to write to the child's stdin (or null to
|
|
89
|
+
* close stdin immediately with no data).
|
|
90
|
+
*/
|
|
91
|
+
function runHook(stdinData) {
|
|
92
|
+
return new Promise((resolve) => {
|
|
93
|
+
// Use node directly to run ts-node's bin.js (avoids .cmd spawn issues on Windows)
|
|
94
|
+
const projectRoot = path.resolve(__dirname, '../../..');
|
|
95
|
+
const tsNodeBin = path.join(projectRoot, 'node_modules', 'ts-node', 'dist', 'bin.js');
|
|
96
|
+
const child = (0, child_process_1.spawn)(process.execPath, [tsNodeBin, '--transpile-only', HOOK_SCRIPT], {
|
|
97
|
+
cwd: projectRoot,
|
|
98
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
99
|
+
env: { ...process.env },
|
|
100
|
+
});
|
|
101
|
+
let stdout = '';
|
|
102
|
+
let stderr = '';
|
|
103
|
+
child.stdout.on('data', (chunk) => {
|
|
104
|
+
stdout += chunk.toString();
|
|
105
|
+
});
|
|
106
|
+
child.stderr.on('data', (chunk) => {
|
|
107
|
+
stderr += chunk.toString();
|
|
108
|
+
});
|
|
109
|
+
child.on('error', (err) => {
|
|
110
|
+
resolve({ stdout, stderr, exitCode: null, parsed: null });
|
|
111
|
+
});
|
|
112
|
+
child.on('close', (code) => {
|
|
113
|
+
let parsed = null;
|
|
114
|
+
try {
|
|
115
|
+
// The hook's respond() function throws 'unreachable' after writing
|
|
116
|
+
// stdout, which triggers the top-level .catch() handler that writes
|
|
117
|
+
// a second JSON line. We only care about the FIRST JSON line.
|
|
118
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
119
|
+
if (lines.length > 0) {
|
|
120
|
+
parsed = JSON.parse(lines[0]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// not parseable
|
|
125
|
+
}
|
|
126
|
+
resolve({ stdout, stderr, exitCode: code, parsed });
|
|
127
|
+
});
|
|
128
|
+
// Write stdin data and close the stream
|
|
129
|
+
if (stdinData !== null) {
|
|
130
|
+
child.stdin.write(stdinData);
|
|
131
|
+
}
|
|
132
|
+
child.stdin.end();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Watch the temp directory for a new .request.json file that was NOT present
|
|
137
|
+
* in the `excludeFiles` set. This prevents picking up stale files from
|
|
138
|
+
* previous tests.
|
|
139
|
+
*/
|
|
140
|
+
function waitForRequestFile(excludeFiles = new Set(), timeoutMs = 15000, pollMs = 100) {
|
|
141
|
+
return new Promise((resolve, reject) => {
|
|
142
|
+
const start = Date.now();
|
|
143
|
+
const check = () => {
|
|
144
|
+
if (Date.now() - start > timeoutMs) {
|
|
145
|
+
reject(new Error(`No new .request.json file appeared in ${TEMP_DIR} within ${timeoutMs}ms`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
if (fs.existsSync(TEMP_DIR)) {
|
|
150
|
+
const files = fs.readdirSync(TEMP_DIR).filter((f) => f.endsWith('.request.json') && !excludeFiles.has(f));
|
|
151
|
+
if (files.length > 0) {
|
|
152
|
+
const filePath = path.join(TEMP_DIR, files[0]);
|
|
153
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
154
|
+
const content = JSON.parse(raw);
|
|
155
|
+
resolve({ filePath, content });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// retry
|
|
162
|
+
}
|
|
163
|
+
setTimeout(check, pollMs);
|
|
164
|
+
};
|
|
165
|
+
check();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Snapshot the current set of files in the temp directory.
|
|
170
|
+
*/
|
|
171
|
+
function snapshotTempDir() {
|
|
172
|
+
try {
|
|
173
|
+
if (fs.existsSync(TEMP_DIR)) {
|
|
174
|
+
return new Set(fs.readdirSync(TEMP_DIR));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// ignore
|
|
179
|
+
}
|
|
180
|
+
return new Set();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Write a response file for the hook to pick up.
|
|
184
|
+
*/
|
|
185
|
+
function writeResponseFile(promptId, decision, reason = '') {
|
|
186
|
+
const responseFile = path.join(TEMP_DIR, `${promptId}.response.json`);
|
|
187
|
+
const data = { decision };
|
|
188
|
+
if (reason) {
|
|
189
|
+
data.reason = reason;
|
|
190
|
+
}
|
|
191
|
+
fs.writeFileSync(responseFile, JSON.stringify(data), 'utf-8');
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Clean up the temp directory before/after tests.
|
|
195
|
+
*/
|
|
196
|
+
function cleanTempDir() {
|
|
197
|
+
try {
|
|
198
|
+
if (fs.existsSync(TEMP_DIR)) {
|
|
199
|
+
const files = fs.readdirSync(TEMP_DIR);
|
|
200
|
+
for (const file of files) {
|
|
201
|
+
try {
|
|
202
|
+
fs.unlinkSync(path.join(TEMP_DIR, file));
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
// ignore
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
// ignore
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ===========================================================================
|
|
215
|
+
// TEST SUITES
|
|
216
|
+
// ===========================================================================
|
|
217
|
+
describe('Permission Hook Script', () => {
|
|
218
|
+
beforeEach(() => {
|
|
219
|
+
cleanTempDir();
|
|
220
|
+
});
|
|
221
|
+
afterEach(() => {
|
|
222
|
+
cleanTempDir();
|
|
223
|
+
});
|
|
224
|
+
// =========================================================================
|
|
225
|
+
// 1. Safe tool (Read) -> auto-approved, exit code 0
|
|
226
|
+
// =========================================================================
|
|
227
|
+
describe('safe tool auto-approval', () => {
|
|
228
|
+
it('auto-approves Read tool with exit code 0', async () => {
|
|
229
|
+
const input = makeInput({ tool_name: 'Read' });
|
|
230
|
+
const result = await runHook(JSON.stringify(input));
|
|
231
|
+
expect(result.exitCode).toBe(0);
|
|
232
|
+
expect(result.parsed).not.toBeNull();
|
|
233
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
234
|
+
expect(result.parsed.hookSpecificOutput.permissionDecisionReason).toContain('Auto-approved');
|
|
235
|
+
expect(result.parsed.hookSpecificOutput.hookEventName).toBe('PreToolUse');
|
|
236
|
+
});
|
|
237
|
+
// =========================================================================
|
|
238
|
+
// 2. Safe tool (Glob) -> same
|
|
239
|
+
// =========================================================================
|
|
240
|
+
it('auto-approves Glob tool with exit code 0', async () => {
|
|
241
|
+
const input = makeInput({ tool_name: 'Glob', tool_input: { pattern: '**/*.ts' } });
|
|
242
|
+
const result = await runHook(JSON.stringify(input));
|
|
243
|
+
expect(result.exitCode).toBe(0);
|
|
244
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
245
|
+
});
|
|
246
|
+
it('auto-approves Grep tool', async () => {
|
|
247
|
+
const input = makeInput({ tool_name: 'Grep', tool_input: { pattern: 'TODO' } });
|
|
248
|
+
const result = await runHook(JSON.stringify(input));
|
|
249
|
+
expect(result.exitCode).toBe(0);
|
|
250
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
251
|
+
});
|
|
252
|
+
it('auto-approves WebSearch tool', async () => {
|
|
253
|
+
const input = makeInput({ tool_name: 'WebSearch', tool_input: { query: 'test' } });
|
|
254
|
+
const result = await runHook(JSON.stringify(input));
|
|
255
|
+
expect(result.exitCode).toBe(0);
|
|
256
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
257
|
+
});
|
|
258
|
+
it('auto-approves TaskCreate tool', async () => {
|
|
259
|
+
const input = makeInput({ tool_name: 'TaskCreate' });
|
|
260
|
+
const result = await runHook(JSON.stringify(input));
|
|
261
|
+
expect(result.exitCode).toBe(0);
|
|
262
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
263
|
+
});
|
|
264
|
+
it('auto-approves mcp__ide__getDiagnostics tool', async () => {
|
|
265
|
+
const input = makeInput({ tool_name: 'mcp__ide__getDiagnostics' });
|
|
266
|
+
const result = await runHook(JSON.stringify(input));
|
|
267
|
+
expect(result.exitCode).toBe(0);
|
|
268
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
269
|
+
});
|
|
270
|
+
it('treats NotebookEdit as dangerous by default (requires approval)', async () => {
|
|
271
|
+
const input = makeInput({ tool_name: 'NotebookEdit' });
|
|
272
|
+
const existing = snapshotTempDir();
|
|
273
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
274
|
+
const { content } = await waitForRequestFile(existing);
|
|
275
|
+
expect(content.toolName).toBe('NotebookEdit');
|
|
276
|
+
writeResponseFile(content.promptId, 'allow');
|
|
277
|
+
const result = await hookPromise;
|
|
278
|
+
expect(result.exitCode).toBe(0);
|
|
279
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
280
|
+
});
|
|
281
|
+
it('does NOT write any temp files for safe tools', async () => {
|
|
282
|
+
const existingBefore = snapshotTempDir();
|
|
283
|
+
const input = makeInput({ tool_name: 'Read' });
|
|
284
|
+
await runHook(JSON.stringify(input));
|
|
285
|
+
// Check that no NEW request files were created
|
|
286
|
+
if (fs.existsSync(TEMP_DIR)) {
|
|
287
|
+
const newFiles = fs.readdirSync(TEMP_DIR).filter((f) => f.endsWith('.request.json') && !existingBefore.has(f));
|
|
288
|
+
expect(newFiles).toHaveLength(0);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
// =========================================================================
|
|
293
|
+
// 3. Dangerous tool (Bash) -> writes request file, allow response -> exit 0
|
|
294
|
+
// =========================================================================
|
|
295
|
+
describe('dangerous tool with allow response', () => {
|
|
296
|
+
it('Bash tool: writes request file and exits 0 when allowed', async () => {
|
|
297
|
+
const input = makeInput({
|
|
298
|
+
tool_name: 'Bash',
|
|
299
|
+
tool_input: { command: 'npm install' },
|
|
300
|
+
tool_use_id: 'toolu_bash_01',
|
|
301
|
+
});
|
|
302
|
+
const existing = snapshotTempDir();
|
|
303
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
304
|
+
const { content } = await waitForRequestFile(existing);
|
|
305
|
+
expect(content.toolName).toBe('Bash');
|
|
306
|
+
expect(content.toolInput).toEqual({ command: 'npm install' });
|
|
307
|
+
expect(content.toolUseId).toBe('toolu_bash_01');
|
|
308
|
+
expect(content.promptId).toBeDefined();
|
|
309
|
+
writeResponseFile(content.promptId, 'allow', 'User approved via mobile');
|
|
310
|
+
const result = await hookPromise;
|
|
311
|
+
expect(result.exitCode).toBe(0);
|
|
312
|
+
expect(result.parsed).not.toBeNull();
|
|
313
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
314
|
+
expect(result.parsed.hookSpecificOutput.hookEventName).toBe('PreToolUse');
|
|
315
|
+
});
|
|
316
|
+
it('Edit tool: writes request file and exits 0 when allowed', async () => {
|
|
317
|
+
const input = makeInput({
|
|
318
|
+
tool_name: 'Edit',
|
|
319
|
+
tool_input: { file_path: '/src/app.ts', old_string: 'a', new_string: 'b' },
|
|
320
|
+
});
|
|
321
|
+
const existing = snapshotTempDir();
|
|
322
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
323
|
+
const { content } = await waitForRequestFile(existing);
|
|
324
|
+
expect(content.toolName).toBe('Edit');
|
|
325
|
+
writeResponseFile(content.promptId, 'allow');
|
|
326
|
+
const result = await hookPromise;
|
|
327
|
+
expect(result.exitCode).toBe(0);
|
|
328
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
// =========================================================================
|
|
332
|
+
// 4. Dangerous tool (Write) -> writes request file, deny response -> exit 2
|
|
333
|
+
// =========================================================================
|
|
334
|
+
describe('dangerous tool with deny response', () => {
|
|
335
|
+
it('Write tool: writes request file and exits 2 when denied', async () => {
|
|
336
|
+
const input = makeInput({
|
|
337
|
+
tool_name: 'Write',
|
|
338
|
+
tool_input: { file_path: '/etc/passwd', content: 'bad stuff' },
|
|
339
|
+
tool_use_id: 'toolu_write_01',
|
|
340
|
+
});
|
|
341
|
+
const existing = snapshotTempDir();
|
|
342
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
343
|
+
const { content } = await waitForRequestFile(existing);
|
|
344
|
+
expect(content.toolName).toBe('Write');
|
|
345
|
+
expect(content.toolInput).toEqual({ file_path: '/etc/passwd', content: 'bad stuff' });
|
|
346
|
+
writeResponseFile(content.promptId, 'deny', 'Too risky');
|
|
347
|
+
const result = await hookPromise;
|
|
348
|
+
expect(result.exitCode).toBe(2);
|
|
349
|
+
expect(result.parsed).not.toBeNull();
|
|
350
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
351
|
+
});
|
|
352
|
+
it('Bash tool: exits 2 when denied', async () => {
|
|
353
|
+
const input = makeInput({
|
|
354
|
+
tool_name: 'Bash',
|
|
355
|
+
tool_input: { command: 'rm -rf /' },
|
|
356
|
+
});
|
|
357
|
+
const existing = snapshotTempDir();
|
|
358
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
359
|
+
const { content } = await waitForRequestFile(existing);
|
|
360
|
+
writeResponseFile(content.promptId, 'deny', 'Absolutely not');
|
|
361
|
+
const result = await hookPromise;
|
|
362
|
+
expect(result.exitCode).toBe(2);
|
|
363
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
// =========================================================================
|
|
367
|
+
// 5. Empty stdin -> deny
|
|
368
|
+
// =========================================================================
|
|
369
|
+
describe('empty stdin', () => {
|
|
370
|
+
it('denies with exit code 2 when stdin is empty', async () => {
|
|
371
|
+
const result = await runHook('');
|
|
372
|
+
expect(result.exitCode).toBe(2);
|
|
373
|
+
expect(result.parsed).not.toBeNull();
|
|
374
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
375
|
+
expect(result.parsed.hookSpecificOutput.permissionDecisionReason).toContain('No input');
|
|
376
|
+
});
|
|
377
|
+
it('denies with exit code 2 when stdin is null (closed immediately)', async () => {
|
|
378
|
+
const result = await runHook(null);
|
|
379
|
+
expect(result.exitCode).toBe(2);
|
|
380
|
+
expect(result.parsed).not.toBeNull();
|
|
381
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
// =========================================================================
|
|
385
|
+
// 6. Invalid JSON stdin -> deny
|
|
386
|
+
// =========================================================================
|
|
387
|
+
describe('invalid JSON stdin', () => {
|
|
388
|
+
it('denies with exit code 2 when stdin is not valid JSON', async () => {
|
|
389
|
+
const result = await runHook('this is not json {{{');
|
|
390
|
+
expect(result.exitCode).toBe(2);
|
|
391
|
+
expect(result.parsed).not.toBeNull();
|
|
392
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
393
|
+
expect(result.parsed.hookSpecificOutput.permissionDecisionReason).toContain('parse');
|
|
394
|
+
});
|
|
395
|
+
it('denies with exit code 2 for truncated JSON', async () => {
|
|
396
|
+
const result = await runHook('{"tool_name": "Bash", "tool_input"');
|
|
397
|
+
expect(result.exitCode).toBe(2);
|
|
398
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
// =========================================================================
|
|
402
|
+
// Output format validation
|
|
403
|
+
// =========================================================================
|
|
404
|
+
describe('output format', () => {
|
|
405
|
+
it('stdout is valid JSON with correct structure', async () => {
|
|
406
|
+
const input = makeInput({ tool_name: 'Read' });
|
|
407
|
+
const result = await runHook(JSON.stringify(input));
|
|
408
|
+
// Must parse as JSON
|
|
409
|
+
expect(result.parsed).not.toBeNull();
|
|
410
|
+
// Must have the required nested structure
|
|
411
|
+
const output = result.parsed;
|
|
412
|
+
expect(output).toHaveProperty('hookSpecificOutput');
|
|
413
|
+
expect(output.hookSpecificOutput).toHaveProperty('hookEventName');
|
|
414
|
+
expect(output.hookSpecificOutput).toHaveProperty('permissionDecision');
|
|
415
|
+
expect(output.hookSpecificOutput).toHaveProperty('permissionDecisionReason');
|
|
416
|
+
});
|
|
417
|
+
it('hookEventName is always PreToolUse', async () => {
|
|
418
|
+
const input = makeInput({ tool_name: 'Glob' });
|
|
419
|
+
const result = await runHook(JSON.stringify(input));
|
|
420
|
+
expect(result.parsed.hookSpecificOutput.hookEventName).toBe('PreToolUse');
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
// =========================================================================
|
|
424
|
+
// Request file content validation (for dangerous tools)
|
|
425
|
+
// =========================================================================
|
|
426
|
+
describe('request file content', () => {
|
|
427
|
+
it('request file contains promptId, toolName, toolInput, toolUseId, and timestamp', async () => {
|
|
428
|
+
const input = makeInput({
|
|
429
|
+
tool_name: 'Bash',
|
|
430
|
+
tool_input: { command: 'echo hello' },
|
|
431
|
+
tool_use_id: 'toolu_verify_fields',
|
|
432
|
+
});
|
|
433
|
+
const existing = snapshotTempDir();
|
|
434
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
435
|
+
const { content } = await waitForRequestFile(existing);
|
|
436
|
+
expect(content).toHaveProperty('promptId');
|
|
437
|
+
expect(typeof content.promptId).toBe('string');
|
|
438
|
+
expect(content.promptId.length).toBeGreaterThan(0);
|
|
439
|
+
expect(content.toolName).toBe('Bash');
|
|
440
|
+
expect(content.toolInput).toEqual({ command: 'echo hello' });
|
|
441
|
+
expect(content.toolUseId).toBe('toolu_verify_fields');
|
|
442
|
+
expect(content).toHaveProperty('timestamp');
|
|
443
|
+
expect(typeof content.timestamp).toBe('number');
|
|
444
|
+
writeResponseFile(content.promptId, 'allow');
|
|
445
|
+
await hookPromise;
|
|
446
|
+
});
|
|
447
|
+
it('request file is written to forkoff-permissions under os.tmpdir()', async () => {
|
|
448
|
+
const input = makeInput({ tool_name: 'Bash' });
|
|
449
|
+
const existing = snapshotTempDir();
|
|
450
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
451
|
+
const { filePath, content } = await waitForRequestFile(existing);
|
|
452
|
+
expect(path.dirname(filePath)).toBe(TEMP_DIR);
|
|
453
|
+
const fileName = path.basename(filePath);
|
|
454
|
+
expect(fileName).toMatch(/^.+\.request\.json$/);
|
|
455
|
+
writeResponseFile(content.promptId, 'allow');
|
|
456
|
+
await hookPromise;
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
// =========================================================================
|
|
460
|
+
// Temp file cleanup by hook
|
|
461
|
+
// =========================================================================
|
|
462
|
+
describe('temp file cleanup', () => {
|
|
463
|
+
it('hook cleans up request and response files after processing', async () => {
|
|
464
|
+
const input = makeInput({ tool_name: 'Bash' });
|
|
465
|
+
const existing = snapshotTempDir();
|
|
466
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
467
|
+
const { content } = await waitForRequestFile(existing);
|
|
468
|
+
writeResponseFile(content.promptId, 'allow');
|
|
469
|
+
await hookPromise;
|
|
470
|
+
// After the hook exits, both files should be cleaned up
|
|
471
|
+
const requestFile = path.join(TEMP_DIR, `${content.promptId}.request.json`);
|
|
472
|
+
const responseFile = path.join(TEMP_DIR, `${content.promptId}.response.json`);
|
|
473
|
+
expect(fs.existsSync(requestFile)).toBe(false);
|
|
474
|
+
expect(fs.existsSync(responseFile)).toBe(false);
|
|
475
|
+
});
|
|
476
|
+
});
|
|
477
|
+
// =========================================================================
|
|
478
|
+
// Unknown/new tools default to dangerous
|
|
479
|
+
// =========================================================================
|
|
480
|
+
describe('unknown tools', () => {
|
|
481
|
+
it('treats unknown tool names as dangerous (writes request file)', async () => {
|
|
482
|
+
const input = makeInput({
|
|
483
|
+
tool_name: 'SomeNewDangerousTool',
|
|
484
|
+
tool_input: { arg: 'value' },
|
|
485
|
+
});
|
|
486
|
+
const existing = snapshotTempDir();
|
|
487
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
488
|
+
const { content } = await waitForRequestFile(existing);
|
|
489
|
+
expect(content.toolName).toBe('SomeNewDangerousTool');
|
|
490
|
+
writeResponseFile(content.promptId, 'deny', 'Unknown tool denied');
|
|
491
|
+
const result = await hookPromise;
|
|
492
|
+
expect(result.exitCode).toBe(2);
|
|
493
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
// =========================================================================
|
|
497
|
+
// Dynamic rules loading (loadRules / matchesAnyPattern)
|
|
498
|
+
// =========================================================================
|
|
499
|
+
describe('dynamic rules', () => {
|
|
500
|
+
const RULES_FILE = path.join(TEMP_DIR, 'rules.json');
|
|
501
|
+
afterEach(() => {
|
|
502
|
+
// Clean up rules file
|
|
503
|
+
try {
|
|
504
|
+
if (fs.existsSync(RULES_FILE))
|
|
505
|
+
fs.unlinkSync(RULES_FILE);
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
508
|
+
// ignore
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
it('uses default safe tools when no rules file exists', async () => {
|
|
512
|
+
// Ensure rules file does not exist
|
|
513
|
+
try {
|
|
514
|
+
fs.unlinkSync(RULES_FILE);
|
|
515
|
+
}
|
|
516
|
+
catch { /* ok */ }
|
|
517
|
+
const input = makeInput({ tool_name: 'Read' });
|
|
518
|
+
const result = await runHook(JSON.stringify(input));
|
|
519
|
+
expect(result.exitCode).toBe(0);
|
|
520
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
521
|
+
});
|
|
522
|
+
it('auto-approves Write when rules file marks it as allow', async () => {
|
|
523
|
+
// Write rules that allow Write
|
|
524
|
+
if (!fs.existsSync(TEMP_DIR))
|
|
525
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
526
|
+
const rules = [
|
|
527
|
+
{ tool: 'Read', action: 'allow' },
|
|
528
|
+
{ tool: 'Write', action: 'allow' },
|
|
529
|
+
{ tool: 'Bash', action: 'ask' },
|
|
530
|
+
];
|
|
531
|
+
fs.writeFileSync(RULES_FILE, JSON.stringify(rules), 'utf-8');
|
|
532
|
+
const input = makeInput({
|
|
533
|
+
tool_name: 'Write',
|
|
534
|
+
tool_input: { file_path: '/test.txt', content: 'hello' },
|
|
535
|
+
});
|
|
536
|
+
const result = await runHook(JSON.stringify(input));
|
|
537
|
+
expect(result.exitCode).toBe(0);
|
|
538
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
539
|
+
});
|
|
540
|
+
it('requires approval for Read when rules file marks it as ask', async () => {
|
|
541
|
+
// Write rules that require approval for Read
|
|
542
|
+
if (!fs.existsSync(TEMP_DIR))
|
|
543
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
544
|
+
const rules = [
|
|
545
|
+
{ tool: 'Read', action: 'ask' },
|
|
546
|
+
{ tool: 'Bash', action: 'ask' },
|
|
547
|
+
];
|
|
548
|
+
fs.writeFileSync(RULES_FILE, JSON.stringify(rules), 'utf-8');
|
|
549
|
+
const input = makeInput({ tool_name: 'Read' });
|
|
550
|
+
const existing = snapshotTempDir();
|
|
551
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
552
|
+
const { content } = await waitForRequestFile(existing);
|
|
553
|
+
expect(content.toolName).toBe('Read');
|
|
554
|
+
writeResponseFile(content.promptId, 'allow');
|
|
555
|
+
const result = await hookPromise;
|
|
556
|
+
expect(result.exitCode).toBe(0);
|
|
557
|
+
});
|
|
558
|
+
it('auto-approves Bash command when it matches a pattern', async () => {
|
|
559
|
+
// Write rules: Bash is allowed with patterns
|
|
560
|
+
if (!fs.existsSync(TEMP_DIR))
|
|
561
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
562
|
+
const rules = [
|
|
563
|
+
{ tool: 'Bash', action: 'allow', patterns: ['npm *', 'git status'] },
|
|
564
|
+
{ tool: 'Read', action: 'allow' },
|
|
565
|
+
];
|
|
566
|
+
fs.writeFileSync(RULES_FILE, JSON.stringify(rules), 'utf-8');
|
|
567
|
+
const input = makeInput({
|
|
568
|
+
tool_name: 'Bash',
|
|
569
|
+
tool_input: { command: 'npm test' },
|
|
570
|
+
});
|
|
571
|
+
const result = await runHook(JSON.stringify(input));
|
|
572
|
+
expect(result.exitCode).toBe(0);
|
|
573
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
574
|
+
expect(result.parsed.hookSpecificOutput.permissionDecisionReason).toContain('pattern');
|
|
575
|
+
});
|
|
576
|
+
it('requires approval for Bash command that does NOT match any pattern', async () => {
|
|
577
|
+
// Write rules: Bash is allowed but only for specific patterns
|
|
578
|
+
if (!fs.existsSync(TEMP_DIR))
|
|
579
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
580
|
+
const rules = [
|
|
581
|
+
{ tool: 'Bash', action: 'allow', patterns: ['npm *', 'git status'] },
|
|
582
|
+
{ tool: 'Read', action: 'allow' },
|
|
583
|
+
];
|
|
584
|
+
fs.writeFileSync(RULES_FILE, JSON.stringify(rules), 'utf-8');
|
|
585
|
+
const input = makeInput({
|
|
586
|
+
tool_name: 'Bash',
|
|
587
|
+
tool_input: { command: 'rm -rf /' },
|
|
588
|
+
});
|
|
589
|
+
const existing = snapshotTempDir();
|
|
590
|
+
const hookPromise = runHook(JSON.stringify(input));
|
|
591
|
+
const { content } = await waitForRequestFile(existing);
|
|
592
|
+
expect(content.toolName).toBe('Bash');
|
|
593
|
+
writeResponseFile(content.promptId, 'deny', 'Not a safe command');
|
|
594
|
+
const result = await hookPromise;
|
|
595
|
+
expect(result.exitCode).toBe(2);
|
|
596
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('deny');
|
|
597
|
+
});
|
|
598
|
+
it('falls back to defaults when rules file has invalid JSON', async () => {
|
|
599
|
+
if (!fs.existsSync(TEMP_DIR))
|
|
600
|
+
fs.mkdirSync(TEMP_DIR, { recursive: true });
|
|
601
|
+
fs.writeFileSync(RULES_FILE, 'not valid json!!!', 'utf-8');
|
|
602
|
+
const input = makeInput({ tool_name: 'Read' });
|
|
603
|
+
const result = await runHook(JSON.stringify(input));
|
|
604
|
+
// Read is in defaults, so should be auto-approved
|
|
605
|
+
expect(result.exitCode).toBe(0);
|
|
606
|
+
expect(result.parsed.hookSpecificOutput.permissionDecision).toBe('allow');
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
// ===========================================================================
|
|
611
|
+
// Unit tests for exported helpers (loadRules, matchesAnyPattern)
|
|
612
|
+
// These use integration-style tests via the hook subprocess since the module
|
|
613
|
+
// runs main() on import. loadRules is tested via rules.json file + subprocess,
|
|
614
|
+
// and matchesAnyPattern is tested via Bash patterns + subprocess.
|
|
615
|
+
// ===========================================================================
|
|
616
|
+
//# sourceMappingURL=permission-hook.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-hook.test.js","sourceRoot":"","sources":["../../../src/__tests__/tools/permission-hook.test.ts"],"names":[],"mappings":"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"}
|