@gotgenes/pi-permission-system 17.0.0 → 17.1.1

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +1 -1
  3. package/src/access-intent/access-path.ts +5 -5
  4. package/src/access-intent/bash/bash-path-resolver.ts +535 -0
  5. package/src/access-intent/bash/program.ts +21 -14
  6. package/src/access-intent/bash/token-classification.ts +24 -1
  7. package/src/canonicalize-path.ts +11 -5
  8. package/src/forwarded-permissions/permission-forwarder.ts +11 -1
  9. package/src/forwarding-manager.ts +7 -1
  10. package/src/handlers/before-agent-start.ts +1 -1
  11. package/src/handlers/gates/bash-path-extractor.ts +7 -5
  12. package/src/handlers/gates/external-directory.ts +7 -18
  13. package/src/handlers/gates/path.ts +3 -2
  14. package/src/handlers/gates/skill-read.ts +4 -2
  15. package/src/handlers/gates/tool-call-gate-pipeline.ts +20 -4
  16. package/src/handlers/gates/tool.ts +4 -2
  17. package/src/index.ts +12 -1
  18. package/src/input-normalizer.ts +9 -4
  19. package/src/path-normalizer.ts +100 -0
  20. package/src/path-utils.ts +39 -28
  21. package/src/permission-manager.ts +21 -7
  22. package/src/permission-session.ts +35 -2
  23. package/src/prompting-gateway.ts +3 -0
  24. package/src/rule.ts +8 -6
  25. package/src/skill-prompt-sanitizer.ts +8 -10
  26. package/src/subagent-context.ts +17 -9
  27. package/test/access-intent/access-path.test.ts +73 -24
  28. package/test/access-intent/bash/program.test.ts +131 -65
  29. package/test/access-intent/bash/token-classification.test.ts +75 -0
  30. package/test/bash-external-directory.test.ts +53 -1
  31. package/test/canonicalize-path.test.ts +34 -8
  32. package/test/forwarding-manager.test.ts +7 -1
  33. package/test/handlers/external-directory-symlink-acceptance.test.ts +23 -4
  34. package/test/handlers/gates/bash-command-metamorphic.test.ts +5 -1
  35. package/test/handlers/gates/bash-external-directory.test.ts +5 -1
  36. package/test/handlers/gates/bash-path.test.ts +6 -1
  37. package/test/handlers/gates/external-directory-policy.test.ts +26 -8
  38. package/test/handlers/gates/external-directory.test.ts +8 -1
  39. package/test/handlers/gates/path.test.ts +53 -14
  40. package/test/handlers/gates/skill-read.test.ts +26 -13
  41. package/test/handlers/gates/tool-call-gate-pipeline.test.ts +2 -1
  42. package/test/handlers/gates/tool.test.ts +13 -1
  43. package/test/helpers/gate-fixtures.ts +10 -0
  44. package/test/helpers/session-fixtures.ts +3 -0
  45. package/test/input-normalizer.test.ts +104 -39
  46. package/test/path-normalizer.test.ts +166 -0
  47. package/test/path-utils.test.ts +130 -51
  48. package/test/permission-forwarder.test.ts +1 -0
  49. package/test/permission-manager-unified.test.ts +40 -0
  50. package/test/permission-resolver.test.ts +12 -3
  51. package/test/permission-session.test.ts +41 -0
  52. package/test/pi-infrastructure-read.test.ts +27 -4
  53. package/test/prompting-gateway.test.ts +1 -0
  54. package/test/rule.test.ts +88 -42
  55. package/test/session-rules.test.ts +21 -4
  56. package/test/skill-prompt-sanitizer.test.ts +37 -16
  57. package/test/subagent-context.test.ts +77 -31
  58. package/test/synthesize.test.ts +13 -11
  59. package/src/access-intent/bash/cwd-projection.ts +0 -500
@@ -35,30 +35,37 @@ describe("AccessPath.forPath", () => {
35
35
  realpathSync.mockImplementation((p: string) =>
36
36
  p.startsWith("/tmp") ? `/private${p}` : p,
37
37
  );
38
- expect(AccessPath.forPath("/tmp/x", { cwd }).matchValues()).toEqual([
39
- "/tmp/x",
40
- "/private/tmp/x",
41
- ]);
38
+ expect(
39
+ AccessPath.forPath("/tmp/x", { cwd, platform: "linux" }).matchValues(),
40
+ ).toEqual(["/tmp/x", "/private/tmp/x"]);
42
41
  });
43
42
 
44
43
  test("deduplicates when the canonical form equals the lexical form", () => {
45
- expect(AccessPath.forPath("/etc/hosts", { cwd }).matchValues()).toEqual([
46
- "/etc/hosts",
47
- ]);
44
+ expect(
45
+ AccessPath.forPath("/etc/hosts", {
46
+ cwd,
47
+ platform: "linux",
48
+ }).matchValues(),
49
+ ).toEqual(["/etc/hosts"]);
48
50
  });
49
51
 
50
52
  test("keeps the relative aliases for an in-cwd token without duplicating", () => {
51
- expect(AccessPath.forPath("src/foo.ts", { cwd }).matchValues()).toEqual([
52
- "/projects/my-app/src/foo.ts",
53
- "src/foo.ts",
54
- ]);
53
+ expect(
54
+ AccessPath.forPath("src/foo.ts", {
55
+ cwd,
56
+ platform: "linux",
57
+ }).matchValues(),
58
+ ).toEqual(["/projects/my-app/src/foo.ts", "src/foo.ts"]);
55
59
  });
56
60
 
57
61
  test("includes only the lexical aliases when canonical is empty", () => {
58
62
  // Force canonicalizePath to return the original (no-op symlink resolution
59
63
  // effectively means canonical === lexical, handled by dedup).
60
64
  expect(
61
- AccessPath.forPath("/etc/hosts", { cwd }).matchValues(),
65
+ AccessPath.forPath("/etc/hosts", {
66
+ cwd,
67
+ platform: "linux",
68
+ }).matchValues(),
62
69
  ).not.toHaveLength(0);
63
70
  });
64
71
 
@@ -68,6 +75,7 @@ describe("AccessPath.forPath", () => {
68
75
  AccessPath.forPath("foo.ts", {
69
76
  cwd,
70
77
  resolveBase: "/projects/my-app/sub",
78
+ platform: "linux",
71
79
  }).matchValues(),
72
80
  ).toEqual(["/projects/my-app/sub/foo.ts", "sub/foo.ts", "foo.ts"]);
73
81
  });
@@ -80,6 +88,7 @@ describe("AccessPath.forPath", () => {
80
88
  AccessPath.forPath("foo.ts", {
81
89
  cwd,
82
90
  resolveBase: "/projects/my-app/sub",
91
+ platform: "linux",
83
92
  }).matchValues(),
84
93
  ).toEqual([
85
94
  "/projects/my-app/sub/foo.ts",
@@ -90,24 +99,59 @@ describe("AccessPath.forPath", () => {
90
99
  });
91
100
  });
92
101
 
102
+ describe("platform option", () => {
103
+ test("win32: builds lexical/match/boundary values with win32 rules", () => {
104
+ const ap = AccessPath.forPath("src\\foo.ts", {
105
+ cwd: "C:\\Projects\\App",
106
+ platform: "win32",
107
+ });
108
+ expect(ap.value()).toBe("c:\\projects\\app\\src\\foo.ts");
109
+ expect(ap.boundaryValue()).toBe("c:\\projects\\app\\src\\foo.ts");
110
+ expect(ap.matchValues()).toEqual([
111
+ "c:\\projects\\app\\src\\foo.ts",
112
+ "src\\foo.ts",
113
+ ]);
114
+ });
115
+
116
+ test("win32: lowercases the symlink-resolved boundary value", () => {
117
+ realpathSync.mockImplementation((p: string) =>
118
+ p === "c:\\projects\\app\\link" ? "C:\\Real\\App" : p,
119
+ );
120
+ expect(
121
+ AccessPath.forPath("link", {
122
+ cwd: "C:\\Projects\\App",
123
+ platform: "win32",
124
+ }).boundaryValue(),
125
+ ).toBe("c:\\real\\app");
126
+ });
127
+ });
128
+
93
129
  describe("boundaryValue()", () => {
94
130
  test("returns the canonical (symlink-resolved) form", () => {
95
131
  realpathSync.mockImplementation((p: string) =>
96
132
  p.startsWith("/tmp") ? `/private${p}` : p,
97
133
  );
98
- expect(AccessPath.forPath("/tmp/x", { cwd }).boundaryValue()).toBe(
99
- "/private/tmp/x",
100
- );
134
+ expect(
135
+ AccessPath.forPath("/tmp/x", {
136
+ cwd,
137
+ platform: "linux",
138
+ }).boundaryValue(),
139
+ ).toBe("/private/tmp/x");
101
140
  });
102
141
 
103
142
  test("returns the lexical form when path has no symlinks", () => {
104
- expect(AccessPath.forPath("/etc/hosts", { cwd }).boundaryValue()).toBe(
105
- "/etc/hosts",
106
- );
143
+ expect(
144
+ AccessPath.forPath("/etc/hosts", {
145
+ cwd,
146
+ platform: "linux",
147
+ }).boundaryValue(),
148
+ ).toBe("/etc/hosts");
107
149
  });
108
150
 
109
151
  test("returns empty string for empty input", () => {
110
- expect(AccessPath.forPath("", { cwd }).boundaryValue()).toBe("");
152
+ expect(
153
+ AccessPath.forPath("", { cwd, platform: "linux" }).boundaryValue(),
154
+ ).toBe("");
111
155
  });
112
156
  });
113
157
 
@@ -117,14 +161,16 @@ describe("AccessPath.forPath", () => {
117
161
  p.startsWith("/tmp") ? `/private${p}` : p,
118
162
  );
119
163
  // Even when the path resolves to a different canonical, value() stays lexical.
120
- expect(AccessPath.forPath("/tmp/x", { cwd }).value()).toBe("/tmp/x");
164
+ expect(
165
+ AccessPath.forPath("/tmp/x", { cwd, platform: "linux" }).value(),
166
+ ).toBe("/tmp/x");
121
167
  });
122
168
 
123
169
  test("normalizes the path against cwd", () => {
124
170
  // A relative path becomes an absolute lexical value.
125
- expect(AccessPath.forPath("src/foo.ts", { cwd }).value()).toBe(
126
- "/projects/my-app/src/foo.ts",
127
- );
171
+ expect(
172
+ AccessPath.forPath("src/foo.ts", { cwd, platform: "linux" }).value(),
173
+ ).toBe("/projects/my-app/src/foo.ts");
128
174
  });
129
175
 
130
176
  test("normalizes a relative path against an explicit resolveBase", () => {
@@ -132,12 +178,15 @@ describe("AccessPath.forPath", () => {
132
178
  AccessPath.forPath("foo.ts", {
133
179
  cwd,
134
180
  resolveBase: "/projects/my-app/sub",
181
+ platform: "linux",
135
182
  }).value(),
136
183
  ).toBe("/projects/my-app/sub/foo.ts");
137
184
  });
138
185
 
139
186
  test("returns empty string for empty input", () => {
140
- expect(AccessPath.forPath("", { cwd }).value()).toBe("");
187
+ expect(AccessPath.forPath("", { cwd, platform: "linux" }).value()).toBe(
188
+ "",
189
+ );
141
190
  });
142
191
  });
143
192
  });