@aliou/pi-guardrails 0.9.2 → 0.9.4
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aliou/pi-guardrails",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"README.md"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@aliou/pi-utils-settings": "^0.10.
|
|
32
|
+
"@aliou/pi-utils-settings": "^0.10.1",
|
|
33
33
|
"@aliou/sh": "^0.1.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
@@ -91,6 +91,173 @@ const POLICY_EXAMPLES: Array<{
|
|
|
91
91
|
enabled: true,
|
|
92
92
|
},
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
label: "SSH keys",
|
|
96
|
+
description: "Block access to SSH private keys",
|
|
97
|
+
rule: {
|
|
98
|
+
id: "example-ssh-keys",
|
|
99
|
+
name: "SSH keys",
|
|
100
|
+
description: "Block SSH private key files",
|
|
101
|
+
patterns: [
|
|
102
|
+
{ pattern: "*.pem" },
|
|
103
|
+
{ pattern: "*_rsa" },
|
|
104
|
+
{ pattern: "*_ed25519" },
|
|
105
|
+
],
|
|
106
|
+
allowedPatterns: [{ pattern: "*.pub" }],
|
|
107
|
+
protection: "noAccess",
|
|
108
|
+
onlyIfExists: true,
|
|
109
|
+
enabled: true,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
label: "AWS credentials",
|
|
114
|
+
description: "Block AWS CLI credentials file",
|
|
115
|
+
rule: {
|
|
116
|
+
id: "example-aws-credentials",
|
|
117
|
+
name: "AWS credentials",
|
|
118
|
+
description: "Block AWS credentials and config files",
|
|
119
|
+
patterns: [{ pattern: ".aws/credentials" }, { pattern: ".aws/config" }],
|
|
120
|
+
protection: "noAccess",
|
|
121
|
+
onlyIfExists: true,
|
|
122
|
+
enabled: true,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
label: "Database files",
|
|
127
|
+
description: "Mark SQLite/DB files read-only",
|
|
128
|
+
rule: {
|
|
129
|
+
id: "example-database-files",
|
|
130
|
+
name: "Database files",
|
|
131
|
+
description: "Protect database files from modification",
|
|
132
|
+
patterns: [
|
|
133
|
+
{ pattern: "*.db" },
|
|
134
|
+
{ pattern: "*.sqlite" },
|
|
135
|
+
{ pattern: "*.sqlite3" },
|
|
136
|
+
],
|
|
137
|
+
protection: "readOnly",
|
|
138
|
+
onlyIfExists: true,
|
|
139
|
+
enabled: true,
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: "Kubernetes secrets",
|
|
144
|
+
description: "Block kubeconfig and k8s secrets",
|
|
145
|
+
rule: {
|
|
146
|
+
id: "example-k8s-secrets",
|
|
147
|
+
name: "Kubernetes secrets",
|
|
148
|
+
description: "Block kubectl config and secrets",
|
|
149
|
+
patterns: [{ pattern: ".kube/config" }, { pattern: "*kubeconfig*" }],
|
|
150
|
+
protection: "noAccess",
|
|
151
|
+
onlyIfExists: true,
|
|
152
|
+
enabled: true,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
label: "Certificates",
|
|
157
|
+
description: "Block SSL/TLS certificate files",
|
|
158
|
+
rule: {
|
|
159
|
+
id: "example-certificates",
|
|
160
|
+
name: "Certificates",
|
|
161
|
+
description: "Block certificate and key files",
|
|
162
|
+
patterns: [
|
|
163
|
+
{ pattern: "*.crt" },
|
|
164
|
+
{ pattern: "*.key" },
|
|
165
|
+
{ pattern: "*.p12" },
|
|
166
|
+
],
|
|
167
|
+
allowedPatterns: [{ pattern: "*.csr" }],
|
|
168
|
+
protection: "noAccess",
|
|
169
|
+
onlyIfExists: true,
|
|
170
|
+
enabled: true,
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
const COMMAND_EXAMPLES: Array<{
|
|
176
|
+
label: string;
|
|
177
|
+
description: string;
|
|
178
|
+
pattern: DangerousPattern;
|
|
179
|
+
}> = [
|
|
180
|
+
{
|
|
181
|
+
label: "Homebrew",
|
|
182
|
+
description: "Block brew commands (use Nix instead)",
|
|
183
|
+
pattern: { pattern: "brew", description: "Homebrew package manager" },
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
label: "Docker secrets",
|
|
187
|
+
description: "Block docker commands that may expose environment secrets",
|
|
188
|
+
pattern: {
|
|
189
|
+
pattern: "docker inspect",
|
|
190
|
+
description: "Docker inspect (may expose env vars)",
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
label: "Terraform apply",
|
|
195
|
+
description: "Require confirmation for infrastructure changes",
|
|
196
|
+
pattern: {
|
|
197
|
+
pattern: "terraform apply",
|
|
198
|
+
description: "Terraform infrastructure changes",
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
label: "Terraform destroy",
|
|
203
|
+
description: "Require confirmation for infrastructure destruction",
|
|
204
|
+
pattern: {
|
|
205
|
+
pattern: "terraform destroy",
|
|
206
|
+
description: "Terraform infrastructure destruction",
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
label: "kubectl delete",
|
|
211
|
+
description: "Require confirmation for k8s resource deletion",
|
|
212
|
+
pattern: {
|
|
213
|
+
pattern: "kubectl delete",
|
|
214
|
+
description: "Kubernetes resource deletion",
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
label: "docker system prune",
|
|
219
|
+
description: "Require confirmation for Docker cleanup",
|
|
220
|
+
pattern: {
|
|
221
|
+
pattern: "docker system prune",
|
|
222
|
+
description: "Docker system cleanup",
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
label: "git push --force",
|
|
227
|
+
description: "Require confirmation for force push",
|
|
228
|
+
pattern: { pattern: "git push --force", description: "Git force push" },
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
label: "npm publish",
|
|
232
|
+
description: "Require confirmation for package publishing",
|
|
233
|
+
pattern: { pattern: "npm publish", description: "NPM package publishing" },
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
label: "yarn publish",
|
|
237
|
+
description: "Require confirmation for package publishing",
|
|
238
|
+
pattern: {
|
|
239
|
+
pattern: "yarn publish",
|
|
240
|
+
description: "Yarn package publishing",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
label: "pnpm publish",
|
|
245
|
+
description: "Require confirmation for package publishing",
|
|
246
|
+
pattern: {
|
|
247
|
+
pattern: "pnpm publish",
|
|
248
|
+
description: "PNPM package publishing",
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
label: "drop database",
|
|
253
|
+
description: "Require confirmation for database drops",
|
|
254
|
+
pattern: { pattern: "DROP DATABASE", description: "SQL database drop" },
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: "drop table",
|
|
258
|
+
description: "Require confirmation for table drops",
|
|
259
|
+
pattern: { pattern: "DROP TABLE", description: "SQL table drop" },
|
|
260
|
+
},
|
|
94
261
|
];
|
|
95
262
|
|
|
96
263
|
function toKebabCase(input: string): string {
|
|
@@ -129,6 +296,26 @@ function appendPolicyRule(
|
|
|
129
296
|
return next;
|
|
130
297
|
}
|
|
131
298
|
|
|
299
|
+
function appendDangerousPattern(
|
|
300
|
+
config: GuardrailsConfig | null,
|
|
301
|
+
pattern: DangerousPattern,
|
|
302
|
+
): GuardrailsConfig {
|
|
303
|
+
const next = structuredClone(config ?? {}) as GuardrailsConfig;
|
|
304
|
+
const currentPatterns = next.permissionGate?.patterns ?? [];
|
|
305
|
+
|
|
306
|
+
const existingPatterns = new Set(currentPatterns.map((p) => p.pattern));
|
|
307
|
+
if (existingPatterns.has(pattern.pattern)) {
|
|
308
|
+
return next;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
next.permissionGate = {
|
|
312
|
+
...(next.permissionGate ?? {}),
|
|
313
|
+
patterns: [...currentPatterns, structuredClone(pattern)],
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
return next;
|
|
317
|
+
}
|
|
318
|
+
|
|
132
319
|
interface NewPolicyDraft {
|
|
133
320
|
name: string;
|
|
134
321
|
id: string;
|
|
@@ -1042,7 +1229,7 @@ export function registerGuardrailsSettings(pi: ExtensionAPI): void {
|
|
|
1042
1229
|
setDraftForScope,
|
|
1043
1230
|
theme,
|
|
1044
1231
|
}): SettingsSection[] => {
|
|
1045
|
-
const
|
|
1232
|
+
const policyItems: SettingItem[] = POLICY_EXAMPLES.map((example) => ({
|
|
1046
1233
|
id: `examples.${example.rule.id}`,
|
|
1047
1234
|
label: ` ${example.label}`,
|
|
1048
1235
|
description: example.description,
|
|
@@ -1063,10 +1250,40 @@ export function registerGuardrailsSettings(pi: ExtensionAPI): void {
|
|
|
1063
1250
|
),
|
|
1064
1251
|
}));
|
|
1065
1252
|
|
|
1253
|
+
const commandItems: SettingItem[] = COMMAND_EXAMPLES.map(
|
|
1254
|
+
(example) => ({
|
|
1255
|
+
id: `examples.cmd.${example.pattern.pattern}`,
|
|
1256
|
+
label: ` ${example.label}`,
|
|
1257
|
+
description: example.description,
|
|
1258
|
+
currentValue: "add",
|
|
1259
|
+
submenu: (_val: string, submenuDone: (v?: string) => void) =>
|
|
1260
|
+
new ScopePickerSubmenu(
|
|
1261
|
+
theme,
|
|
1262
|
+
enabledScopes,
|
|
1263
|
+
(targetScope) => {
|
|
1264
|
+
const baseConfig =
|
|
1265
|
+
getDraftForScope(targetScope) ??
|
|
1266
|
+
getRawForScope(targetScope) ??
|
|
1267
|
+
null;
|
|
1268
|
+
const updated = appendDangerousPattern(
|
|
1269
|
+
baseConfig,
|
|
1270
|
+
example.pattern,
|
|
1271
|
+
);
|
|
1272
|
+
setDraftForScope(targetScope, updated);
|
|
1273
|
+
},
|
|
1274
|
+
submenuDone,
|
|
1275
|
+
),
|
|
1276
|
+
}),
|
|
1277
|
+
);
|
|
1278
|
+
|
|
1066
1279
|
return [
|
|
1067
1280
|
{
|
|
1068
|
-
label: "
|
|
1069
|
-
items,
|
|
1281
|
+
label: "File policy presets",
|
|
1282
|
+
items: policyItems,
|
|
1283
|
+
},
|
|
1284
|
+
{
|
|
1285
|
+
label: "Dangerous command presets",
|
|
1286
|
+
items: commandItems,
|
|
1070
1287
|
},
|
|
1071
1288
|
];
|
|
1072
1289
|
},
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type ExtensionAPI,
|
|
5
5
|
type ExtensionContext,
|
|
6
6
|
getMarkdownTheme,
|
|
7
|
+
isToolCallEventType,
|
|
7
8
|
} from "@mariozechner/pi-coding-agent";
|
|
8
9
|
import {
|
|
9
10
|
Box,
|
|
@@ -268,9 +269,9 @@ export function setupPermissionGateHook(
|
|
|
268
269
|
);
|
|
269
270
|
|
|
270
271
|
pi.on("tool_call", async (event, ctx) => {
|
|
271
|
-
if (
|
|
272
|
+
if (!isToolCallEventType("bash", event)) return;
|
|
272
273
|
|
|
273
|
-
const command =
|
|
274
|
+
const command = event.input.command;
|
|
274
275
|
|
|
275
276
|
// Check allowed patterns first (bypass)
|
|
276
277
|
for (const pattern of allowedPatterns) {
|