@aliou/pi-guardrails 0.14.0 → 0.15.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/README.md
CHANGED
|
@@ -64,7 +64,7 @@ Granted paths are stored in `pathAccess.allowedPaths` as explicit `{ kind, path
|
|
|
64
64
|
|
|
65
65
|
The `permission-gate` extension detects dangerous bash commands before they run.
|
|
66
66
|
|
|
67
|
-
It catches built-in risky patterns like recursive deletes, privileged commands, disk formatting, broad permission changes, and configured custom patterns. You can allow once, allow for the session, deny, or configure auto-deny rules.
|
|
67
|
+
It catches built-in risky patterns like recursive deletes, privileged commands, disk formatting, broad permission changes, and configured custom patterns. You can allow once, allow for the session, deny, decline and stop (which also aborts the current turn), or configure auto-deny rules.
|
|
68
68
|
|
|
69
69
|
[](https://assets.aliou.me/github/aliou/pi-guardrails/v0.12.0/permission-gate.mp4)
|
|
70
70
|
|
|
@@ -89,7 +89,7 @@ export default async function permissionGate(pi: ExtensionAPI) {
|
|
|
89
89
|
return { block: true, reason };
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
type ConfirmResult = "allow" | "allow-session" | "deny";
|
|
92
|
+
type ConfirmResult = "allow" | "allow-session" | "deny" | "stop";
|
|
93
93
|
emitActionPrompted(pi, {
|
|
94
94
|
feature: "permissionGate",
|
|
95
95
|
action: safety.action,
|
|
@@ -108,10 +108,11 @@ export default async function permissionGate(pi: ExtensionAPI) {
|
|
|
108
108
|
if (result === undefined) {
|
|
109
109
|
const selection = await ctx.ui.select(
|
|
110
110
|
`Dangerous command: ${safety.reason}`,
|
|
111
|
-
["Allow once", "Allow for session", "Deny"],
|
|
111
|
+
["Allow once", "Allow for session", "Deny", "Decline and stop"],
|
|
112
112
|
);
|
|
113
113
|
if (selection === "Allow once") result = "allow";
|
|
114
114
|
else if (selection === "Allow for session") result = "allow-session";
|
|
115
|
+
else if (selection === "Decline and stop") result = "stop";
|
|
115
116
|
else result = "deny";
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -121,6 +122,19 @@ export default async function permissionGate(pi: ExtensionAPI) {
|
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
124
|
|
|
125
|
+
if (result === "stop") {
|
|
126
|
+
const reason = "User declined and stopped dangerous command";
|
|
127
|
+
emitActionBlocked(pi, {
|
|
128
|
+
feature: "permissionGate",
|
|
129
|
+
action: safety.action,
|
|
130
|
+
reason,
|
|
131
|
+
block: { source: "user-stop", metadata: safety.metadata },
|
|
132
|
+
context: { toolName: "bash", input: event.input },
|
|
133
|
+
});
|
|
134
|
+
ctx.abort();
|
|
135
|
+
return { block: true, reason };
|
|
136
|
+
}
|
|
137
|
+
|
|
124
138
|
const reason = "User denied dangerous command";
|
|
125
139
|
emitActionBlocked(pi, {
|
|
126
140
|
feature: "permissionGate",
|
|
@@ -103,7 +103,7 @@ export function createPermissionGateConfirmComponent(
|
|
|
103
103
|
tui: { terminal: { rows: number; columns: number }; requestRender(): void },
|
|
104
104
|
theme: MinimalTheme,
|
|
105
105
|
_kb: unknown,
|
|
106
|
-
done: (result: "allow" | "allow-session" | "deny") => void,
|
|
106
|
+
done: (result: "allow" | "allow-session" | "deny" | "stop") => void,
|
|
107
107
|
) => {
|
|
108
108
|
const container = new Container();
|
|
109
109
|
const redBorder = (s: string) => theme.fg("error", s);
|
|
@@ -140,7 +140,7 @@ export function createPermissionGateConfirmComponent(
|
|
|
140
140
|
new Text(
|
|
141
141
|
theme.fg(
|
|
142
142
|
"dim",
|
|
143
|
-
"↑/↓ or j/k: scroll • y/enter: allow • a: session • n/esc: deny",
|
|
143
|
+
"↑/↓ or j/k: scroll • y/enter: allow • a: session • n/esc: deny • s: decline & stop",
|
|
144
144
|
),
|
|
145
145
|
1,
|
|
146
146
|
0,
|
|
@@ -215,6 +215,8 @@ export function createPermissionGateConfirmComponent(
|
|
|
215
215
|
data === "N"
|
|
216
216
|
) {
|
|
217
217
|
done("deny");
|
|
218
|
+
} else if (data === "s" || data === "S") {
|
|
219
|
+
done("stop");
|
|
218
220
|
}
|
|
219
221
|
},
|
|
220
222
|
};
|
package/package.json
CHANGED
|
@@ -5,7 +5,12 @@ export function shouldRun(config: GuardrailsConfig): boolean {
|
|
|
5
5
|
const raw = config as Record<string, unknown>;
|
|
6
6
|
const pathAccess = raw.pathAccess as Record<string, unknown> | undefined;
|
|
7
7
|
if (!Array.isArray(pathAccess?.allowedPaths)) return false;
|
|
8
|
-
return pathAccess.allowedPaths.some(
|
|
8
|
+
return pathAccess.allowedPaths.some(
|
|
9
|
+
(item) =>
|
|
10
|
+
typeof item === "object" &&
|
|
11
|
+
item !== null &&
|
|
12
|
+
typeof (item as Record<string, unknown>).pattern === "string",
|
|
13
|
+
);
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
export function run(config: GuardrailsConfig): GuardrailsConfig {
|
|
@@ -27,8 +32,11 @@ function normalizeAllowedPaths(items: unknown): string[] {
|
|
|
27
32
|
if (typeof item === "string") {
|
|
28
33
|
path = item;
|
|
29
34
|
} else if (typeof item === "object" && item !== null) {
|
|
30
|
-
const
|
|
35
|
+
const obj = item as Record<string, unknown>;
|
|
36
|
+
const pattern = obj.pattern;
|
|
37
|
+
const objectPath = obj.path;
|
|
31
38
|
if (typeof pattern === "string") path = pattern;
|
|
39
|
+
else if (typeof objectPath === "string") path = objectPath;
|
|
32
40
|
}
|
|
33
41
|
|
|
34
42
|
const normalized = path?.trim();
|