@codyswann/lisa 4.84.5 → 4.84.7

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 (100) hide show
  1. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  2. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  3. package/dist/core/upstream-evidence-manifest.js +21 -4
  4. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  5. package/package.json +4 -4
  6. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa/hooks/block-host-name-leak.mjs +130 -40
  9. package/plugins/lisa/hooks/block-host-name-leak.sh +5 -0
  10. package/plugins/lisa/hooks/host-name-command-context.mjs +409 -0
  11. package/plugins/lisa-agy/hooks/block-host-name-leak.mjs +130 -40
  12. package/plugins/lisa-agy/hooks/block-host-name-leak.sh +5 -0
  13. package/plugins/lisa-agy/hooks/host-name-command-context.mjs +409 -0
  14. package/plugins/lisa-agy/plugin.json +1 -1
  15. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  17. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  18. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  19. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  20. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  22. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa-copilot/hooks/block-host-name-leak.mjs +130 -40
  24. package/plugins/lisa-copilot/hooks/block-host-name-leak.sh +5 -0
  25. package/plugins/lisa-copilot/hooks/host-name-command-context.mjs +409 -0
  26. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cursor/hooks/block-host-name-leak.mjs +130 -40
  28. package/plugins/lisa-cursor/hooks/block-host-name-leak.sh +5 -0
  29. package/plugins/lisa-cursor/hooks/host-name-command-context.mjs +409 -0
  30. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  32. package/plugins/lisa-devin/hooks/block-host-name-leak.mjs +130 -40
  33. package/plugins/lisa-devin/hooks/block-host-name-leak.sh +5 -0
  34. package/plugins/lisa-devin/hooks/host-name-command-context.mjs +409 -0
  35. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  37. package/plugins/lisa-expo-agy/plugin.json +1 -1
  38. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  45. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  52. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  56. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  59. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  63. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  66. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-rails-agy/plugin.json +1 -1
  73. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  79. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  80. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  84. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  86. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  87. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  91. package/plugins/src/base/hooks/block-host-name-leak.mjs +130 -40
  92. package/plugins/src/base/hooks/block-host-name-leak.sh +5 -0
  93. package/plugins/src/base/hooks/host-name-command-context.mjs +409 -0
  94. package/scripts/generate-agy-plugin-artifacts.mjs +7 -3
  95. package/scripts/lib/host-name-expanded-command-cases.mjs +286 -0
  96. package/scripts/lib/host-name-field-occurrence-cases.mjs +60 -0
  97. package/scripts/lib/host-name-review-cases.mjs +354 -0
  98. package/scripts/lib/host-name-target-cases.mjs +298 -0
  99. package/scripts/two-channel-couplings.json +29 -6
  100. package/scripts/verify-host-name-target-scope.mjs +373 -0
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env node
2
+ /** Actual-wrapper target-scope proof; commands under inspection are never executed. */
3
+ import assert from "node:assert/strict";
4
+ import { createHash } from "node:crypto";
5
+ import {
6
+ existsSync,
7
+ mkdirSync,
8
+ mkdtempSync,
9
+ readFileSync,
10
+ realpathSync,
11
+ rmSync,
12
+ symlinkSync,
13
+ writeFileSync,
14
+ } from "node:fs";
15
+ import { tmpdir } from "node:os";
16
+ import path from "node:path";
17
+ import { boundedExecFileSync, boundedSpawnSync } from "./lib/bounded-spawn.mjs";
18
+ import { hostNameTargetCases } from "./lib/host-name-target-cases.mjs";
19
+ import { hostNameReviewCases } from "./lib/host-name-review-cases.mjs";
20
+ import {
21
+ hostNameExpandedCommandCases,
22
+ verifyExpandedCommandArgv,
23
+ } from "./lib/host-name-expanded-command-cases.mjs";
24
+ import { hostNameFieldOccurrenceCases } from "./lib/host-name-field-occurrence-cases.mjs";
25
+
26
+ const root = process.cwd();
27
+ const DETECTOR_RELATIVE = "dist/core/downstream-references.js";
28
+ const fixtureName = "notarealhostproject";
29
+ const lisa = "CodySwannGT/lisa";
30
+ const host = `acme/${fixtureName}`;
31
+ const scratch = mkdtempSync(path.join(tmpdir(), "lisa-host-scope-"));
32
+ const historical = process.argv
33
+ .find(arg => arg.startsWith("--historical-ref="))
34
+ ?.split("=")[1];
35
+ const hookFlag = process.argv
36
+ .find(arg => arg.startsWith("--hooks="))
37
+ ?.slice("--hooks=".length);
38
+ const packageFlag = process.argv
39
+ .find(arg => arg.startsWith("--package-root="))
40
+ ?.slice("--package-root=".length);
41
+ const payloadRoot = path.resolve(packageFlag ?? root);
42
+ const baseEnv = Object.fromEntries(
43
+ Object.entries(process.env).filter(
44
+ ([key]) => !/^(?:GIT_|GH_|GITHUB_)/u.test(key)
45
+ )
46
+ );
47
+ const fixtureEnv = {
48
+ ...baseEnv,
49
+ GH_CONFIG_DIR: path.join(scratch, "gh-config"),
50
+ GH_TOKEN: "local-fixture-no-network-token",
51
+ GH_PROMPT_DISABLED: "1",
52
+ CDPATH: "",
53
+ GIT_CONFIG_GLOBAL: "/dev/null",
54
+ GIT_CONFIG_SYSTEM: "/dev/null",
55
+ LISA_DOWNSTREAM_NAMES: fixtureName,
56
+ };
57
+ mkdirSync(fixtureEnv.GH_CONFIG_DIR);
58
+
59
+ /** @param {string} file @returns {string} Exact byte digest. */
60
+ const digest = file =>
61
+ createHash("sha256").update(readFileSync(file)).digest("hex");
62
+ /** @param {string} cwd @param {string[]} args @returns {string} Local Git output. */
63
+ const git = (cwd, args) =>
64
+ boundedExecFileSync("git", ["-C", cwd, ...args], {
65
+ env: fixtureEnv,
66
+ encoding: "utf8",
67
+ stdio: ["ignore", "pipe", "pipe"],
68
+ }).trim();
69
+ /** @param {string} name @param {string|undefined} origin @returns {string} Isolated checkout. */
70
+ function checkout(name, origin) {
71
+ const cwd = path.join(scratch, name);
72
+ mkdirSync(path.join(cwd, "node_modules/@codyswann/lisa"), {
73
+ recursive: true,
74
+ });
75
+ symlinkSync(
76
+ path.join(payloadRoot, "dist"),
77
+ path.join(cwd, "node_modules/@codyswann/lisa/dist")
78
+ );
79
+ assert.equal(
80
+ realpathSync(path.join(cwd, "node_modules/@codyswann/lisa/dist")),
81
+ realpathSync(path.join(payloadRoot, "dist")),
82
+ "fixture must load the selected payload detector, never a checkout substitute"
83
+ );
84
+ git(cwd, ["init", "-q"]);
85
+ if (origin !== undefined)
86
+ git(cwd, ["remote", "add", "origin", `git@github.com:${origin}.git`]);
87
+ return cwd;
88
+ }
89
+
90
+ const records = [];
91
+ try {
92
+ assert.ok(
93
+ existsSync(path.join(payloadRoot, DETECTOR_RELATIVE)),
94
+ "actual compiled detector must exist; build/access is not replaced by a fixture"
95
+ );
96
+ assert.ok(
97
+ !(packageFlag && historical),
98
+ "Published payload replay cannot mix historical hooks with package detector bytes"
99
+ );
100
+ let hooks = path.resolve(
101
+ payloadRoot,
102
+ hookFlag ?? (packageFlag ? "plugins/lisa/hooks" : "plugins/src/base/hooks")
103
+ );
104
+ if (historical !== undefined) {
105
+ hooks = path.join(scratch, "historical-hooks");
106
+ mkdirSync(hooks);
107
+ for (const file of [
108
+ "block-host-name-leak.mjs",
109
+ "block-host-name-leak.sh",
110
+ ]) {
111
+ writeFileSync(
112
+ path.join(hooks, file),
113
+ boundedExecFileSync(
114
+ "git",
115
+ ["-C", root, "show", `${historical}:plugins/src/base/hooks/${file}`],
116
+ { env: fixtureEnv, stdio: ["ignore", "pipe", "pipe"] }
117
+ )
118
+ );
119
+ }
120
+ }
121
+ const wrapper = path.join(hooks, "block-host-name-leak.sh");
122
+ const classifier = path.join(hooks, "block-host-name-leak.mjs");
123
+ const identity = {
124
+ proverCheckoutHead: git(root, ["rev-parse", "HEAD"]),
125
+ proverSha256: digest(new URL(import.meta.url)),
126
+ caseModules: [
127
+ "host-name-target-cases.mjs",
128
+ "host-name-review-cases.mjs",
129
+ "host-name-expanded-command-cases.mjs",
130
+ "host-name-field-occurrence-cases.mjs",
131
+ ].map(file => ({
132
+ file,
133
+ sha256: digest(new URL(`./lib/${file}`, import.meta.url)),
134
+ })),
135
+ payloadRoot,
136
+ payloadPackageVersion: JSON.parse(
137
+ readFileSync(path.join(payloadRoot, "package.json"), "utf8")
138
+ ).version,
139
+ payloadPackageJsonSha256: digest(path.join(payloadRoot, "package.json")),
140
+ detectorRealpath: realpathSync(path.join(payloadRoot, DETECTOR_RELATIVE)),
141
+ historicalRef: historical ?? null,
142
+ wrapperSha256: digest(wrapper),
143
+ classifierSha256: digest(classifier),
144
+ contextHelperSha256: existsSync(
145
+ path.join(hooks, "host-name-command-context.mjs")
146
+ )
147
+ ? digest(path.join(hooks, "host-name-command-context.mjs"))
148
+ : null,
149
+ detectorSha256: digest(path.join(payloadRoot, DETECTOR_RELATIVE)),
150
+ observation:
151
+ "Actual Bash wrapper and MJS CLI with real compiled detector; synthetic name via documented environment extension. Inspected tracker writes are never executed; no GitHub API request is made.",
152
+ };
153
+ console.log(JSON.stringify({ identity }));
154
+ const hostCwd = checkout("host", host);
155
+ const lisaCwd = checkout("protected", lisa);
156
+ const unknownCwd = checkout("unknown", undefined);
157
+ const body = path.join(scratch, "body.md");
158
+ writeFileSync(body, `Work-Item: ${host}#1\n`);
159
+ const relative = path.join(lisaCwd, "subdirectory");
160
+ mkdirSync(relative);
161
+ writeFileSync(path.join(lisaCwd, "body.md"), "No identity here.\n");
162
+ writeFileSync(
163
+ path.join(relative, "body.md"),
164
+ `Evidence from ${fixtureName}.\n`
165
+ );
166
+ const selectedLisa = checkout("selected-protected", host);
167
+ git(selectedLisa, [
168
+ "remote",
169
+ "add",
170
+ "upstream",
171
+ `git@github.com:${lisa}.git`,
172
+ ]);
173
+ git(selectedLisa, ["config", "remote.upstream.gh-resolved", "base"]);
174
+ const selectedHost = checkout("selected-host", lisa);
175
+ git(selectedHost, [
176
+ "remote",
177
+ "add",
178
+ "upstream",
179
+ `git@github.com:${host}.git`,
180
+ ]);
181
+ git(selectedHost, ["config", "remote.upstream.gh-resolved", "base"]);
182
+ for (const [cwd, expected] of [
183
+ [selectedLisa, lisa],
184
+ [selectedHost, host],
185
+ ]) {
186
+ const selected = boundedExecFileSync(
187
+ "gh",
188
+ ["repo", "set-default", "--view"],
189
+ {
190
+ cwd,
191
+ env: fixtureEnv,
192
+ encoding: "utf8",
193
+ stdio: ["ignore", "pipe", "pipe"],
194
+ }
195
+ ).trim();
196
+ assert.equal(
197
+ selected.toLowerCase(),
198
+ expected.toLowerCase(),
199
+ "real gh local default-repository read must confirm the fixture"
200
+ );
201
+ console.log(
202
+ JSON.stringify({ localDefaultControl: path.basename(cwd), selected })
203
+ );
204
+ }
205
+ const quotedHost = checkout("host; quoted-directory", host);
206
+ const ambiguous = checkout("ambiguous", host);
207
+ git(ambiguous, ["remote", "add", "upstream", `git@github.com:${lisa}.git`]);
208
+ const cdStart = checkout("cd-start", host);
209
+ const cdHost = checkout("cd-start/target", host);
210
+ const cdProtected = checkout("cd-search/target", lisa);
211
+ const cdSearch = path.dirname(cdProtected);
212
+ writeFileSync(path.join(cdHost, "body.md"), "No identity here.\n");
213
+ writeFileSync(path.join(cdProtected, "body.md"), fixtureName);
214
+ const actualCd = boundedExecFileSync(
215
+ "/bin/bash",
216
+ ["-c", 'cd target && printf "%s" "$PWD"'],
217
+ { cwd: cdStart, env: { ...fixtureEnv, CDPATH: cdSearch }, encoding: "utf8" }
218
+ );
219
+ assert.ok(
220
+ actualCd.endsWith(cdProtected),
221
+ "real Bash must confirm CDPATH fixture routing"
222
+ );
223
+ console.log(
224
+ JSON.stringify({ localCdpathControl: { actualCd, expected: cdProtected } })
225
+ );
226
+ const actualArgv = boundedExecFileSync(
227
+ "/bin/bash",
228
+ [
229
+ "-c",
230
+ 'gh() { printf "%s\\n" "$@"; }; gh issue comment 1 --repo "$HOST_REPOSITORY" $ROUTING --body "$KNOWN_NAME"',
231
+ ],
232
+ {
233
+ cwd: hostCwd,
234
+ env: {
235
+ ...fixtureEnv,
236
+ HOST_REPOSITORY: host,
237
+ ROUTING: `--repo ${lisa}`,
238
+ KNOWN_NAME: fixtureName,
239
+ },
240
+ encoding: "utf8",
241
+ }
242
+ )
243
+ .trim()
244
+ .split("\n");
245
+ assert.deepEqual(actualArgv, [
246
+ "issue",
247
+ "comment",
248
+ "1",
249
+ "--repo",
250
+ host,
251
+ "--repo",
252
+ lisa,
253
+ "--body",
254
+ fixtureName,
255
+ ]);
256
+ console.log(
257
+ JSON.stringify({
258
+ localArgvControl: { actualArgv, inspectedWriteExecuted: false },
259
+ })
260
+ );
261
+ const continuation = "\\\n";
262
+ for (const [name, command, expected] of [
263
+ [
264
+ "continuation removes no real argument",
265
+ `gh issue ${continuation} create --body ${continuation} "$KNOWN_NAME"`,
266
+ ["issue", "create", "--body", fixtureName],
267
+ ],
268
+ [
269
+ "quoted empty word survives continuation",
270
+ `gh issue create --body ""${continuation} "$KNOWN_NAME"`,
271
+ ["issue", "create", "--body", "", fixtureName],
272
+ ],
273
+ ]) {
274
+ const argv = boundedExecFileSync(
275
+ "/bin/bash",
276
+ ["-c", `gh() { printf "%s\\n" "$@"; }; ${command}`],
277
+ {
278
+ cwd: hostCwd,
279
+ env: { ...fixtureEnv, KNOWN_NAME: fixtureName },
280
+ encoding: "utf8",
281
+ }
282
+ )
283
+ .trim()
284
+ .split("\n");
285
+ assert.deepEqual(argv, expected, name);
286
+ console.log(
287
+ JSON.stringify({
288
+ localContinuationArgvControl: {
289
+ name,
290
+ argv,
291
+ inspectedWriteExecuted: false,
292
+ },
293
+ })
294
+ );
295
+ }
296
+ const context = {
297
+ hostCwd,
298
+ lisaCwd,
299
+ unknownCwd,
300
+ body,
301
+ selectedLisa,
302
+ selectedHost,
303
+ relative,
304
+ quotedHost,
305
+ ambiguous,
306
+ fixtureName,
307
+ lisa,
308
+ host,
309
+ cdStart,
310
+ cdSearch,
311
+ cdHost,
312
+ };
313
+ const cases = [
314
+ ...hostNameTargetCases(context),
315
+ ...hostNameReviewCases(context),
316
+ ...hostNameExpandedCommandCases(context),
317
+ ...hostNameFieldOccurrenceCases(context),
318
+ ];
319
+ verifyExpandedCommandArgv(context, fixtureEnv);
320
+ for (const [
321
+ name,
322
+ cwd,
323
+ command,
324
+ expected,
325
+ overrides = {},
326
+ refusal = "BLOCKED: this tracker write names a host project.",
327
+ ] of cases) {
328
+ const run = boundedSpawnSync("/bin/bash", [wrapper], {
329
+ cwd,
330
+ input: JSON.stringify({ tool_name: "Bash", tool_input: { command } }),
331
+ env: {
332
+ ...fixtureEnv,
333
+ ...overrides,
334
+ CLAUDE_PROJECT_DIR: overrides.CLAUDE_PROJECT_DIR ?? cwd,
335
+ },
336
+ encoding: "utf8",
337
+ stdio: ["pipe", "pipe", "pipe"],
338
+ });
339
+ const output = `${run.stdout ?? ""}${run.stderr ?? ""}`;
340
+ assert.ok(
341
+ !output.includes("NOT active"),
342
+ `${name}: a missing detector/interpreter is not a passing control`
343
+ );
344
+ assert.ok(
345
+ run.status === 0 || run.status === 2,
346
+ `${name}: expected an actual hook verdict, got ${run.status}`
347
+ );
348
+ const passed =
349
+ run.status === expected && (expected !== 2 || output.includes(refusal));
350
+ records.push({
351
+ name,
352
+ cwd,
353
+ command,
354
+ overrides,
355
+ expected,
356
+ actual: run.status,
357
+ passed,
358
+ output,
359
+ });
360
+ console.log(JSON.stringify(records.at(-1)));
361
+ }
362
+ const failed = records.filter(record => !record.passed);
363
+ console.log(
364
+ JSON.stringify({
365
+ total: records.length,
366
+ passed: records.length - failed.length,
367
+ failed: failed.length,
368
+ })
369
+ );
370
+ if (failed.length > 0) process.exitCode = 1;
371
+ } finally {
372
+ rmSync(scratch, { recursive: true, force: true });
373
+ }