@gotgenes/pi-permission-system 16.2.1 → 17.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 +26 -0
- package/README.md +2 -1
- package/package.json +1 -1
- package/src/access-intent/access-intent.ts +8 -10
- package/src/access-intent/access-path.ts +39 -16
- package/src/access-intent/bash/bash-path-resolver.ts +531 -0
- package/src/access-intent/bash/program.ts +21 -14
- package/src/access-intent/bash/token-classification.ts +1 -1
- package/src/canonicalize-path.ts +11 -5
- package/src/forwarded-permissions/permission-forwarder.ts +11 -1
- package/src/forwarding-manager.ts +7 -1
- package/src/handlers/before-agent-start.ts +1 -0
- package/src/handlers/gates/bash-path-extractor.ts +7 -5
- package/src/handlers/gates/bash-path.ts +13 -13
- package/src/handlers/gates/external-directory.ts +14 -16
- package/src/handlers/gates/path.ts +12 -7
- package/src/handlers/gates/skill-read.ts +7 -1
- package/src/handlers/gates/tool-call-gate-pipeline.ts +21 -4
- package/src/handlers/gates/tool.ts +4 -2
- package/src/index.ts +12 -1
- package/src/input-normalizer.ts +9 -4
- package/src/path-normalizer.ts +70 -0
- package/src/path-utils.ts +39 -28
- package/src/permission-manager.ts +21 -7
- package/src/permission-session.ts +35 -2
- package/src/prompting-gateway.ts +3 -0
- package/src/rule.ts +8 -6
- package/src/skill-prompt-sanitizer.ts +15 -4
- package/src/subagent-context.ts +17 -9
- package/test/access-intent/access-path.test.ts +124 -15
- package/test/access-intent/bash/program.test.ts +178 -80
- package/test/bash-external-directory.test.ts +15 -1
- package/test/canonicalize-path.test.ts +34 -8
- package/test/forwarding-manager.test.ts +7 -1
- package/test/handlers/external-directory-symlink-acceptance.test.ts +26 -4
- package/test/handlers/gates/bash-command-metamorphic.test.ts +5 -1
- package/test/handlers/gates/bash-external-directory.test.ts +5 -2
- package/test/handlers/gates/bash-path.test.ts +14 -9
- package/test/handlers/gates/external-directory-policy.test.ts +28 -18
- package/test/handlers/gates/external-directory.test.ts +9 -1
- package/test/handlers/gates/path.test.ts +90 -20
- package/test/handlers/gates/skill-read.test.ts +22 -13
- package/test/handlers/gates/tool-call-gate-pipeline.test.ts +2 -1
- package/test/handlers/gates/tool.test.ts +13 -1
- package/test/helpers/gate-fixtures.ts +11 -2
- package/test/helpers/session-fixtures.ts +3 -0
- package/test/input-normalizer.test.ts +104 -39
- package/test/path-normalizer.test.ts +123 -0
- package/test/path-utils.test.ts +130 -51
- package/test/permission-forwarder.test.ts +1 -0
- package/test/permission-manager-unified.test.ts +40 -0
- package/test/permission-resolver.test.ts +14 -32
- package/test/permission-session.test.ts +41 -0
- package/test/pi-infrastructure-read.test.ts +27 -4
- package/test/prompting-gateway.test.ts +1 -0
- package/test/rule.test.ts +88 -42
- package/test/session-rules.test.ts +21 -4
- package/test/skill-prompt-sanitizer.test.ts +69 -14
- package/test/subagent-context.test.ts +77 -31
- package/test/synthesize.test.ts +13 -11
- package/src/access-intent/bash/cwd-projection.ts +0 -498
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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
|
+
## [17.1.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v17.0.0...pi-permission-system-v17.1.0) (2026-06-28)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **pi-permission-system:** add PathNormalizer collaborator ([#510](https://github.com/gotgenes/pi-packages/issues/510)) ([d016089](https://github.com/gotgenes/pi-packages/commit/d016089690aff3821eabd5bd5351bb3a0c3639d4))
|
|
14
|
+
|
|
15
|
+
## [17.0.0](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v16.2.1...pi-permission-system-v17.0.0) (2026-06-27)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### ⚠ BREAKING CHANGES
|
|
19
|
+
|
|
20
|
+
* **pi-permission-system:** the path surface now also matches the canonical (symlink-resolved) form of bash path-rule tokens, so a path rule can fire on a symlink alias it previously missed, changing decisions on upgrade with no config edit.
|
|
21
|
+
* **pi-permission-system:** the path surface now also matches the canonical (symlink-resolved) form of a tool's file path. A path rule that previously matched only the as-typed spelling now also matches when the path resolves through a symlink to a target the pattern covers, which can change allow/deny decisions on upgrade with no config edit.
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* **pi-permission-system:** add AccessPath.forPath and forLiteral factories ([4323cae](https://github.com/gotgenes/pi-packages/commit/4323cae859907dd62d9cb401c194443717ab752c)), closes [#486](https://github.com/gotgenes/pi-packages/issues/486)
|
|
26
|
+
* **pi-permission-system:** match the canonical form on the bash-path gate ([6ce0c06](https://github.com/gotgenes/pi-packages/commit/6ce0c06b35c12eca32ffce5d4fe974c2f2dee393))
|
|
27
|
+
* **pi-permission-system:** match the canonical form on the path tool gate ([869ca76](https://github.com/gotgenes/pi-packages/commit/869ca761cf3d7a8367eb932106065a866d996ce4))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Documentation
|
|
31
|
+
|
|
32
|
+
* **pi-permission-system:** document canonical path-surface matching ([9606dce](https://github.com/gotgenes/pi-packages/commit/9606dce744bc1f95bd8424d2bb115a376d9a1191)), closes [#486](https://github.com/gotgenes/pi-packages/issues/486)
|
|
33
|
+
|
|
8
34
|
## [16.2.1](https://github.com/gotgenes/pi-packages/compare/pi-permission-system-v16.2.0...pi-permission-system-v16.2.1) (2026-06-27)
|
|
9
35
|
|
|
10
36
|
|
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Permission enforcement extension for the [Pi](https://pi.mariozechner.at/) codin
|
|
|
17
17
|
- **Enforces allow / ask / deny** at tool-call time with UI confirmation dialogs
|
|
18
18
|
- **Controls bash commands** with wildcard pattern matching (`git *: ask`, `rm -rf *: deny`)
|
|
19
19
|
- **Gates MCP and skill access** at server, tool, and skill-name granularity
|
|
20
|
-
- **Protects sensitive file patterns** — cross-cutting `path` rules deny `.env`, `~/.ssh/*`, etc. across all tools and bash at once
|
|
20
|
+
- **Protects sensitive file patterns** — cross-cutting `path` rules deny `.env`, `~/.ssh/*`, etc. across all tools and bash at once, matching both the path as referenced and its symlink-resolved form so a deny cannot be evaded through a symlink alias
|
|
21
21
|
- **Guards external paths** — prompts before file tools or bash commands reach outside `cwd`
|
|
22
22
|
- **Fails closed** — an internal gate error blocks the tool (with a `gate_error` review-log entry), and an unparseable bash command — or an opaque `bash -c`/`eval` wrapper — prompts (`ask`) rather than passing silently
|
|
23
23
|
- **Forwards prompts from subagents** — `ask` policies work even in non-UI execution contexts
|
|
@@ -69,6 +69,7 @@ See [docs/session-approvals.md](docs/session-approvals.md) for details on sessio
|
|
|
69
69
|
|
|
70
70
|
The `path` surface is a cross-cutting gate that applies to **all** file access — Pi tools, bash commands, MCP calls, and extension tools alike.
|
|
71
71
|
Extension and MCP tools that operate on paths (via `input.path`, MCP's `input.arguments.path`, or a registered access extractor) are gated by default, so a `path` deny cannot be overridden by a per-tool allow — making it the right place to protect sensitive files like `.env` or `~/.ssh/*` from every tool at once.
|
|
72
|
+
A `path` pattern matches both the path as the agent references it and its canonical (symlink-resolved) form, so a deny still fires when a symlink aliases a sensitive target.
|
|
72
73
|
|
|
73
74
|
For per-tool path patterns (`read`, `write`, `edit`, `find`, `grep`, `ls`), patterns are matched against the file path from `input.path`.
|
|
74
75
|
This lets you express rules like "allow reads but deny `.env` files" at the individual tool level.
|
package/package.json
CHANGED
|
@@ -17,8 +17,9 @@ export interface ToolAccessIntent {
|
|
|
17
17
|
/**
|
|
18
18
|
* Precomputed equivalent policy values for a path-shaped surface.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
* `
|
|
20
|
+
* Not gate-emitted: the resolver produces it internally by unwrapping an
|
|
21
|
+
* `access-path` intent via `matchValues()`, keeping the low-level manager
|
|
22
|
+
* string-based (it never imports `AccessPath`). See {@link ResolvedAccessIntent}.
|
|
22
23
|
*/
|
|
23
24
|
export interface PathValuesAccessIntent {
|
|
24
25
|
kind: "path-values";
|
|
@@ -31,9 +32,9 @@ export interface PathValuesAccessIntent {
|
|
|
31
32
|
/**
|
|
32
33
|
* An `AccessPath` value object for a path-shaped surface.
|
|
33
34
|
*
|
|
34
|
-
*
|
|
35
|
-
* resolver as a first-class variant so the
|
|
36
|
-
* for `matchValues()` (Tell-Don't-Ask).
|
|
35
|
+
* Emitted by every path gate (the `path` and `external_directory` surfaces);
|
|
36
|
+
* lets `AccessPath` flow into the resolver as a first-class variant so the
|
|
37
|
+
* resolver — not the gate — asks it for `matchValues()` (Tell-Don't-Ask).
|
|
37
38
|
*/
|
|
38
39
|
export interface AccessPathAccessIntent {
|
|
39
40
|
kind: "access-path";
|
|
@@ -42,11 +43,8 @@ export interface AccessPathAccessIntent {
|
|
|
42
43
|
agentName?: string;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
/** What a gate emits —
|
|
46
|
-
export type AccessIntent =
|
|
47
|
-
| ToolAccessIntent
|
|
48
|
-
| PathValuesAccessIntent
|
|
49
|
-
| AccessPathAccessIntent;
|
|
46
|
+
/** What a gate emits — a raw tool input or an `AccessPath`. */
|
|
47
|
+
export type AccessIntent = ToolAccessIntent | AccessPathAccessIntent;
|
|
50
48
|
|
|
51
49
|
/**
|
|
52
50
|
* What the manager consumes — the `access-path` variant has already been
|
|
@@ -20,7 +20,9 @@ import {
|
|
|
20
20
|
* - {@link value} returns `string` — the lexical absolute form, for display,
|
|
21
21
|
* approval patterns, decision values, and logs.
|
|
22
22
|
*
|
|
23
|
-
* Construct via {@link
|
|
23
|
+
* Construct via {@link forPath} (resolved, with optional cd-folded base) or
|
|
24
|
+
* {@link forLiteral} (literal-only, for an unknown base); the constructor is
|
|
25
|
+
* private.
|
|
24
26
|
*/
|
|
25
27
|
export class AccessPath {
|
|
26
28
|
private constructor(
|
|
@@ -65,24 +67,45 @@ export class AccessPath {
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
/**
|
|
68
|
-
* Build an `AccessPath` for a tool-input or bash-token path resolved against
|
|
69
|
-
* `cwd
|
|
70
|
+
* Build an `AccessPath` for a tool-input or bash-token path, resolved against
|
|
71
|
+
* `resolveBase` (the cd-folded effective directory; defaults to `cwd`).
|
|
70
72
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* - `
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
73
|
+
* Serves every path surface: the tool path gate, the tool external-directory
|
|
74
|
+
* gate, and the bash path/external-directory gates (which pass a cd-resolved
|
|
75
|
+
* `resolveBase`).
|
|
76
|
+
*
|
|
77
|
+
* - `matchValues()` returns the lexical alias union from `getPathPolicyValues`
|
|
78
|
+
* plus the canonical alias from `canonicalNormalizePathForComparison`
|
|
79
|
+
* (#418), so a config pattern on either the typed or symlink-resolved form
|
|
80
|
+
* matches.
|
|
81
|
+
* - `boundaryValue()` returns
|
|
82
|
+
* `canonicalNormalizePathForComparison(pathValue, resolveBase)`, which is
|
|
83
|
+
* win32-lowercased (#382) — do not substitute a raw `canonicalizePath`
|
|
84
|
+
* output here.
|
|
85
|
+
* - `value()` returns `normalizePathForComparison(pathValue, resolveBase)`,
|
|
86
|
+
* the absolute lexical form.
|
|
80
87
|
*/
|
|
81
|
-
static
|
|
88
|
+
static forPath(
|
|
89
|
+
pathValue: string,
|
|
90
|
+
options: { cwd: string; resolveBase?: string; platform: NodeJS.Platform },
|
|
91
|
+
): AccessPath {
|
|
92
|
+
const { cwd, resolveBase = cwd, platform } = options;
|
|
82
93
|
return new AccessPath(
|
|
83
|
-
normalizePathForComparison(pathValue,
|
|
84
|
-
getPathPolicyValues(pathValue, { cwd }),
|
|
85
|
-
canonicalNormalizePathForComparison(pathValue,
|
|
94
|
+
normalizePathForComparison(pathValue, resolveBase, platform),
|
|
95
|
+
getPathPolicyValues(pathValue, { cwd, resolveBase }, platform),
|
|
96
|
+
canonicalNormalizePathForComparison(pathValue, resolveBase, platform),
|
|
86
97
|
);
|
|
87
98
|
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Build a literal-only `AccessPath` for a path whose effective base is
|
|
102
|
+
* unknown (a relative bash token after a non-literal `cd`).
|
|
103
|
+
*
|
|
104
|
+
* Carries no canonical alias and no absolute resolution — `matchValues()` is
|
|
105
|
+
* `[literal]` (or `[]` when empty) and `boundaryValue()` is `""` — so no
|
|
106
|
+
* spurious absolute or symlink-resolved rule can match (#393).
|
|
107
|
+
*/
|
|
108
|
+
static forLiteral(literal: string): AccessPath {
|
|
109
|
+
return new AccessPath(literal, literal ? [literal] : [], "");
|
|
110
|
+
}
|
|
88
111
|
}
|