@gotgenes/pi-permission-system 5.5.0 → 5.5.1
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/CHANGELOG.md +9 -0
- package/package.json +1 -1
- package/src/handlers/before-agent-start.ts +7 -7
- package/src/handlers/gates/bash-external-directory.ts +22 -24
- package/src/handlers/gates/external-directory.ts +32 -41
- package/src/handlers/gates/skill-read.ts +10 -12
- package/src/handlers/gates/tool.ts +20 -27
- package/src/handlers/gates/types.ts +75 -0
- package/src/handlers/input.ts +3 -3
- package/src/handlers/lifecycle.ts +21 -21
- package/src/handlers/tool-call.ts +77 -7
- package/src/handlers/types.ts +20 -7
- package/src/index.ts +6 -1
- package/src/runtime.ts +17 -9
- package/tests/handlers/before-agent-start.test.ts +17 -27
- package/tests/handlers/gates/bash-external-directory.test.ts +48 -105
- package/tests/handlers/gates/external-directory.test.ts +65 -140
- package/tests/handlers/gates/skill-read.test.ts +50 -65
- package/tests/handlers/gates/tool.test.ts +90 -334
- package/tests/handlers/input-events.test.ts +10 -21
- package/tests/handlers/input.test.ts +26 -43
- package/tests/handlers/lifecycle.test.ts +47 -66
- package/tests/handlers/tool-call-events.test.ts +29 -40
- package/tests/handlers/tool-call.test.ts +19 -30
|
@@ -1,25 +1,19 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
|
|
3
3
|
import { evaluateToolGate } from "../../../src/handlers/gates/tool";
|
|
4
|
-
import type {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import type {
|
|
5
|
+
ToolCallContext,
|
|
6
|
+
ToolGateDeps,
|
|
7
|
+
} from "../../../src/handlers/gates/types";
|
|
7
8
|
import type { PermissionCheckResult } from "../../../src/types";
|
|
8
9
|
|
|
9
|
-
// ── SDK stubs ──────────────────────────────────────────────────────────────
|
|
10
|
-
vi.mock("@mariozechner/pi-coding-agent", async (importOriginal) => {
|
|
11
|
-
const original =
|
|
12
|
-
await importOriginal<typeof import("@mariozechner/pi-coding-agent")>();
|
|
13
|
-
return { ...original };
|
|
14
|
-
});
|
|
15
|
-
|
|
16
10
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
17
11
|
|
|
18
12
|
function makeTcc(overrides: Partial<ToolCallContext> = {}): ToolCallContext {
|
|
19
13
|
return {
|
|
20
14
|
toolName: "read",
|
|
21
15
|
agentName: null,
|
|
22
|
-
input: {
|
|
16
|
+
input: {},
|
|
23
17
|
toolCallId: "tc-1",
|
|
24
18
|
cwd: "/test/project",
|
|
25
19
|
...overrides,
|
|
@@ -35,383 +29,145 @@ function makeCheckResult(
|
|
|
35
29
|
toolName: "read",
|
|
36
30
|
source: "tool",
|
|
37
31
|
origin: "builtin",
|
|
32
|
+
matchedPattern: "*",
|
|
38
33
|
...overrides,
|
|
39
34
|
};
|
|
40
35
|
}
|
|
41
36
|
|
|
42
|
-
function
|
|
43
|
-
return { emit: vi.fn(), on: vi.fn().mockReturnValue(() => undefined) };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function makeRuntime(
|
|
47
|
-
overrides: Record<string, unknown> = {},
|
|
48
|
-
): HandlerDeps["runtime"] {
|
|
37
|
+
function makeToolGateDeps(overrides: Partial<ToolGateDeps> = {}): ToolGateDeps {
|
|
49
38
|
return {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("allow")),
|
|
54
|
-
},
|
|
55
|
-
sessionRules: {
|
|
56
|
-
approve: vi.fn(),
|
|
57
|
-
getRuleset: vi.fn().mockReturnValue([]),
|
|
58
|
-
clear: vi.fn(),
|
|
59
|
-
},
|
|
39
|
+
checkPermission: vi.fn().mockReturnValue(makeCheckResult("allow")),
|
|
40
|
+
getSessionRuleset: vi.fn().mockReturnValue([]),
|
|
41
|
+
approveSessionRule: vi.fn(),
|
|
60
42
|
writeReviewLog: vi.fn(),
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function makeDeps(overrides: Record<string, unknown> = {}): HandlerDeps {
|
|
66
|
-
const { runtime: runtimeOverrides, events, ...rest } = overrides;
|
|
67
|
-
return {
|
|
68
|
-
runtime: makeRuntime(runtimeOverrides as Record<string, unknown>),
|
|
69
|
-
events: events ?? makeEvents(),
|
|
70
|
-
canRequestPermissionConfirmation: vi.fn().mockReturnValue(true),
|
|
43
|
+
emitDecision: vi.fn(),
|
|
44
|
+
canConfirm: vi.fn().mockReturnValue(true),
|
|
71
45
|
promptPermission: vi
|
|
72
46
|
.fn()
|
|
73
47
|
.mockResolvedValue({ approved: true, state: "approved" }),
|
|
74
|
-
...
|
|
75
|
-
}
|
|
48
|
+
...overrides,
|
|
49
|
+
};
|
|
76
50
|
}
|
|
77
51
|
|
|
78
52
|
// ── tests ──────────────────────────────────────────────────────────────────
|
|
79
53
|
|
|
80
54
|
describe("evaluateToolGate", () => {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const events = makeEvents();
|
|
85
|
-
const deps = makeDeps({
|
|
86
|
-
runtime: {
|
|
87
|
-
permissionManager: {
|
|
88
|
-
checkPermission: vi.fn().mockReturnValue(
|
|
89
|
-
makeCheckResult("allow", {
|
|
90
|
-
source: "session",
|
|
91
|
-
toolName: "bash",
|
|
92
|
-
command: "git status",
|
|
93
|
-
matchedPattern: "git *",
|
|
94
|
-
}),
|
|
95
|
-
),
|
|
96
|
-
},
|
|
97
|
-
},
|
|
98
|
-
events,
|
|
99
|
-
});
|
|
100
|
-
const tcc = makeTcc({
|
|
101
|
-
toolName: "bash",
|
|
102
|
-
input: { command: "git status" },
|
|
103
|
-
});
|
|
104
|
-
const result = await evaluateToolGate(tcc, deps);
|
|
55
|
+
it("allows when policy is allow", async () => {
|
|
56
|
+
const deps = makeToolGateDeps();
|
|
57
|
+
const result = await evaluateToolGate(makeTcc(), deps);
|
|
105
58
|
expect(result).toEqual({ action: "allow" });
|
|
106
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
107
|
-
"permissions:decision",
|
|
108
|
-
expect.objectContaining({
|
|
109
|
-
surface: "bash",
|
|
110
|
-
value: "git status",
|
|
111
|
-
result: "allow",
|
|
112
|
-
resolution: "session_approved",
|
|
113
|
-
matchedPattern: "git *",
|
|
114
|
-
}),
|
|
115
|
-
);
|
|
116
59
|
});
|
|
117
60
|
|
|
118
|
-
it("
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
getRuleset: vi.fn().mockReturnValue([]),
|
|
122
|
-
clear: vi.fn(),
|
|
123
|
-
};
|
|
124
|
-
const deps = makeDeps({
|
|
125
|
-
runtime: {
|
|
126
|
-
permissionManager: {
|
|
127
|
-
checkPermission: vi.fn().mockReturnValue(
|
|
128
|
-
makeCheckResult("allow", {
|
|
129
|
-
source: "session",
|
|
130
|
-
matchedPattern: "git *",
|
|
131
|
-
}),
|
|
132
|
-
),
|
|
133
|
-
},
|
|
134
|
-
sessionRules,
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
const tcc = makeTcc({
|
|
138
|
-
toolName: "bash",
|
|
139
|
-
input: { command: "git status" },
|
|
61
|
+
it("blocks when policy is deny", async () => {
|
|
62
|
+
const deps = makeToolGateDeps({
|
|
63
|
+
checkPermission: vi.fn().mockReturnValue(makeCheckResult("deny")),
|
|
140
64
|
});
|
|
141
|
-
await evaluateToolGate(
|
|
142
|
-
expect(
|
|
65
|
+
const result = await evaluateToolGate(makeTcc(), deps);
|
|
66
|
+
expect(result).toMatchObject({ action: "block" });
|
|
143
67
|
});
|
|
144
68
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
69
|
+
it("allows on session-approved fast path", async () => {
|
|
70
|
+
const deps = makeToolGateDeps({
|
|
71
|
+
checkPermission: vi.fn().mockReturnValue(
|
|
72
|
+
makeCheckResult("allow", {
|
|
73
|
+
source: "session",
|
|
74
|
+
matchedPattern: "git *",
|
|
75
|
+
}),
|
|
76
|
+
),
|
|
77
|
+
});
|
|
78
|
+
const result = await evaluateToolGate(
|
|
79
|
+
makeTcc({ toolName: "bash", input: { command: "git status" } }),
|
|
80
|
+
deps,
|
|
81
|
+
);
|
|
152
82
|
expect(result).toEqual({ action: "allow" });
|
|
153
|
-
expect(
|
|
154
|
-
"
|
|
155
|
-
expect.objectContaining({
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}),
|
|
83
|
+
expect(deps.writeReviewLog).toHaveBeenCalledWith(
|
|
84
|
+
"permission_request.session_approved",
|
|
85
|
+
expect.objectContaining({ resolution: "session_approved" }),
|
|
86
|
+
);
|
|
87
|
+
expect(deps.emitDecision).toHaveBeenCalledWith(
|
|
88
|
+
expect.objectContaining({ resolution: "session_approved" }),
|
|
159
89
|
);
|
|
160
90
|
});
|
|
161
91
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const deps = makeDeps({
|
|
167
|
-
runtime: {
|
|
168
|
-
permissionManager: {
|
|
169
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("deny")),
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
events,
|
|
92
|
+
it("blocks when state is ask but canConfirm is false", async () => {
|
|
93
|
+
const deps = makeToolGateDeps({
|
|
94
|
+
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
95
|
+
canConfirm: vi.fn().mockReturnValue(false),
|
|
173
96
|
});
|
|
174
|
-
const
|
|
175
|
-
const result = await evaluateToolGate(tcc, deps);
|
|
97
|
+
const result = await evaluateToolGate(makeTcc(), deps);
|
|
176
98
|
expect(result).toMatchObject({ action: "block" });
|
|
177
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
178
|
-
"permissions:decision",
|
|
179
|
-
expect.objectContaining({
|
|
180
|
-
result: "deny",
|
|
181
|
-
resolution: "policy_deny",
|
|
182
|
-
}),
|
|
183
|
-
);
|
|
184
99
|
});
|
|
185
100
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const events = makeEvents();
|
|
190
|
-
const sessionRules = {
|
|
191
|
-
approve: vi.fn(),
|
|
192
|
-
getRuleset: vi.fn().mockReturnValue([]),
|
|
193
|
-
clear: vi.fn(),
|
|
194
|
-
};
|
|
195
|
-
const deps = makeDeps({
|
|
196
|
-
runtime: {
|
|
197
|
-
permissionManager: {
|
|
198
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
199
|
-
},
|
|
200
|
-
sessionRules,
|
|
201
|
-
},
|
|
202
|
-
events,
|
|
101
|
+
it("allows when state is ask and user approves", async () => {
|
|
102
|
+
const deps = makeToolGateDeps({
|
|
103
|
+
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
203
104
|
promptPermission: vi
|
|
204
105
|
.fn()
|
|
205
106
|
.mockResolvedValue({ approved: true, state: "approved" }),
|
|
206
107
|
});
|
|
207
|
-
const
|
|
208
|
-
const result = await evaluateToolGate(tcc, deps);
|
|
209
|
-
expect(result).toEqual({ action: "allow" });
|
|
210
|
-
expect(sessionRules.approve).not.toHaveBeenCalled();
|
|
211
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
212
|
-
"permissions:decision",
|
|
213
|
-
expect.objectContaining({
|
|
214
|
-
result: "allow",
|
|
215
|
-
resolution: "user_approved",
|
|
216
|
-
}),
|
|
217
|
-
);
|
|
218
|
-
});
|
|
219
|
-
|
|
220
|
-
// ── Policy ask — user approves for session ───────────────────────────────
|
|
221
|
-
|
|
222
|
-
it("records session rule when user approves for session", async () => {
|
|
223
|
-
const events = makeEvents();
|
|
224
|
-
const sessionRules = {
|
|
225
|
-
approve: vi.fn(),
|
|
226
|
-
getRuleset: vi.fn().mockReturnValue([]),
|
|
227
|
-
clear: vi.fn(),
|
|
228
|
-
};
|
|
229
|
-
const deps = makeDeps({
|
|
230
|
-
runtime: {
|
|
231
|
-
permissionManager: {
|
|
232
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
233
|
-
},
|
|
234
|
-
sessionRules,
|
|
235
|
-
},
|
|
236
|
-
events,
|
|
237
|
-
promptPermission: vi
|
|
238
|
-
.fn()
|
|
239
|
-
.mockResolvedValue({ approved: true, state: "approved_for_session" }),
|
|
240
|
-
});
|
|
241
|
-
const tcc = makeTcc();
|
|
242
|
-
const result = await evaluateToolGate(tcc, deps);
|
|
108
|
+
const result = await evaluateToolGate(makeTcc(), deps);
|
|
243
109
|
expect(result).toEqual({ action: "allow" });
|
|
244
|
-
expect(sessionRules.approve).toHaveBeenCalledWith("read", "*");
|
|
245
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
246
|
-
"permissions:decision",
|
|
247
|
-
expect.objectContaining({
|
|
248
|
-
resolution: "user_approved_for_session",
|
|
249
|
-
}),
|
|
250
|
-
);
|
|
251
110
|
});
|
|
252
111
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const events = makeEvents();
|
|
257
|
-
const deps = makeDeps({
|
|
258
|
-
runtime: {
|
|
259
|
-
permissionManager: {
|
|
260
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
events,
|
|
112
|
+
it("blocks when state is ask and user denies", async () => {
|
|
113
|
+
const deps = makeToolGateDeps({
|
|
114
|
+
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
264
115
|
promptPermission: vi
|
|
265
116
|
.fn()
|
|
266
117
|
.mockResolvedValue({ approved: false, state: "denied" }),
|
|
267
118
|
});
|
|
268
|
-
const
|
|
269
|
-
const result = await evaluateToolGate(tcc, deps);
|
|
119
|
+
const result = await evaluateToolGate(makeTcc(), deps);
|
|
270
120
|
expect(result).toMatchObject({ action: "block" });
|
|
271
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
272
|
-
"permissions:decision",
|
|
273
|
-
expect.objectContaining({
|
|
274
|
-
result: "deny",
|
|
275
|
-
resolution: "user_denied",
|
|
276
|
-
}),
|
|
277
|
-
);
|
|
278
121
|
});
|
|
279
122
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
permissionManager: {
|
|
287
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
288
|
-
},
|
|
289
|
-
},
|
|
290
|
-
events,
|
|
291
|
-
canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
|
|
123
|
+
it("approves session rule when user approves for session", async () => {
|
|
124
|
+
const deps = makeToolGateDeps({
|
|
125
|
+
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
126
|
+
promptPermission: vi
|
|
127
|
+
.fn()
|
|
128
|
+
.mockResolvedValue({ approved: true, state: "approved_for_session" }),
|
|
292
129
|
});
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
expect(result).toMatchObject({ action: "block" });
|
|
296
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
297
|
-
"permissions:decision",
|
|
298
|
-
expect.objectContaining({
|
|
299
|
-
result: "deny",
|
|
300
|
-
resolution: "confirmation_unavailable",
|
|
301
|
-
}),
|
|
302
|
-
);
|
|
130
|
+
await evaluateToolGate(makeTcc(), deps);
|
|
131
|
+
expect(deps.approveSessionRule).toHaveBeenCalled();
|
|
303
132
|
});
|
|
304
133
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
checkPermission: vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
313
|
-
},
|
|
314
|
-
},
|
|
315
|
-
events,
|
|
316
|
-
promptPermission: vi.fn().mockResolvedValue({
|
|
317
|
-
approved: true,
|
|
318
|
-
state: "approved",
|
|
319
|
-
autoApproved: true,
|
|
320
|
-
}),
|
|
134
|
+
it("emits decision event with correct surface and result", async () => {
|
|
135
|
+
const deps = makeToolGateDeps({
|
|
136
|
+
checkPermission: vi
|
|
137
|
+
.fn()
|
|
138
|
+
.mockReturnValue(
|
|
139
|
+
makeCheckResult("allow", { origin: "global", matchedPattern: "*" }),
|
|
140
|
+
),
|
|
321
141
|
});
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
expect(result).toEqual({ action: "allow" });
|
|
325
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
326
|
-
"permissions:decision",
|
|
142
|
+
await evaluateToolGate(makeTcc({ toolName: "write" }), deps);
|
|
143
|
+
expect(deps.emitDecision).toHaveBeenCalledWith(
|
|
327
144
|
expect.objectContaining({
|
|
328
|
-
|
|
145
|
+
surface: "write",
|
|
146
|
+
result: "allow",
|
|
147
|
+
resolution: "policy_allow",
|
|
148
|
+
origin: "global",
|
|
329
149
|
}),
|
|
330
150
|
);
|
|
331
151
|
});
|
|
332
152
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const events = makeEvents();
|
|
337
|
-
const deps = makeDeps({
|
|
338
|
-
runtime: {
|
|
339
|
-
permissionManager: {
|
|
340
|
-
checkPermission: vi.fn().mockReturnValue(
|
|
341
|
-
makeCheckResult("allow", {
|
|
342
|
-
toolName: "bash",
|
|
343
|
-
command: "git status",
|
|
344
|
-
}),
|
|
345
|
-
),
|
|
346
|
-
},
|
|
347
|
-
},
|
|
348
|
-
events,
|
|
349
|
-
});
|
|
350
|
-
const tcc = makeTcc({
|
|
351
|
-
toolName: "bash",
|
|
352
|
-
input: { command: "git status" },
|
|
353
|
-
});
|
|
354
|
-
await evaluateToolGate(tcc, deps);
|
|
355
|
-
expect(events.emit).toHaveBeenCalledWith(
|
|
356
|
-
"permissions:decision",
|
|
357
|
-
expect.objectContaining({
|
|
153
|
+
it("passes session ruleset to checkPermission", async () => {
|
|
154
|
+
const sessionRules = [
|
|
155
|
+
{
|
|
358
156
|
surface: "bash",
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
// ── MCP-specific value ───────────────────────────────────────────────────
|
|
365
|
-
|
|
366
|
-
it("uses target as decision value for mcp tool", async () => {
|
|
367
|
-
const events = makeEvents();
|
|
368
|
-
const deps = makeDeps({
|
|
369
|
-
runtime: {
|
|
370
|
-
permissionManager: {
|
|
371
|
-
checkPermission: vi.fn().mockReturnValue(
|
|
372
|
-
makeCheckResult("allow", {
|
|
373
|
-
toolName: "mcp",
|
|
374
|
-
target: "exa:search",
|
|
375
|
-
}),
|
|
376
|
-
),
|
|
377
|
-
},
|
|
157
|
+
pattern: "git *",
|
|
158
|
+
action: "allow" as const,
|
|
159
|
+
origin: "session" as const,
|
|
378
160
|
},
|
|
379
|
-
|
|
161
|
+
];
|
|
162
|
+
const deps = makeToolGateDeps({
|
|
163
|
+
getSessionRuleset: vi.fn().mockReturnValue(sessionRules),
|
|
380
164
|
});
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
value: "exa:search",
|
|
388
|
-
}),
|
|
165
|
+
await evaluateToolGate(makeTcc({ toolName: "bash" }), deps);
|
|
166
|
+
expect(deps.checkPermission).toHaveBeenCalledWith(
|
|
167
|
+
"bash",
|
|
168
|
+
expect.anything(),
|
|
169
|
+
undefined,
|
|
170
|
+
sessionRules,
|
|
389
171
|
);
|
|
390
172
|
});
|
|
391
|
-
|
|
392
|
-
// ── Bash unavailable message ─────────────────────────────────────────────
|
|
393
|
-
|
|
394
|
-
it("includes command in unavailable message for bash", async () => {
|
|
395
|
-
const deps = makeDeps({
|
|
396
|
-
runtime: {
|
|
397
|
-
permissionManager: {
|
|
398
|
-
checkPermission: vi
|
|
399
|
-
.fn()
|
|
400
|
-
.mockReturnValue(
|
|
401
|
-
makeCheckResult("ask", { toolName: "bash", command: "rm -rf /" }),
|
|
402
|
-
),
|
|
403
|
-
},
|
|
404
|
-
},
|
|
405
|
-
canRequestPermissionConfirmation: vi.fn().mockReturnValue(false),
|
|
406
|
-
});
|
|
407
|
-
const tcc = makeTcc({
|
|
408
|
-
toolName: "bash",
|
|
409
|
-
input: { command: "rm -rf /" },
|
|
410
|
-
});
|
|
411
|
-
const result = await evaluateToolGate(tcc, deps);
|
|
412
|
-
expect(result.action).toBe("block");
|
|
413
|
-
if (result.action === "block") {
|
|
414
|
-
expect(result.reason).toContain("rm -rf /");
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
173
|
});
|
|
@@ -8,7 +8,7 @@ import { handleInput } from "../../src/handlers/input";
|
|
|
8
8
|
import type { HandlerDeps } from "../../src/handlers/types";
|
|
9
9
|
import type { PermissionDecisionEvent } from "../../src/permission-events";
|
|
10
10
|
import { PERMISSIONS_DECISION_CHANNEL } from "../../src/permission-events";
|
|
11
|
-
import type {
|
|
11
|
+
import type { SessionState } from "../../src/runtime";
|
|
12
12
|
|
|
13
13
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
14
14
|
|
|
@@ -38,17 +38,8 @@ function makeCtx(overrides: Partial<ExtensionContext> = {}): ExtensionContext {
|
|
|
38
38
|
} as unknown as ExtensionContext;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function
|
|
42
|
-
state: "allow" | "deny" | "ask" = "allow",
|
|
43
|
-
): ExtensionRuntime {
|
|
41
|
+
function makeSession(state: "allow" | "deny" | "ask" = "allow"): SessionState {
|
|
44
42
|
return {
|
|
45
|
-
agentDir: "/test/agent",
|
|
46
|
-
sessionsDir: "/test/agent/sessions",
|
|
47
|
-
subagentSessionsDir: "/test/agent/subagent-sessions",
|
|
48
|
-
forwardingDir: "/test/agent/sessions/permission-forwarding",
|
|
49
|
-
globalLogsDir: "/test/agent/extensions/pi-permission-system/logs",
|
|
50
|
-
piInfrastructureDirs: ["/test/agent"],
|
|
51
|
-
config: { debugLog: false, permissionReviewLog: true, yoloMode: false },
|
|
52
43
|
runtimeContext: null,
|
|
53
44
|
permissionManager: {
|
|
54
45
|
checkPermission: vi.fn().mockReturnValue({
|
|
@@ -58,23 +49,17 @@ function makeRuntime(
|
|
|
58
49
|
origin: "global",
|
|
59
50
|
matchedPattern: "*",
|
|
60
51
|
}),
|
|
61
|
-
} as unknown as
|
|
52
|
+
} as unknown as SessionState["permissionManager"],
|
|
62
53
|
activeSkillEntries: [],
|
|
63
54
|
lastKnownActiveAgentName: null,
|
|
64
55
|
lastActiveToolsCacheKey: null,
|
|
65
56
|
lastPromptStateCacheKey: null,
|
|
66
|
-
lastConfigWarning: null,
|
|
67
57
|
sessionRules: {
|
|
68
58
|
approve: vi.fn(),
|
|
69
59
|
getRuleset: vi.fn().mockReturnValue([]),
|
|
70
60
|
clear: vi.fn(),
|
|
71
|
-
} as unknown as
|
|
72
|
-
|
|
73
|
-
permissionForwardingTimer: null,
|
|
74
|
-
isProcessingForwardedRequests: false,
|
|
75
|
-
writeDebugLog: vi.fn(),
|
|
76
|
-
writeReviewLog: vi.fn(),
|
|
77
|
-
} as ExtensionRuntime;
|
|
61
|
+
} as unknown as SessionState["sessionRules"],
|
|
62
|
+
};
|
|
78
63
|
}
|
|
79
64
|
|
|
80
65
|
function makeDeps(
|
|
@@ -82,7 +67,11 @@ function makeDeps(
|
|
|
82
67
|
overrides: Partial<HandlerDeps> = {},
|
|
83
68
|
): HandlerDeps {
|
|
84
69
|
return {
|
|
85
|
-
|
|
70
|
+
session: makeSession(state),
|
|
71
|
+
writeDebugLog: vi.fn(),
|
|
72
|
+
writeReviewLog: vi.fn(),
|
|
73
|
+
piInfrastructureDirs: ["/test/agent"],
|
|
74
|
+
getPiInfrastructureReadPaths: vi.fn().mockReturnValue([]),
|
|
86
75
|
events: makeEvents(),
|
|
87
76
|
createPermissionManagerForCwd: vi.fn(),
|
|
88
77
|
refreshExtensionConfig: vi.fn(),
|