@gotgenes/pi-permission-system 9.0.1 → 9.1.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 +13 -0
- package/package.json +1 -1
- package/src/denial-messages.ts +44 -3
- package/src/handlers/gates/bash-command.ts +26 -24
- package/src/handlers/gates/bash-external-directory.ts +9 -9
- package/src/handlers/gates/bash-path.ts +9 -6
- package/src/handlers/gates/bash-program.ts +135 -55
- package/src/handlers/permission-gate-handler.ts +25 -8
- package/src/permission-prompts.ts +7 -4
- package/src/types.ts +15 -0
- package/test/denial-messages.test.ts +16 -0
- package/test/handlers/gates/bash-command.test.ts +62 -24
- package/test/handlers/gates/bash-external-directory.test.ts +40 -14
- package/test/handlers/gates/bash-path.test.ts +41 -14
- package/test/handlers/gates/bash-program.test.ts +95 -23
- package/test/handlers/tool-call.test.ts +38 -0
- package/test/permission-prompts.test.ts +16 -0
|
@@ -98,6 +98,22 @@ describe("formatDenyReason", () => {
|
|
|
98
98
|
);
|
|
99
99
|
});
|
|
100
100
|
|
|
101
|
+
test("bash with nested execution context", () => {
|
|
102
|
+
expect(
|
|
103
|
+
formatDenyReason(
|
|
104
|
+
toolCtx(
|
|
105
|
+
toolCheck("bash", {
|
|
106
|
+
command: "rm -rf foo",
|
|
107
|
+
matchedPattern: "rm *",
|
|
108
|
+
commandContext: "command_substitution",
|
|
109
|
+
}),
|
|
110
|
+
),
|
|
111
|
+
),
|
|
112
|
+
).toBe(
|
|
113
|
+
"[pi-permission-system] is not permitted to run 'bash' command 'rm -rf foo' (matched 'rm *', inside command substitution).",
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
101
117
|
test("MCP source with target on non-mcp toolName", () => {
|
|
102
118
|
expect(
|
|
103
119
|
formatDenyReason(
|
|
@@ -23,18 +23,17 @@ function bashResult(
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
describe("resolveBashCommandCheck", () => {
|
|
26
|
-
it("passes a single command straight through",
|
|
27
|
-
const decompose = vi.fn(async () => ["npm install pkg"]);
|
|
26
|
+
it("passes a single command straight through", () => {
|
|
28
27
|
const checkPermission = vi
|
|
29
28
|
.fn<CheckPermissionFn>()
|
|
30
29
|
.mockReturnValue(bashResult("allow", "npm install pkg", "npm *"));
|
|
31
30
|
|
|
32
|
-
const result =
|
|
31
|
+
const result = resolveBashCommandCheck(
|
|
33
32
|
"npm install pkg",
|
|
33
|
+
[{ text: "npm install pkg" }],
|
|
34
34
|
undefined,
|
|
35
35
|
[],
|
|
36
36
|
checkPermission,
|
|
37
|
-
decompose,
|
|
38
37
|
);
|
|
39
38
|
|
|
40
39
|
expect(result.state).toBe("allow");
|
|
@@ -47,8 +46,7 @@ describe("resolveBashCommandCheck", () => {
|
|
|
47
46
|
);
|
|
48
47
|
});
|
|
49
48
|
|
|
50
|
-
it("denies the chain when any sub-command is denied, reporting that command's pattern",
|
|
51
|
-
const decompose = vi.fn(async () => ["cd /p", "npm install pkg"]);
|
|
49
|
+
it("denies the chain when any sub-command is denied, reporting that command's pattern", () => {
|
|
52
50
|
const checkPermission = vi
|
|
53
51
|
.fn<CheckPermissionFn>()
|
|
54
52
|
.mockImplementation((_surface, input) => {
|
|
@@ -58,12 +56,12 @@ describe("resolveBashCommandCheck", () => {
|
|
|
58
56
|
: bashResult("allow", command, "cd *");
|
|
59
57
|
});
|
|
60
58
|
|
|
61
|
-
const result =
|
|
59
|
+
const result = resolveBashCommandCheck(
|
|
62
60
|
"cd /p && npm install pkg",
|
|
61
|
+
[{ text: "cd /p" }, { text: "npm install pkg" }],
|
|
63
62
|
undefined,
|
|
64
63
|
[],
|
|
65
64
|
checkPermission,
|
|
66
|
-
decompose,
|
|
67
65
|
);
|
|
68
66
|
|
|
69
67
|
expect(result.state).toBe("deny");
|
|
@@ -71,8 +69,7 @@ describe("resolveBashCommandCheck", () => {
|
|
|
71
69
|
expect(result.command).toBe("npm install pkg");
|
|
72
70
|
});
|
|
73
71
|
|
|
74
|
-
it("asks when a sub-command asks and none denies",
|
|
75
|
-
const decompose = vi.fn(async () => ["cd /p", "git push"]);
|
|
72
|
+
it("asks when a sub-command asks and none denies", () => {
|
|
76
73
|
const checkPermission = vi
|
|
77
74
|
.fn<CheckPermissionFn>()
|
|
78
75
|
.mockImplementation((_surface, input) => {
|
|
@@ -82,12 +79,12 @@ describe("resolveBashCommandCheck", () => {
|
|
|
82
79
|
: bashResult("allow", command, "cd *");
|
|
83
80
|
});
|
|
84
81
|
|
|
85
|
-
const result =
|
|
82
|
+
const result = resolveBashCommandCheck(
|
|
86
83
|
"cd /p && git push",
|
|
84
|
+
[{ text: "cd /p" }, { text: "git push" }],
|
|
87
85
|
undefined,
|
|
88
86
|
[],
|
|
89
87
|
checkPermission,
|
|
90
|
-
decompose,
|
|
91
88
|
);
|
|
92
89
|
|
|
93
90
|
expect(result.state).toBe("ask");
|
|
@@ -95,8 +92,7 @@ describe("resolveBashCommandCheck", () => {
|
|
|
95
92
|
expect(result.command).toBe("git push");
|
|
96
93
|
});
|
|
97
94
|
|
|
98
|
-
it("returns the first allow result when every sub-command is allowed",
|
|
99
|
-
const decompose = vi.fn(async () => ["a", "b"]);
|
|
95
|
+
it("returns the first allow result when every sub-command is allowed", () => {
|
|
100
96
|
const checkPermission = vi
|
|
101
97
|
.fn<CheckPermissionFn>()
|
|
102
98
|
.mockImplementation((_surface, input) => {
|
|
@@ -104,33 +100,33 @@ describe("resolveBashCommandCheck", () => {
|
|
|
104
100
|
return bashResult("allow", command, `${command} *`);
|
|
105
101
|
});
|
|
106
102
|
|
|
107
|
-
const result =
|
|
103
|
+
const result = resolveBashCommandCheck(
|
|
108
104
|
"a && b",
|
|
105
|
+
[{ text: "a" }, { text: "b" }],
|
|
109
106
|
undefined,
|
|
110
107
|
[],
|
|
111
108
|
checkPermission,
|
|
112
|
-
decompose,
|
|
113
109
|
);
|
|
114
110
|
|
|
115
111
|
expect(result.state).toBe("allow");
|
|
116
112
|
expect(result.matchedPattern).toBe("a *");
|
|
117
113
|
});
|
|
118
114
|
|
|
119
|
-
it("falls back to the whole command when no top-level commands are found",
|
|
120
|
-
const decompose = vi.fn(async () => []);
|
|
115
|
+
it("falls back to the whole command when no top-level commands are found", () => {
|
|
121
116
|
const checkPermission = vi
|
|
122
117
|
.fn<CheckPermissionFn>()
|
|
123
118
|
.mockReturnValue(bashResult("ask", "( rm x )", "*"));
|
|
124
119
|
|
|
125
|
-
const result =
|
|
120
|
+
const result = resolveBashCommandCheck(
|
|
126
121
|
"( rm x )",
|
|
122
|
+
[],
|
|
127
123
|
undefined,
|
|
128
124
|
[],
|
|
129
125
|
checkPermission,
|
|
130
|
-
decompose,
|
|
131
126
|
);
|
|
132
127
|
|
|
133
128
|
expect(result.state).toBe("ask");
|
|
129
|
+
expect(result.commandContext).toBeUndefined();
|
|
134
130
|
expect(checkPermission).toHaveBeenCalledTimes(1);
|
|
135
131
|
expect(checkPermission).toHaveBeenCalledWith(
|
|
136
132
|
"bash",
|
|
@@ -140,21 +136,20 @@ describe("resolveBashCommandCheck", () => {
|
|
|
140
136
|
);
|
|
141
137
|
});
|
|
142
138
|
|
|
143
|
-
it("forwards the agent name and session rules to each sub-command check",
|
|
139
|
+
it("forwards the agent name and session rules to each sub-command check", () => {
|
|
144
140
|
const sessionRules: Rule[] = [
|
|
145
141
|
{ surface: "bash", pattern: "npm *", action: "allow", origin: "session" },
|
|
146
142
|
];
|
|
147
|
-
const decompose = vi.fn(async () => ["npm i"]);
|
|
148
143
|
const checkPermission = vi
|
|
149
144
|
.fn<CheckPermissionFn>()
|
|
150
145
|
.mockReturnValue(bashResult("allow", "npm i"));
|
|
151
146
|
|
|
152
|
-
|
|
147
|
+
resolveBashCommandCheck(
|
|
153
148
|
"npm i",
|
|
149
|
+
[{ text: "npm i" }],
|
|
154
150
|
"agent-x",
|
|
155
151
|
sessionRules,
|
|
156
152
|
checkPermission,
|
|
157
|
-
decompose,
|
|
158
153
|
);
|
|
159
154
|
|
|
160
155
|
expect(checkPermission).toHaveBeenCalledWith(
|
|
@@ -164,4 +159,47 @@ describe("resolveBashCommandCheck", () => {
|
|
|
164
159
|
sessionRules,
|
|
165
160
|
);
|
|
166
161
|
});
|
|
162
|
+
|
|
163
|
+
it("tags the winning result with the offending command's execution context", () => {
|
|
164
|
+
const checkPermission = vi
|
|
165
|
+
.fn<CheckPermissionFn>()
|
|
166
|
+
.mockImplementation((_surface, input) => {
|
|
167
|
+
const command = (input as { command: string }).command;
|
|
168
|
+
return command.startsWith("rm")
|
|
169
|
+
? bashResult("deny", command, "rm *")
|
|
170
|
+
: bashResult("allow", command, "echo *");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const result = resolveBashCommandCheck(
|
|
174
|
+
"echo $(rm -rf foo)",
|
|
175
|
+
[
|
|
176
|
+
{ text: "echo $(rm -rf foo)" },
|
|
177
|
+
{ text: "rm -rf foo", context: "command_substitution" },
|
|
178
|
+
],
|
|
179
|
+
undefined,
|
|
180
|
+
[],
|
|
181
|
+
checkPermission,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
expect(result.state).toBe("deny");
|
|
185
|
+
expect(result.command).toBe("rm -rf foo");
|
|
186
|
+
expect(result.commandContext).toBe("command_substitution");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("leaves commandContext unset when the winning command is top-level", () => {
|
|
190
|
+
const checkPermission = vi
|
|
191
|
+
.fn<CheckPermissionFn>()
|
|
192
|
+
.mockReturnValue(bashResult("deny", "rm -rf foo", "rm *"));
|
|
193
|
+
|
|
194
|
+
const result = resolveBashCommandCheck(
|
|
195
|
+
"rm -rf foo",
|
|
196
|
+
[{ text: "rm -rf foo" }],
|
|
197
|
+
undefined,
|
|
198
|
+
[],
|
|
199
|
+
checkPermission,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
expect(result.state).toBe("deny");
|
|
203
|
+
expect(result.commandContext).toBeUndefined();
|
|
204
|
+
});
|
|
167
205
|
});
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { getNonEmptyString, toRecord } from "#src/common";
|
|
2
3
|
import { describeBashExternalDirectoryGate } from "#src/handlers/gates/bash-external-directory";
|
|
4
|
+
import { BashProgram } from "#src/handlers/gates/bash-program";
|
|
3
5
|
import type {
|
|
4
6
|
GateBypass,
|
|
5
7
|
GateDescriptor,
|
|
8
|
+
GateResult,
|
|
6
9
|
} from "#src/handlers/gates/descriptor";
|
|
7
10
|
import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
8
11
|
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
@@ -34,11 +37,34 @@ function makeCheckResult(
|
|
|
34
37
|
};
|
|
35
38
|
}
|
|
36
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Mirror the handler's parse-once derivation: parse the bash command into a
|
|
42
|
+
* shared `BashProgram` and inject it, exactly as `permission-gate-handler.ts`
|
|
43
|
+
* does, so the gate is exercised through the production wiring.
|
|
44
|
+
*/
|
|
45
|
+
async function describeGate(
|
|
46
|
+
tcc: ToolCallContext,
|
|
47
|
+
checkPermission: Parameters<typeof describeBashExternalDirectoryGate>[2],
|
|
48
|
+
getSessionRuleset: Parameters<typeof describeBashExternalDirectoryGate>[3],
|
|
49
|
+
): Promise<GateResult> {
|
|
50
|
+
const command = getNonEmptyString(toRecord(tcc.input).command);
|
|
51
|
+
const bashProgram =
|
|
52
|
+
tcc.toolName === "bash" && command
|
|
53
|
+
? await BashProgram.parse(command)
|
|
54
|
+
: null;
|
|
55
|
+
return describeBashExternalDirectoryGate(
|
|
56
|
+
tcc,
|
|
57
|
+
bashProgram,
|
|
58
|
+
checkPermission,
|
|
59
|
+
getSessionRuleset,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
37
63
|
// ── tests ──────────────────────────────────────────────────────────────────
|
|
38
64
|
|
|
39
65
|
describe("describeBashExternalDirectoryGate", () => {
|
|
40
66
|
it("returns null when tool is not bash", async () => {
|
|
41
|
-
const result = await
|
|
67
|
+
const result = await describeGate(
|
|
42
68
|
makeTcc({ toolName: "read" }),
|
|
43
69
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
44
70
|
vi.fn().mockReturnValue([]),
|
|
@@ -47,7 +73,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
47
73
|
});
|
|
48
74
|
|
|
49
75
|
it("returns null when no CWD", async () => {
|
|
50
|
-
const result = await
|
|
76
|
+
const result = await describeGate(
|
|
51
77
|
makeTcc({ cwd: undefined }),
|
|
52
78
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
53
79
|
vi.fn().mockReturnValue([]),
|
|
@@ -56,7 +82,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
56
82
|
});
|
|
57
83
|
|
|
58
84
|
it("returns null when command has no external paths", async () => {
|
|
59
|
-
const result = await
|
|
85
|
+
const result = await describeGate(
|
|
60
86
|
makeTcc({ input: { command: "ls -la" } }),
|
|
61
87
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
62
88
|
vi.fn().mockReturnValue([]),
|
|
@@ -68,7 +94,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
68
94
|
const checkPermission = vi
|
|
69
95
|
.fn()
|
|
70
96
|
.mockReturnValue(makeCheckResult("allow", { source: "session" }));
|
|
71
|
-
const result = await
|
|
97
|
+
const result = await describeGate(
|
|
72
98
|
makeTcc(),
|
|
73
99
|
checkPermission,
|
|
74
100
|
vi.fn().mockReturnValue([]),
|
|
@@ -85,7 +111,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
85
111
|
|
|
86
112
|
it("returns GateDescriptor with multi-pattern sessionApproval for uncovered paths", async () => {
|
|
87
113
|
const checkPermission = vi.fn().mockReturnValue(makeCheckResult("ask"));
|
|
88
|
-
const result = await
|
|
114
|
+
const result = await describeGate(
|
|
89
115
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
90
116
|
checkPermission,
|
|
91
117
|
vi.fn().mockReturnValue([]),
|
|
@@ -110,7 +136,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
110
136
|
return makeCheckResult("ask");
|
|
111
137
|
},
|
|
112
138
|
);
|
|
113
|
-
const result = await
|
|
139
|
+
const result = await describeGate(
|
|
114
140
|
makeTcc(),
|
|
115
141
|
checkPermission,
|
|
116
142
|
vi.fn().mockReturnValue([]),
|
|
@@ -132,7 +158,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
132
158
|
return makeCheckResult("ask");
|
|
133
159
|
},
|
|
134
160
|
);
|
|
135
|
-
const result = await
|
|
161
|
+
const result = await describeGate(
|
|
136
162
|
makeTcc(),
|
|
137
163
|
checkPermission,
|
|
138
164
|
vi.fn().mockReturnValue([]),
|
|
@@ -143,7 +169,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
143
169
|
});
|
|
144
170
|
|
|
145
171
|
it("descriptor surface is 'external_directory'", async () => {
|
|
146
|
-
const result = await
|
|
172
|
+
const result = await describeGate(
|
|
147
173
|
makeTcc(),
|
|
148
174
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
149
175
|
vi.fn().mockReturnValue([]),
|
|
@@ -153,7 +179,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
153
179
|
});
|
|
154
180
|
|
|
155
181
|
it("descriptor decision surface is 'external_directory'", async () => {
|
|
156
|
-
const result = await
|
|
182
|
+
const result = await describeGate(
|
|
157
183
|
makeTcc(),
|
|
158
184
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
159
185
|
vi.fn().mockReturnValue([]),
|
|
@@ -163,7 +189,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
163
189
|
});
|
|
164
190
|
|
|
165
191
|
it("denialContext contains the command and external paths", async () => {
|
|
166
|
-
const result = await
|
|
192
|
+
const result = await describeGate(
|
|
167
193
|
makeTcc({ input: { command: "cat /outside/file.ts" } }),
|
|
168
194
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
169
195
|
vi.fn().mockReturnValue([]),
|
|
@@ -177,7 +203,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
177
203
|
});
|
|
178
204
|
|
|
179
205
|
it("promptDetails includes command and tool_call source", async () => {
|
|
180
|
-
const result = await
|
|
206
|
+
const result = await describeGate(
|
|
181
207
|
makeTcc({ agentName: "agent-1", toolCallId: "tc-5" }),
|
|
182
208
|
vi.fn().mockReturnValue(makeCheckResult("ask")),
|
|
183
209
|
vi.fn().mockReturnValue([]),
|
|
@@ -203,7 +229,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
203
229
|
return makeCheckResult("ask");
|
|
204
230
|
},
|
|
205
231
|
);
|
|
206
|
-
const result = await
|
|
232
|
+
const result = await describeGate(
|
|
207
233
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
208
234
|
checkPermission,
|
|
209
235
|
vi.fn().mockReturnValue([]),
|
|
@@ -227,7 +253,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
227
253
|
return makeCheckResult("ask");
|
|
228
254
|
},
|
|
229
255
|
);
|
|
230
|
-
const result = await
|
|
256
|
+
const result = await describeGate(
|
|
231
257
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
232
258
|
checkPermission,
|
|
233
259
|
vi.fn().mockReturnValue([]),
|
|
@@ -252,7 +278,7 @@ describe("describeBashExternalDirectoryGate", () => {
|
|
|
252
278
|
return makeCheckResult("ask");
|
|
253
279
|
},
|
|
254
280
|
);
|
|
255
|
-
const result = await
|
|
281
|
+
const result = await describeGate(
|
|
256
282
|
makeTcc({ input: { command: "diff /outside/a.ts /outside/b.ts" } }),
|
|
257
283
|
checkPermission,
|
|
258
284
|
vi.fn().mockReturnValue([]),
|
|
@@ -9,12 +9,16 @@ vi.mock("node:os", () => {
|
|
|
9
9
|
};
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
+
import { getNonEmptyString, toRecord } from "#src/common";
|
|
12
13
|
import { describeBashPathGate } from "#src/handlers/gates/bash-path";
|
|
14
|
+
import { BashProgram } from "#src/handlers/gates/bash-program";
|
|
13
15
|
import type {
|
|
14
16
|
GateBypass,
|
|
15
17
|
GateDescriptor,
|
|
18
|
+
GateResult,
|
|
16
19
|
} from "#src/handlers/gates/descriptor";
|
|
17
20
|
import { isGateBypass, isGateDescriptor } from "#src/handlers/gates/descriptor";
|
|
21
|
+
import type { ToolCallContext } from "#src/handlers/gates/types";
|
|
18
22
|
import type { Rule } from "#src/rule";
|
|
19
23
|
import type { PermissionCheckResult } from "#src/types";
|
|
20
24
|
|
|
@@ -34,13 +38,36 @@ type CheckPermissionFn = (
|
|
|
34
38
|
sessionRules?: Rule[],
|
|
35
39
|
) => PermissionCheckResult;
|
|
36
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Mirror the handler's parse-once derivation: parse the bash command into a
|
|
43
|
+
* shared `BashProgram` and inject it, exactly as `permission-gate-handler.ts`
|
|
44
|
+
* does, so the gate is exercised through the production wiring.
|
|
45
|
+
*/
|
|
46
|
+
async function describeGate(
|
|
47
|
+
tcc: ToolCallContext,
|
|
48
|
+
checkPermission: CheckPermissionFn,
|
|
49
|
+
getSessionRuleset: () => Rule[],
|
|
50
|
+
): Promise<GateResult> {
|
|
51
|
+
const command = getNonEmptyString(toRecord(tcc.input).command);
|
|
52
|
+
const bashProgram =
|
|
53
|
+
tcc.toolName === "bash" && command
|
|
54
|
+
? await BashProgram.parse(command)
|
|
55
|
+
: null;
|
|
56
|
+
return describeBashPathGate(
|
|
57
|
+
tcc,
|
|
58
|
+
bashProgram,
|
|
59
|
+
checkPermission,
|
|
60
|
+
getSessionRuleset,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
// ── tests ──────────────────────────────────────────────────────────────────
|
|
38
65
|
|
|
39
66
|
describe("describeBashPathGate", () => {
|
|
40
67
|
it("returns null for non-bash tools", async () => {
|
|
41
68
|
const checkPermission = vi.fn<CheckPermissionFn>();
|
|
42
69
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
43
|
-
const result = await
|
|
70
|
+
const result = await describeGate(
|
|
44
71
|
makeTcc({ toolName: "read", input: { path: ".env" } }),
|
|
45
72
|
checkPermission,
|
|
46
73
|
getSessionRuleset,
|
|
@@ -51,7 +78,7 @@ describe("describeBashPathGate", () => {
|
|
|
51
78
|
it("returns null when no tokens are extracted", async () => {
|
|
52
79
|
const checkPermission = vi.fn<CheckPermissionFn>();
|
|
53
80
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
54
|
-
const result = await
|
|
81
|
+
const result = await describeGate(
|
|
55
82
|
makeTcc({ input: { command: "echo hello" } }),
|
|
56
83
|
checkPermission,
|
|
57
84
|
getSessionRuleset,
|
|
@@ -64,7 +91,7 @@ describe("describeBashPathGate", () => {
|
|
|
64
91
|
.fn<CheckPermissionFn>()
|
|
65
92
|
.mockReturnValue(makeCheckResult({ state: "allow" }));
|
|
66
93
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
67
|
-
const result = await
|
|
94
|
+
const result = await describeGate(
|
|
68
95
|
makeTcc({ input: { command: "cat .env" } }),
|
|
69
96
|
checkPermission,
|
|
70
97
|
getSessionRuleset,
|
|
@@ -80,7 +107,7 @@ describe("describeBashPathGate", () => {
|
|
|
80
107
|
}),
|
|
81
108
|
);
|
|
82
109
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
83
|
-
const result = await
|
|
110
|
+
const result = await describeGate(
|
|
84
111
|
makeTcc({ input: { command: "cat .env" } }),
|
|
85
112
|
checkPermission,
|
|
86
113
|
getSessionRuleset,
|
|
@@ -97,7 +124,7 @@ describe("describeBashPathGate", () => {
|
|
|
97
124
|
.fn<CheckPermissionFn>()
|
|
98
125
|
.mockReturnValue(makeCheckResult({ state: "ask", matchedPattern: "*" }));
|
|
99
126
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
100
|
-
const result = await
|
|
127
|
+
const result = await describeGate(
|
|
101
128
|
makeTcc({ input: { command: "cat .env" } }),
|
|
102
129
|
checkPermission,
|
|
103
130
|
getSessionRuleset,
|
|
@@ -115,7 +142,7 @@ describe("describeBashPathGate", () => {
|
|
|
115
142
|
makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
|
|
116
143
|
);
|
|
117
144
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
118
|
-
const result = (await
|
|
145
|
+
const result = (await describeGate(
|
|
119
146
|
makeTcc({ input: { command: "cat .env" } }),
|
|
120
147
|
checkPermission,
|
|
121
148
|
getSessionRuleset,
|
|
@@ -135,7 +162,7 @@ describe("describeBashPathGate", () => {
|
|
|
135
162
|
makeCheckResult({ state: "deny", matchedPattern: "*.env" }),
|
|
136
163
|
);
|
|
137
164
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
138
|
-
const result = (await
|
|
165
|
+
const result = (await describeGate(
|
|
139
166
|
makeTcc({ input: { command: "cat .env" } }),
|
|
140
167
|
checkPermission,
|
|
141
168
|
getSessionRuleset,
|
|
@@ -156,7 +183,7 @@ describe("describeBashPathGate", () => {
|
|
|
156
183
|
origin: "session",
|
|
157
184
|
},
|
|
158
185
|
]);
|
|
159
|
-
const result = await
|
|
186
|
+
const result = await describeGate(
|
|
160
187
|
makeTcc({ input: { command: "cat .env" } }),
|
|
161
188
|
checkPermission,
|
|
162
189
|
getSessionRuleset,
|
|
@@ -169,7 +196,7 @@ describe("describeBashPathGate", () => {
|
|
|
169
196
|
it("returns null when command is missing", async () => {
|
|
170
197
|
const checkPermission = vi.fn<CheckPermissionFn>();
|
|
171
198
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
172
|
-
const result = await
|
|
199
|
+
const result = await describeGate(
|
|
173
200
|
makeTcc({ input: {} }),
|
|
174
201
|
checkPermission,
|
|
175
202
|
getSessionRuleset,
|
|
@@ -188,7 +215,7 @@ describe("describeBashPathGate", () => {
|
|
|
188
215
|
return makeCheckResult({ state: "deny", matchedPattern: "*.env" });
|
|
189
216
|
});
|
|
190
217
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
191
|
-
const result = await
|
|
218
|
+
const result = await describeGate(
|
|
192
219
|
makeTcc({ input: { command: "cat src/foo.ts .env" } }),
|
|
193
220
|
checkPermission,
|
|
194
221
|
getSessionRuleset,
|
|
@@ -209,7 +236,7 @@ describe("describeBashPathGate", () => {
|
|
|
209
236
|
return makeCheckResult({ state: "allow" });
|
|
210
237
|
});
|
|
211
238
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
212
|
-
const result = await
|
|
239
|
+
const result = await describeGate(
|
|
213
240
|
makeTcc({ input: { command: "cp .env README.md" } }),
|
|
214
241
|
checkPermission,
|
|
215
242
|
getSessionRuleset,
|
|
@@ -232,7 +259,7 @@ describe("describeBashPathGate", () => {
|
|
|
232
259
|
return makeCheckResult({ state: "allow" });
|
|
233
260
|
});
|
|
234
261
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
235
|
-
const result = await
|
|
262
|
+
const result = await describeGate(
|
|
236
263
|
makeTcc({ input: { command: "echo test > .env" } }),
|
|
237
264
|
checkPermission,
|
|
238
265
|
getSessionRuleset,
|
|
@@ -252,7 +279,7 @@ describe("describeBashPathGate", () => {
|
|
|
252
279
|
}),
|
|
253
280
|
);
|
|
254
281
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
255
|
-
const result = await
|
|
282
|
+
const result = await describeGate(
|
|
256
283
|
makeTcc({ input: { command: "cat .env" } }),
|
|
257
284
|
checkPermission,
|
|
258
285
|
getSessionRuleset,
|
|
@@ -280,7 +307,7 @@ describe("describeBashPathGate", () => {
|
|
|
280
307
|
});
|
|
281
308
|
});
|
|
282
309
|
const getSessionRuleset = vi.fn<() => Rule[]>().mockReturnValue([]);
|
|
283
|
-
const result = await
|
|
310
|
+
const result = await describeGate(
|
|
284
311
|
makeTcc({ input: { command: "cat src/foo.ts .env" } }),
|
|
285
312
|
checkPermission,
|
|
286
313
|
getSessionRuleset,
|