@codyswann/lisa 4.45.7 → 4.45.8

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 (80) hide show
  1. package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +21 -2
  2. package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +68 -0
  3. package/all/merge/.claude/settings.json +10 -0
  4. package/dist/configs/vitest/scratch-global-setup.d.ts +9 -3
  5. package/dist/configs/vitest/scratch-global-setup.d.ts.map +1 -1
  6. package/dist/configs/vitest/scratch-global-setup.js +23 -10
  7. package/dist/configs/vitest/scratch-global-setup.js.map +1 -1
  8. package/dist/configs/vitest/scratch-tmpdir-trust.d.ts +48 -0
  9. package/dist/configs/vitest/scratch-tmpdir-trust.d.ts.map +1 -0
  10. package/dist/configs/vitest/scratch-tmpdir-trust.js +194 -0
  11. package/dist/configs/vitest/scratch-tmpdir-trust.js.map +1 -0
  12. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  13. package/dist/core/lisa-owned-hash-ledger.js +6 -0
  14. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  15. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  16. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  17. package/dist/core/upstream-evidence-manifest.js +13 -8
  18. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  19. package/package.json +5 -5
  20. package/plugins/lisa/.claude-plugin/plugin.json +9 -1
  21. package/plugins/lisa/.codex-plugin/hooks.json +8 -0
  22. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  23. package/plugins/lisa/hooks/worktree-binding-guard.mjs +68 -0
  24. package/plugins/lisa-agy/plugin.json +1 -1
  25. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  26. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  27. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  28. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  30. package/plugins/lisa-copilot/.claude-plugin/plugin.json +9 -1
  31. package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +68 -0
  32. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cursor/hooks/hooks.json +3 -0
  34. package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +68 -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-harper-fabric/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  42. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  43. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  48. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  52. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  53. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  57. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  58. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-rails-agy/plugin.json +1 -1
  63. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  67. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  68. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  72. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  73. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  75. package/plugins/src/base/.claude-plugin/plugin.json +8 -0
  76. package/plugins/src/base/hooks/worktree-binding-guard.mjs +68 -0
  77. package/scripts/check-shell-guard-refusal-coverage.mjs +49 -3
  78. package/scripts/lib/shell-guard-trace.mjs +26 -8
  79. package/scripts/lisa-enforcement-fallback.sh +21 -2
  80. package/typescript/copy-overwrite/scripts/lisa-mutation.mjs +244 -3
@@ -483,6 +483,25 @@ notice_marker=""
483
483
  # non-sticky shared TMPDIR: another user could replace that parent between the
484
484
  # checks below. Root- or caller-owned parents are acceptable; any parent that
485
485
  # grants group or other write access must also carry the sticky bit.
486
+ #
487
+ # The two `stat` spellings must be asked for the SAME twelve bits, and getting
488
+ # that wrong is invisible because each platform only ever runs its own branch.
489
+ # BSD `%Lp` renders the low NINE bits and drops setuid/setgid/sticky entirely:
490
+ #
491
+ # stat -f '%Lp' <a 1777 dir> -> 777 <- sticky gone
492
+ # stat -f '%p' <a 1777 dir> -> 41777 <- sticky present, plus file type
493
+ # stat -c '%a' <a 1777 dir> -> 1777 <- sticky present (GNU includes it)
494
+ #
495
+ # So under `%Lp` the sticky test below is `777 & 1000`, which is zero for every
496
+ # directory on the machine — the exemption was unreachable on macOS and the
497
+ # whole `&& [ ... 8#1000 ] -eq 0` clause was dead code there. The same logical
498
+ # root was therefore trusted on Linux and rejected on macOS, and since CI is
499
+ # Linux the stricter platform was the one nobody specified and nobody tests on
500
+ # (CodySwannGT/lisa#3691).
501
+ #
502
+ # `%p` restores the bit and adds the file type above it, so the mask normalizes
503
+ # both spellings to one value and the arithmetic below has a single meaning:
504
+ # `41777 & 7777` and `1777 & 7777` are both 1777.
486
505
  notice_directory_trusted() {
487
506
  local directory="$1"
488
507
  local directory_stat=""
@@ -490,7 +509,7 @@ notice_directory_trusted() {
490
509
  local directory_mode=""
491
510
  local directory_mode_value=0
492
511
 
493
- directory_stat="$(stat -f '%u %Lp' "$directory" 2>/dev/null)" || \
512
+ directory_stat="$(stat -f '%u %p' "$directory" 2>/dev/null)" || \
494
513
  directory_stat="$(stat -c '%u %a' "$directory" 2>/dev/null)" || return 1
495
514
  directory_owner="${directory_stat%% *}"
496
515
  directory_mode="${directory_stat#* }"
@@ -498,7 +517,7 @@ notice_directory_trusted() {
498
517
  *[!0-9:]* | :* | *: ) return 1 ;;
499
518
  esac
500
519
  [ "$directory_owner" = "0" ] || [ "$directory_owner" = "$notice_uid" ] || return 1
501
- directory_mode_value=$((8#$directory_mode))
520
+ directory_mode_value=$((8#$directory_mode & 8#7777))
502
521
  if [ $((directory_mode_value & 8#0022)) -ne 0 ] && \
503
522
  [ $((directory_mode_value & 8#1000)) -eq 0 ]; then
504
523
  return 1
@@ -550,6 +550,63 @@ function claimedRoot(toolInput, cwd) {
550
550
  return null;
551
551
  }
552
552
 
553
+ /**
554
+ * Record where the session STARTED, so its first guarded call is checked
555
+ * against a baseline rather than establishing one.
556
+ *
557
+ * ## Why the first call could not be checked before
558
+ *
559
+ * It established the baseline instead of testing one, which is
560
+ * trust-on-first-use — and TOFU is only as safe as its first use. Here the
561
+ * first use is assigned by the harness, unasked (CodySwannGT/lisa#3864), so the
562
+ * one moment the guard trusted was the moment the session had least control
563
+ * over. A session already sitting in a foreign worktree when it first acted
564
+ * bound to that tree and was never refused, and every later call was then
565
+ * checked faithfully against the wrong baseline — defending the session INTO
566
+ * the wrong worktree rather than out of it (CodySwannGT/lisa#3955).
567
+ *
568
+ * ## Why an earlier observation separates what a cleverer check cannot
569
+ *
570
+ * At the first guarded call, "I was displaced before I acted" and "I
571
+ * legitimately started here" present identically: one session id, one observed
572
+ * root, no prior state. No comparison can tell them apart, because the
573
+ * information is not in the hook's inputs at that instant.
574
+ *
575
+ * At session start it is. The displacement mechanism actually observed is a
576
+ * PEER's `EnterWorktree` moving this session (CodySwannGT/lisa#3712), which
577
+ * happens during the session — after it has started. A baseline taken at start
578
+ * is therefore recorded BEFORE the event that moves it, and the first guarded
579
+ * call becomes an ordinary comparison against a baseline that already exists.
580
+ * It reaches the same refusal a later displacement produces, because by then it
581
+ * is one.
582
+ *
583
+ * ## What this does NOT achieve
584
+ *
585
+ * #3955 asks for a baseline from the ASSIGNER rather than from observation.
586
+ * That is not what this is and Lisa cannot build it: Lisa does not own
587
+ * `EnterWorktree`, and nothing the harness writes names a session's assigned
588
+ * worktree in a form a hook can read. This is still an observation, just an
589
+ * earlier one. It closes the window between session start and the first guarded
590
+ * call; it leaves open a session LAUNCHED already displaced, where start
591
+ * observes the foreign tree and trust-on-first-use still applies.
592
+ *
593
+ * Returns nothing on purpose, like its sibling below: SessionStart has no call
594
+ * to refuse, so every path through it allows.
595
+ * @param sessionId - The session this baseline belongs to
596
+ * @param observed - The worktree root the session started in
597
+ */
598
+ function recordBaseline(sessionId, observed) {
599
+ // Never overwrite. A resumed session, a reconnect, or anything else that
600
+ // fires the event twice must not be able to launder a displaced binding into
601
+ // a fresh baseline — which is the whole failure this exists to close.
602
+ if (readState(sessionId)?.boundRoot) return;
603
+ writeState(sessionId, {
604
+ boundRoot: observed,
605
+ claimedRoot: null,
606
+ updatedAt: new Date().toISOString(),
607
+ });
608
+ }
609
+
553
610
  /**
554
611
  * Record what `EnterWorktree` just claimed, for the next tool call to check.
555
612
  *
@@ -651,6 +708,10 @@ function evaluate(payload) {
651
708
  const observed = worktreeRoot(cwd);
652
709
  if (!observed) return 0;
653
710
 
711
+ if (payload.hook_event_name === "SessionStart") {
712
+ recordBaseline(sessionId, observed);
713
+ return 0;
714
+ }
654
715
  if (payload.tool_name === "EnterWorktree") {
655
716
  recordClaim(payload, observed);
656
717
  return 0;
@@ -662,6 +723,13 @@ function evaluate(payload) {
662
723
 
663
724
  const state = readState(sessionId);
664
725
  if (!state?.boundRoot) {
726
+ // No baseline means SessionStart did not run for this session — an older
727
+ // install, a runtime that fires no such event, a harness that skips it.
728
+ // Absence of a baseline is absence of evidence, not evidence of
729
+ // displacement, so this records one exactly as it always did rather than
730
+ // refusing. Treating it as a refusal would turn a floor into a wall on
731
+ // every surface where the event does not fire, and a wall gets switched
732
+ // off, which costs the whole guard.
665
733
  writeState(sessionId, {
666
734
  boundRoot: observed,
667
735
  claimedRoot: null,
@@ -67,6 +67,16 @@
67
67
  }
68
68
  ]
69
69
  }
70
+ ],
71
+ "SessionStart": [
72
+ {
73
+ "hooks": [
74
+ {
75
+ "type": "command",
76
+ "command": "bash \"$CLAUDE_PROJECT_DIR\"/scripts/lisa-hooks/worktree-binding-guard.sh"
77
+ }
78
+ ]
79
+ }
70
80
  ]
71
81
  }
72
82
  }
@@ -224,13 +224,19 @@ export declare const armRefusalSummary: (failure: string, write?: (text: string)
224
224
  */
225
225
  export declare const announceRefusal: (failure: string, writeNotice?: (text: string) => void) => void;
226
226
  /**
227
- * Reclaims residue from previous runs, then refuses to start into a namespace
228
- * that is accumulating.
227
+ * Refuses to start into an untrusted temp root or an accumulating namespace.
229
228
  *
230
229
  * A refusal speaks twice — a banner before collection and a summary line at
231
230
  * exit — and the two are not redundant. They are the top and the bottom of a
232
231
  * transcript nobody reads in full.
233
- * @throws {Error} When residue is present that the sweep cannot or did not reclaim.
232
+ *
233
+ * The TMPDIR trust check runs FIRST, and the order is load-bearing rather than
234
+ * arbitrary. An untrusted root is the more fundamental fault: it is a property
235
+ * of the environment the run was launched in, it is what every rung of the
236
+ * namespace audit is then inspected THROUGH, and it is the one whose absence
237
+ * of a message cost four separate investigations (CodySwannGT/lisa#3691).
238
+ * Reporting a namespace symptom first would name the second-order fact.
239
+ * @throws {Error} When the temp root is untrusted, or residue the sweep cannot or did not reclaim is present.
234
240
  */
235
241
  export declare const setup: () => void;
236
242
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"scratch-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"AAmDA,OAAO,EAGL,+BAA+B,EAGhC,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB;AAC5B,kDAAkD;AAChD,eAAe;AACjB,0EAA0E;GACxE,mBAAmB;AACrB,kEAAkE;GAChE,mBAAmB;AACrB,mDAAmD;GACjD,iBAAiB,CAAC;AAEtB,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;CACrC;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,gEAAgE;IAChE,QAAQ,CAAC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC1D,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AA+ED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAA8B,EACnC,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAmC,KACnE,gBAoBF,CAAC;AAwEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,MAAM,EACX,SAAS,gBAAgB,KACxB,MAAM,GAAG,SAuCX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,WAAU,OAAO,+BAAiE,KACjF,gBAQF,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAcxC,CAAC;AAKf;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MAG/B,CAAC;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAQhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,QAAO,CAAC,IAAI,EAAE,MAAM,KAAK,IAExB,KACA,IAIF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EACf,cAAa,CAAC,IAAI,EAAE,MAAM,KAAK,IAE9B,KACA,IAMF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,QAAO,IAexB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,QAAO,IAE3B,CAAC"}
1
+ {"version":3,"file":"scratch-global-setup.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"AAmDA,OAAO,EAGL,+BAA+B,EAGhC,MAAM,oBAAoB,CAAC;AAG5B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB;AAC5B,kDAAkD;AAChD,eAAe;AACjB,0EAA0E;GACxE,mBAAmB;AACrB,kEAAkE;GAChE,mBAAmB;AACrB,mDAAmD;GACjD,iBAAiB,CAAC;AAEtB,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;CACrC;AAED,iDAAiD;AACjD,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,gEAAgE;IAChE,QAAQ,CAAC,kBAAkB,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAC1D,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AA+ED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAA8B,EACnC,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAmC,KACnE,gBAoBF,CAAC;AAwEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,sBAAsB,GACjC,KAAK,MAAM,EACX,SAAS,gBAAgB,KACxB,MAAM,GAAG,SAuCX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAO,CAAC,GAAG,EAAE,MAAM,KAAK,OAAwB,EAChD,WAAU,OAAO,+BAAiE,KACjF,gBAQF,CAAC;AAgBF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM,KAAG,MAcxC,CAAC;AAKf;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,KAAG,MAG/B,CAAC;AAEzB;;;;;GAKG;AACH,eAAO,MAAM,eAAe,mBAAmB,CAAC;AAQhD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,QAAO,CAAC,IAAI,EAAE,MAAM,KAAK,IAExB,KACA,IAIF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAS,MAAM,EACf,cAAa,CAAC,IAAI,EAAE,MAAM,KAAK,IAE9B,KACA,IAMF,CAAC;AAgBF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,KAAK,QAAO,IASxB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,QAAQ,QAAO,IAE3B,CAAC"}
@@ -42,6 +42,7 @@ import * as path from "node:path";
42
42
  import { env } from "node:process";
43
43
  import { isProcessAlive, liveOwnerBirthProbe, readNamespaceEntries, scratchNamespaceDir, sweepScratchNamespace, } from "./scratch.js";
44
44
  import { classifyScratchOwner, processBirthFingerprint, processBirthFingerprintSnapshot, readScratchOwnerRecord, } from "./scratch-owner.js";
45
+ import { describeTmpdirTrustFailure } from "./scratch-tmpdir-trust.js";
45
46
  /**
46
47
  * Upper bound on entries the namespace may hold that **nobody owns**.
47
48
  *
@@ -448,22 +449,34 @@ export const announceRefusal = (failure, writeNotice = text => {
448
449
  armRefusalSummary(failure);
449
450
  };
450
451
  /**
451
- * Reclaims residue from previous runs, then refuses to start into a namespace
452
- * that is accumulating.
453
- *
454
- * A refusal speaks twice — a banner before collection and a summary line at
455
- * exit — and the two are not redundant. They are the top and the bottom of a
456
- * transcript nobody reads in full.
457
- * @throws {Error} When residue is present that the sweep cannot or did not reclaim.
452
+ * Why the namespace must be refused, or `undefined` when it is usable.
453
+ * @returns Refusal text, or undefined
458
454
  */
459
- export const setup = () => {
455
+ const describeNamespaceFailure = () => {
460
456
  // The namespace directory is not created here. `createRunRoot` makes it
461
457
  // recursively in whichever worker allocates first, and both the sweep and the
462
458
  // inspection treat an absent namespace as an empty one — so creating it would
463
459
  // be a side effect ahead of the audit that buys nothing.
464
460
  const dir = scratchNamespaceDir();
465
- const residue = auditNamespace();
466
- const failure = describeResidueFailure(dir, residue);
461
+ return describeResidueFailure(dir, auditNamespace());
462
+ };
463
+ /**
464
+ * Refuses to start into an untrusted temp root or an accumulating namespace.
465
+ *
466
+ * A refusal speaks twice — a banner before collection and a summary line at
467
+ * exit — and the two are not redundant. They are the top and the bottom of a
468
+ * transcript nobody reads in full.
469
+ *
470
+ * The TMPDIR trust check runs FIRST, and the order is load-bearing rather than
471
+ * arbitrary. An untrusted root is the more fundamental fault: it is a property
472
+ * of the environment the run was launched in, it is what every rung of the
473
+ * namespace audit is then inspected THROUGH, and it is the one whose absence
474
+ * of a message cost four separate investigations (CodySwannGT/lisa#3691).
475
+ * Reporting a namespace symptom first would name the second-order fact.
476
+ * @throws {Error} When the temp root is untrusted, or residue the sweep cannot or did not reclaim is present.
477
+ */
478
+ export const setup = () => {
479
+ const failure = describeTmpdirTrustFailure() ?? describeNamespaceFailure();
467
480
  if (failure !== undefined) {
468
481
  // Both halves of the announcement happen before the throw, because the
469
482
  // throw is what ends this function.
@@ -1 +1 @@
1
- {"version":3,"file":"scratch-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scratch-global-setup.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-global-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /** The rung of the chain that failed the trust test, and why it failed. */
2
+ export interface UntrustedAncestor {
3
+ /** Absolute path of the rejected directory. */
4
+ readonly directory: string;
5
+ /** Owning uid, as reported by `stat`. */
6
+ readonly uid: number;
7
+ /** Permission, setuid/setgid and sticky bits. */
8
+ readonly mode: number;
9
+ /** Operator-readable statement of which property failed. */
10
+ readonly reason: string;
11
+ }
12
+ /**
13
+ * Render a mode the way `ls` and `chmod` spell it, so it can be compared.
14
+ * @param mode - Permission bits
15
+ * @returns Four-digit octal string
16
+ */
17
+ export declare const octalMode: (mode: number) => string;
18
+ /**
19
+ * Every ancestor of an absolute path, root first and the path itself last.
20
+ * @param target - Absolute, symlink-resolved path
21
+ * @returns The chain to walk, in the order the shell walks it
22
+ */
23
+ export declare const ancestorChain: (target: string) => readonly string[];
24
+ /**
25
+ * The first untrusted ancestor of a path, if any.
26
+ *
27
+ * Platforms without uid ownership have nothing to check and are never refused.
28
+ * @param target - Absolute, symlink-resolved path to walk
29
+ * @returns The first failing rung, or undefined
30
+ */
31
+ export declare const findUntrustedAncestor: (target: string) => UntrustedAncestor | undefined;
32
+ /**
33
+ * The refusal text: what was rejected, which rung rejected it, and the fix.
34
+ * @param resolved - The symlink-resolved TMPDIR that was walked
35
+ * @param ancestor - The rung that failed
36
+ * @returns Operator-readable failure text
37
+ */
38
+ export declare const describeUntrustedTmpdir: (resolved: string, ancestor: UntrustedAncestor) => string;
39
+ /**
40
+ * Why this run's TMPDIR must be refused, or `undefined` when it is trusted.
41
+ *
42
+ * Read through `realpathSync` because the shell resolves with `cd -P` before it
43
+ * walks: the chain that matters is the physical one, and a symlinked TMPDIR
44
+ * whose logical spelling looks fine can resolve onto a rung that is not.
45
+ * @returns Refusal text, or undefined when the chain is trusted
46
+ */
47
+ export declare const describeTmpdirTrustFailure: () => string | undefined;
48
+ //# sourceMappingURL=scratch-tmpdir-trust.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-tmpdir-trust.d.ts","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-tmpdir-trust.ts"],"names":[],"mappings":"AA0DA,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,yCAAyC;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,GAAI,MAAM,MAAM,KAAG,MACN,CAAC;AAEpC;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,KAAG,SAAS,MAAM,EAUzD,CAAC;AAuEN;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAChC,QAAQ,MAAM,KACb,iBAAiB,GAAG,SAItB,CAAC;AAqBF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAClC,UAAU,MAAM,EAChB,UAAU,iBAAiB,KAC1B,MAuBW,CAAC;AAEf;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,QAAO,MAAM,GAAG,SAOtD,CAAC"}
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Vitest Configuration - TMPDIR ancestor-chain trust (refusal)
3
+ *
4
+ * A test run under a `TMPDIR` whose ancestor chain is untrusted produces four
5
+ * failing assertions about enforcement rate-limiting and says nothing about the
6
+ * environment that caused them. Nobody's first hypothesis is "my temp
7
+ * directory's ancestor has the wrong mode bits", so the failure reads as a
8
+ * defect in the rate limiter — measured at four separate investigations, two of
9
+ * which escalated claiming `main` was red (CodySwannGT/lisa#3691).
10
+ *
11
+ * The mechanism is `scripts/lisa-enforcement-fallback.sh`, which walks every
12
+ * ancestor of the resolved `TMPDIR` before it writes its once-per-session
13
+ * marker. When a rung fails the walk the marker is never created, the rate
14
+ * limit stands down, and the notice speaks on every call. THAT STAND-DOWN IS
15
+ * CORRECT — the guard is designed to fail open rather than write into an
16
+ * untrusted state directory, and it should keep doing that. What was missing is
17
+ * anything telling the operator why, which is what this module supplies.
18
+ *
19
+ * The verdict is deliberately a REFUSAL rather than a warning, and it is wired
20
+ * into `globalSetup` rather than `setupFiles` for a measured reason: a throw
21
+ * from a `setupFiles` module fails collection, so the suite reports zero tests
22
+ * and the gate goes red having evaluated nothing — a result that looks like a
23
+ * verdict and is not one. Installing that failure to fix a failure that names
24
+ * nothing would reproduce the defect one level up. `scratch-global-setup`
25
+ * already refuses from `setup`, where vitest honours the throw and exits 1.
26
+ *
27
+ * The predicate below is a second expression of the shell's arithmetic, which
28
+ * is a real risk: two copies can drift, and the copy that decides behaviour is
29
+ * the shell's. That risk is priced by a case in
30
+ * `tests/unit/config/scratch-tmpdir-trust.test.ts` asserting the two agree on a
31
+ * table of representative roots, real directories rather than fabricated modes.
32
+ * Sharing the shell predicate directly was rejected: that script is the
33
+ * PreToolUse dispatcher that runs on EVERY tool call and consumes stdin, so a
34
+ * probe mode would put a test-serving branch on the hottest path in the repo.
35
+ * @see {@link module:configs/vitest/scratch-global-setup} for the refusal path
36
+ * @module configs/vitest/scratch-tmpdir-trust
37
+ */
38
+ import { realpathSync, statSync } from "node:fs";
39
+ import { tmpdir } from "node:os";
40
+ import * as path from "node:path";
41
+ import { getuid, platform } from "node:process";
42
+ /** Permission, setuid/setgid and sticky bits of `st_mode`. */
43
+ const PERMISSION_BITS = 0o7777;
44
+ /** Write access granted to group or to other. */
45
+ const SHARED_WRITE_BITS = 0o022;
46
+ /**
47
+ * The sticky bit.
48
+ *
49
+ * It is what makes a shared, world-writable temp root safe to hold per-user
50
+ * state: another uid can still CREATE a name there, but cannot unlink or rename
51
+ * one that is not theirs. That is the whole basis on which `/tmp` is trusted.
52
+ */
53
+ const STICKY_BIT = 0o1000;
54
+ /**
55
+ * Render a mode the way `ls` and `chmod` spell it, so it can be compared.
56
+ * @param mode - Permission bits
57
+ * @returns Four-digit octal string
58
+ */
59
+ export const octalMode = (mode) => mode.toString(8).padStart(4, "0");
60
+ /**
61
+ * Every ancestor of an absolute path, root first and the path itself last.
62
+ * @param target - Absolute, symlink-resolved path
63
+ * @returns The chain to walk, in the order the shell walks it
64
+ */
65
+ export const ancestorChain = (target) => target
66
+ .split(path.sep)
67
+ .filter(segment => segment !== "")
68
+ .reduce((chain, segment) => [
69
+ ...chain,
70
+ path.join(chain[chain.length - 1] ?? path.sep, segment),
71
+ ], [path.sep]);
72
+ /**
73
+ * Why one directory is untrusted, or `undefined` when it is trusted.
74
+ *
75
+ * Ownership is checked as "root or me" rather than "mine". A valid chain is
76
+ * MOSTLY root-owned — on macOS four of the six rungs above the per-user temp
77
+ * root are uid 0 — so a check written as "is this mine?" would reject every
78
+ * valid root on the machine. The discriminating property is the MODE.
79
+ * @param uid - Owning uid of the directory
80
+ * @param mode - Its permission, setuid/setgid and sticky bits
81
+ * @param self - The current uid
82
+ * @returns The failing property, or undefined
83
+ */
84
+ const untrustReason = (uid, mode, self) => {
85
+ if (uid !== 0 && uid !== self) {
86
+ return (`owned by uid ${String(uid)}, which is neither root nor you ` +
87
+ `(uid ${String(self)}), so its owner can replace it underneath you`);
88
+ }
89
+ if ((mode & SHARED_WRITE_BITS) !== 0 && (mode & STICKY_BIT) === 0) {
90
+ return (`mode ${octalMode(mode)} grants write access to group or other ` +
91
+ `WITHOUT the sticky bit, so any user can replace entries in it`);
92
+ }
93
+ return undefined;
94
+ };
95
+ /**
96
+ * The first rung of a chain that fails the trust test.
97
+ *
98
+ * A rung that cannot be inspected at all counts as untrusted, matching the
99
+ * shell, whose `stat` failure returns the same verdict as a bad mode.
100
+ * @param target - Absolute, symlink-resolved path to walk
101
+ * @param self - The current uid
102
+ * @returns The first failing rung, or undefined when every rung passes
103
+ */
104
+ const firstUntrustedRung = (target, self) => ancestorChain(target).reduce((found, directory) => {
105
+ if (found !== undefined)
106
+ return found;
107
+ try {
108
+ const stat = statSync(directory);
109
+ const mode = stat.mode & PERMISSION_BITS;
110
+ const reason = untrustReason(stat.uid, mode, self);
111
+ return reason === undefined
112
+ ? undefined
113
+ : { directory, uid: stat.uid, mode, reason };
114
+ }
115
+ catch (error) {
116
+ return {
117
+ directory,
118
+ uid: -1,
119
+ mode: 0,
120
+ reason: `could not be inspected: ${error instanceof Error ? error.message : String(error)}`,
121
+ };
122
+ }
123
+ }, undefined);
124
+ /**
125
+ * The first untrusted ancestor of a path, if any.
126
+ *
127
+ * Platforms without uid ownership have nothing to check and are never refused.
128
+ * @param target - Absolute, symlink-resolved path to walk
129
+ * @returns The first failing rung, or undefined
130
+ */
131
+ export const findUntrustedAncestor = (target) => {
132
+ const self = getuid?.();
133
+ return self === undefined ? undefined : firstUntrustedRung(target, self);
134
+ };
135
+ /**
136
+ * The remedy lines, spelled for the platform the operator is actually on.
137
+ *
138
+ * A remedy that cannot be followed is worse than none: it reads as the guard
139
+ * being wrong rather than the environment being wrong, and the available move
140
+ * then is to route around the guard.
141
+ * @returns Shell lines that produce a trusted TMPDIR
142
+ */
143
+ const remedyLines = () => platform === "darwin"
144
+ ? [
145
+ ' root="$(getconf DARWIN_USER_TEMP_DIR)"',
146
+ ' mkdir -p "${root}lisa" && export TMPDIR="${root}lisa"',
147
+ ]
148
+ : [
149
+ ' root="${XDG_RUNTIME_DIR:-/tmp}/lisa-$(id -u)"',
150
+ ' mkdir -p "$root" && export TMPDIR="$root"',
151
+ ];
152
+ /**
153
+ * The refusal text: what was rejected, which rung rejected it, and the fix.
154
+ * @param resolved - The symlink-resolved TMPDIR that was walked
155
+ * @param ancestor - The rung that failed
156
+ * @returns Operator-readable failure text
157
+ */
158
+ export const describeUntrustedTmpdir = (resolved, ancestor) => [
159
+ "Lisa test scratch requires a TMPDIR whose ancestor chain is trusted.",
160
+ "",
161
+ ` TMPDIR resolves to ${resolved}`,
162
+ ` rejected at ${ancestor.directory}`,
163
+ ` uid ${String(ancestor.uid)}, mode ${octalMode(ancestor.mode)}`,
164
+ ` ${ancestor.reason}`,
165
+ "",
166
+ "A directory is trusted when it is owned by root or by you AND is not",
167
+ "group- or world-writable unless it carries the sticky bit.",
168
+ "",
169
+ "This is the same chain scripts/lisa-enforcement-fallback.sh walks before",
170
+ "it writes per-session state. A TMPDIR refused here would silently stand",
171
+ "that rate limit down instead, which surfaces as four assertion failures",
172
+ "in tests/unit/hooks/enforcement-fallback-notice-rate.test.ts that read as",
173
+ "defects in the rate limiter and say nothing about your environment. That",
174
+ "is the failure this refusal replaces (CodySwannGT/lisa#3691).",
175
+ "",
176
+ "Point TMPDIR at a directory of your own instead:",
177
+ ...remedyLines(),
178
+ ].join("\n");
179
+ /**
180
+ * Why this run's TMPDIR must be refused, or `undefined` when it is trusted.
181
+ *
182
+ * Read through `realpathSync` because the shell resolves with `cd -P` before it
183
+ * walks: the chain that matters is the physical one, and a symlinked TMPDIR
184
+ * whose logical spelling looks fine can resolve onto a rung that is not.
185
+ * @returns Refusal text, or undefined when the chain is trusted
186
+ */
187
+ export const describeTmpdirTrustFailure = () => {
188
+ const resolved = realpathSync(tmpdir());
189
+ const ancestor = findUntrustedAncestor(resolved);
190
+ return ancestor === undefined
191
+ ? undefined
192
+ : describeUntrustedTmpdir(resolved, ancestor);
193
+ };
194
+ //# sourceMappingURL=scratch-tmpdir-trust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scratch-tmpdir-trust.js","sourceRoot":"","sources":["../../../src/configs/vitest/scratch-tmpdir-trust.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAqlCjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAw/BjC,CAAC"}
1
+ {"version":3,"file":"lisa-owned-hash-ledger.d.ts","sourceRoot":"","sources":["../../src/core/lisa-owned-hash-ledger.ts"],"names":[],"mappings":"AACA,6EAA6E;AAE7E;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAC3C,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAwlCjC,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CACpD,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CA2/BjC,CAAC"}
@@ -449,6 +449,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
449
449
  "7caa48fcba51e1e4bf21b909e51474be3b929907ffc35896b6de13aac6cb6782",
450
450
  "7d333dc10be8c810b9b5aca35ab7f6f60413134414d82bf0cdaedb4e6e3ca8da",
451
451
  "8dc942ac4edc2017d6b8277296c8f80542e7c024eef1bb65de609979d0ca9a0e",
452
+ "916b2b48a2b55c2090468c48c972b6c7c5fad39fb53956ceffebdd1ff0c69230",
452
453
  "9cab71562d01421d57c6719318a4d03a6249a45c1aea434db3ff26e3065eeb66",
453
454
  "9cfad1d691235fa70ccf97603eea2ef177bbded4a34d85aeedc41018c894b591",
454
455
  "aafd027c4b6092da15d0389f9e8dcedbd9105e9083057e2da39f78fd5f72e0df",
@@ -802,6 +803,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
802
803
  "6b0c13bf89dd62577baebb7e6e8ff7a0bbcb1e83b32c5a1b878a64c76d83d822",
803
804
  "822e4f9d2c3ad8fb184ff7fc880f6811004852fb731c7878b5a8a1a9532bceb8",
804
805
  "bc1118b05eaea3fc7b3409c96979d64e3dee0907118f3e84d735163f473f632a",
806
+ "cccc2f5ba33bc3abe03b850d3dd747c27553f985b9213d1b1a35297312fa1586",
805
807
  "e121219bbee1960a6bf88d1e2ba0b50587d63013e7872c56619c04182e8a7561",
806
808
  "efbf0d770c5547e9b7465eac4650c4ff01d427dee86fdb623a463a32023bcabf",
807
809
  ]),
@@ -813,6 +815,7 @@ export const LISA_OWNED_HASH_LEDGER = Object.freeze({
813
815
  ]),
814
816
  "scripts/lisa-mutation.mjs": Object.freeze([
815
817
  "03dda43b5813b5b6a083c1e0302ce3e9fec13818864ed38dce180cbdaa4e2de2",
818
+ "08855e056f353392e8072e8f868335f7fe4bde4f83a5affed4765ff53eea1129",
816
819
  "0cd013a51a81d8eeebced9f2646bdb3fe0a9247fcd74b57c03e95cc2db3fe7de",
817
820
  "0f5d9027e95d47745827adcd979e3c2355271066b6b84d661215dc6e895b3ac2",
818
821
  "0fe73e162ece6c4a8270b0d5d675d6927a62962e0ae9275239c6acfacd0ef6c8",
@@ -1551,6 +1554,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
1551
1554
  "7caa48fcba51e1e4bf21b909e51474be3b929907ffc35896b6de13aac6cb6782",
1552
1555
  "7d333dc10be8c810b9b5aca35ab7f6f60413134414d82bf0cdaedb4e6e3ca8da",
1553
1556
  "8dc942ac4edc2017d6b8277296c8f80542e7c024eef1bb65de609979d0ca9a0e",
1557
+ "916b2b48a2b55c2090468c48c972b6c7c5fad39fb53956ceffebdd1ff0c69230",
1554
1558
  "9cab71562d01421d57c6719318a4d03a6249a45c1aea434db3ff26e3065eeb66",
1555
1559
  "9cfad1d691235fa70ccf97603eea2ef177bbded4a34d85aeedc41018c894b591",
1556
1560
  "aafd027c4b6092da15d0389f9e8dcedbd9105e9083057e2da39f78fd5f72e0df",
@@ -1858,6 +1862,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
1858
1862
  "6b0c13bf89dd62577baebb7e6e8ff7a0bbcb1e83b32c5a1b878a64c76d83d822",
1859
1863
  "822e4f9d2c3ad8fb184ff7fc880f6811004852fb731c7878b5a8a1a9532bceb8",
1860
1864
  "bc1118b05eaea3fc7b3409c96979d64e3dee0907118f3e84d735163f473f632a",
1865
+ "cccc2f5ba33bc3abe03b850d3dd747c27553f985b9213d1b1a35297312fa1586",
1861
1866
  "e121219bbee1960a6bf88d1e2ba0b50587d63013e7872c56619c04182e8a7561",
1862
1867
  "efbf0d770c5547e9b7465eac4650c4ff01d427dee86fdb623a463a32023bcabf",
1863
1868
  ]),
@@ -1868,6 +1873,7 @@ export const LISA_OWNED_HASH_HISTORY_DERIVED = Object.freeze({
1868
1873
  "ce4bc224a102e3ac2bc29d8f2038eefd619129d4639c25954c93f05011f7f977",
1869
1874
  ]),
1870
1875
  "scripts/lisa-mutation.mjs": Object.freeze([
1876
+ "08855e056f353392e8072e8f868335f7fe4bde4f83a5affed4765ff53eea1129",
1871
1877
  "0cd013a51a81d8eeebced9f2646bdb3fe0a9247fcd74b57c03e95cc2db3fe7de",
1872
1878
  "0f5d9027e95d47745827adcd979e3c2355271066b6b84d661215dc6e895b3ac2",
1873
1879
  "0fe73e162ece6c4a8270b0d5d675d6927a62962e0ae9275239c6acfacd0ef6c8",