@gotgenes/pi-permission-system 15.0.1 → 16.0.0
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 +30 -0
- package/README.md +11 -0
- package/package.json +1 -1
- package/src/async-cache.ts +21 -0
- package/src/config-loader.ts +35 -0
- package/src/decision-audit.ts +75 -0
- package/src/handlers/gates/bash-command.ts +35 -3
- package/src/handlers/gates/bash-program.ts +5 -6
- package/src/handlers/lifecycle.ts +4 -0
- package/src/handlers/permission-gate-handler.ts +4 -7
- package/src/handlers/tool-call-boundary.ts +91 -0
- package/src/index.ts +13 -1
- package/test/async-cache.test.ts +48 -0
- package/test/config-loader.test.ts +22 -1
- package/test/decision-audit.test.ts +72 -0
- package/test/detect-permissive-bash-fallback.test.ts +56 -0
- package/test/handlers/external-directory-integration.test.ts +24 -20
- package/test/handlers/external-directory-session-dedup.test.ts +4 -4
- package/test/handlers/gates/bash-command-metamorphic.test.ts +83 -0
- package/test/handlers/gates/bash-command.test.ts +33 -6
- package/test/handlers/lifecycle.test.ts +9 -0
- package/test/handlers/tool-call-boundary.test.ts +145 -0
- package/test/handlers/tool-call.test.ts +18 -18
|
@@ -62,7 +62,7 @@ describe("handleToolCall", () => {
|
|
|
62
62
|
makeCtx(),
|
|
63
63
|
);
|
|
64
64
|
expect(result).toEqual({
|
|
65
|
-
|
|
65
|
+
action: "block",
|
|
66
66
|
reason: expect.stringContaining("tool"),
|
|
67
67
|
});
|
|
68
68
|
});
|
|
@@ -73,7 +73,7 @@ describe("handleToolCall", () => {
|
|
|
73
73
|
makeToolCallEvent("unknown-tool"),
|
|
74
74
|
makeCtx(),
|
|
75
75
|
);
|
|
76
|
-
expect(result).toMatchObject({
|
|
76
|
+
expect(result).toMatchObject({ action: "block" });
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
it("returns empty object when tool is allowed", async () => {
|
|
@@ -82,7 +82,7 @@ describe("handleToolCall", () => {
|
|
|
82
82
|
makeToolCallEvent("read"),
|
|
83
83
|
makeCtx(),
|
|
84
84
|
);
|
|
85
|
-
expect(result).toEqual({});
|
|
85
|
+
expect(result).toEqual({ action: "allow" });
|
|
86
86
|
});
|
|
87
87
|
|
|
88
88
|
it("blocks when tool is denied by policy", async () => {
|
|
@@ -97,7 +97,7 @@ describe("handleToolCall", () => {
|
|
|
97
97
|
makeToolCallEvent("read"),
|
|
98
98
|
makeCtx(),
|
|
99
99
|
);
|
|
100
|
-
expect(result).toMatchObject({
|
|
100
|
+
expect(result).toMatchObject({ action: "block" });
|
|
101
101
|
});
|
|
102
102
|
});
|
|
103
103
|
|
|
@@ -128,7 +128,7 @@ describe("handleToolCall — skill-read gate", () => {
|
|
|
128
128
|
input: { path: "/skills/librarian/SKILL.md" },
|
|
129
129
|
};
|
|
130
130
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
131
|
-
expect(result).toMatchObject({
|
|
131
|
+
expect(result).toMatchObject({ action: "block" });
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
it("allows a read of a non-skill path even when skill entries are present", async () => {
|
|
@@ -155,7 +155,7 @@ describe("handleToolCall — skill-read gate", () => {
|
|
|
155
155
|
input: { path: "/test/project/src/index.ts" },
|
|
156
156
|
};
|
|
157
157
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
158
|
-
expect(result).toEqual({});
|
|
158
|
+
expect(result).toEqual({ action: "allow" });
|
|
159
159
|
});
|
|
160
160
|
});
|
|
161
161
|
|
|
@@ -175,7 +175,7 @@ describe("handleToolCall — external-directory gate", () => {
|
|
|
175
175
|
input: { path: "/outside/project/file.ts" },
|
|
176
176
|
});
|
|
177
177
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
178
|
-
expect(result).toMatchObject({
|
|
178
|
+
expect(result).toMatchObject({ action: "block" });
|
|
179
179
|
});
|
|
180
180
|
});
|
|
181
181
|
|
|
@@ -195,7 +195,7 @@ describe("handleToolCall — bash external-directory gate", () => {
|
|
|
195
195
|
input: { command: "cat /outside/project/file.ts" },
|
|
196
196
|
});
|
|
197
197
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
198
|
-
expect(result).toMatchObject({
|
|
198
|
+
expect(result).toMatchObject({ action: "block" });
|
|
199
199
|
});
|
|
200
200
|
});
|
|
201
201
|
|
|
@@ -213,7 +213,7 @@ describe("handleToolCall — path gate (tools)", () => {
|
|
|
213
213
|
});
|
|
214
214
|
const event = makeToolCallEvent("read", { input: { path: ".env" } });
|
|
215
215
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
216
|
-
expect(result).toMatchObject({
|
|
216
|
+
expect(result).toMatchObject({ action: "block" });
|
|
217
217
|
});
|
|
218
218
|
|
|
219
219
|
it("allows a read when path surface allows", async () => {
|
|
@@ -222,7 +222,7 @@ describe("handleToolCall — path gate (tools)", () => {
|
|
|
222
222
|
input: { path: "src/index.ts" },
|
|
223
223
|
});
|
|
224
224
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
225
|
-
expect(result).toEqual({});
|
|
225
|
+
expect(result).toEqual({ action: "allow" });
|
|
226
226
|
});
|
|
227
227
|
});
|
|
228
228
|
|
|
@@ -240,7 +240,7 @@ describe("handleToolCall — bash path gate", () => {
|
|
|
240
240
|
});
|
|
241
241
|
const event = makeToolCallEvent("bash", { input: { command: "cat .env" } });
|
|
242
242
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
243
|
-
expect(result).toMatchObject({
|
|
243
|
+
expect(result).toMatchObject({ action: "block" });
|
|
244
244
|
});
|
|
245
245
|
});
|
|
246
246
|
|
|
@@ -262,7 +262,7 @@ describe("handleToolCall — bash command chain gate", () => {
|
|
|
262
262
|
input: { command: "echo start && npm install compromised-package" },
|
|
263
263
|
});
|
|
264
264
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
265
|
-
expect(result).toMatchObject({
|
|
265
|
+
expect(result).toMatchObject({ action: "block" });
|
|
266
266
|
});
|
|
267
267
|
|
|
268
268
|
it("blocks a command nested inside command substitution (#306)", async () => {
|
|
@@ -280,14 +280,14 @@ describe("handleToolCall — bash command chain gate", () => {
|
|
|
280
280
|
input: { command: "echo $(rm -rf foo)" },
|
|
281
281
|
});
|
|
282
282
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
283
|
-
expect(result).toMatchObject({
|
|
283
|
+
expect(result).toMatchObject({ action: "block" });
|
|
284
284
|
});
|
|
285
285
|
|
|
286
286
|
it("allows a single non-chained bash command", async () => {
|
|
287
287
|
const { handler } = makeHandler({ tools: ["bash"] });
|
|
288
288
|
const event = makeToolCallEvent("bash", { input: { command: "echo hi" } });
|
|
289
289
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
290
|
-
expect(result).toEqual({});
|
|
290
|
+
expect(result).toEqual({ action: "allow" });
|
|
291
291
|
});
|
|
292
292
|
});
|
|
293
293
|
|
|
@@ -302,7 +302,7 @@ describe("handleToolCall — bash external-directory policy states", () => {
|
|
|
302
302
|
input: { command: "cat src/index.ts" },
|
|
303
303
|
});
|
|
304
304
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
305
|
-
expect(result).toEqual({});
|
|
305
|
+
expect(result).toEqual({ action: "allow" });
|
|
306
306
|
});
|
|
307
307
|
|
|
308
308
|
it("blocks bash command with external path when external_directory is ask and no UI", async () => {
|
|
@@ -325,7 +325,7 @@ describe("handleToolCall — bash external-directory policy states", () => {
|
|
|
325
325
|
event,
|
|
326
326
|
makeCtx({ hasUI: false }),
|
|
327
327
|
);
|
|
328
|
-
expect(result).toMatchObject({
|
|
328
|
+
expect(result).toMatchObject({ action: "block" });
|
|
329
329
|
expect(String((result as { reason?: unknown }).reason)).toMatch(
|
|
330
330
|
/no interactive UI/i,
|
|
331
331
|
);
|
|
@@ -344,7 +344,7 @@ describe("handleToolCall — bash external-directory policy states", () => {
|
|
|
344
344
|
input: { command: "cat /etc/hosts" },
|
|
345
345
|
});
|
|
346
346
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
347
|
-
expect(result).toEqual({});
|
|
347
|
+
expect(result).toEqual({ action: "allow" });
|
|
348
348
|
});
|
|
349
349
|
|
|
350
350
|
it("applies bash pattern deny after external_directory allow", async () => {
|
|
@@ -364,7 +364,7 @@ describe("handleToolCall — bash external-directory policy states", () => {
|
|
|
364
364
|
input: { command: "cat /etc/hosts" },
|
|
365
365
|
});
|
|
366
366
|
const result = await handler.handleToolCall(event, makeCtx());
|
|
367
|
-
expect(result).toMatchObject({
|
|
367
|
+
expect(result).toMatchObject({ action: "block" });
|
|
368
368
|
});
|
|
369
369
|
});
|
|
370
370
|
|