@codyswann/lisa 4.45.4 → 4.45.6
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/all/copy-overwrite/scripts/lisa-cross-worktree-guard.mjs +6 -0
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +74 -0
- package/all/copy-overwrite/scripts/lisa-hooks/worktree-binding-guard.mjs +115 -23
- package/all/copy-overwrite/scripts/lisa-worktree-guard.mjs +456 -0
- package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
- package/dist/core/lisa-owned-hash-ledger.js +28 -0
- package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +21 -10
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/parity-safety-net.sh +74 -0
- package/package.json +5 -4
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/cleanup-stale-worktrees.sh +42 -1
- package/plugins/lisa/hooks/parity-safety-net.sh +74 -0
- package/plugins/lisa/hooks/worktree-binding-guard.mjs +115 -23
- package/plugins/lisa-agy/hooks/parity-safety-net.sh +74 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/cleanup-stale-worktrees.sh +42 -1
- package/plugins/lisa-copilot/hooks/parity-safety-net.sh +74 -0
- package/plugins/lisa-copilot/hooks/worktree-binding-guard.mjs +115 -23
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/cleanup-stale-worktrees.sh +42 -1
- package/plugins/lisa-cursor/hooks/parity-safety-net.sh +74 -0
- package/plugins/lisa-cursor/hooks/worktree-binding-guard.mjs +115 -23
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/cleanup-stale-worktrees.sh +42 -1
- package/plugins/src/base/hooks/parity-safety-net.sh +74 -0
- package/plugins/src/base/hooks/worktree-binding-guard.mjs +115 -23
- package/scripts/check-cwd-resolution-corpus.mjs +163 -0
- package/scripts/cleanup-worktrees.sh +36 -3
- package/scripts/two-channel-couplings.json +1 -1
- package/typescript/copy-overwrite/.prettierignore +1 -0
- package/typescript/create-only/.github/workflows/review-evidence.yml +20 -1
|
@@ -28,6 +28,12 @@
|
|
|
28
28
|
* proof that none is takeable *right now*. Deleting a worktree that holds
|
|
29
29
|
* uncommitted work permanently forecloses the only window in which its own
|
|
30
30
|
* case was detectable.
|
|
31
|
+
*
|
|
32
|
+
* That limit is now held open from the other end by
|
|
33
|
+
* `lisa-worktree-guard.mjs` (CodySwannGT/lisa#3863), which refuses to remove a
|
|
34
|
+
* worktree whose uncommitted bytes exist in no commit. The two are one control
|
|
35
|
+
* read in two directions: this file needs the original to still be there, and
|
|
36
|
+
* that one is what keeps it there.
|
|
31
37
|
*/
|
|
32
38
|
|
|
33
39
|
import { execFileSync } from "node:child_process";
|
|
@@ -2033,6 +2033,77 @@ readonly GIT_CONTROL_PLANE='(^|[[:space:]='"$qc"'./])\.git([/[:space:]'"$qc"']|$
|
|
|
2033
2033
|
# that could not run produced no lines, the loop body never executed, and the
|
|
2034
2034
|
# guard was skipped with the command ALLOWED. A segmentation that cannot answer
|
|
2035
2035
|
# must refuse, exactly like a scan that cannot answer.
|
|
2036
|
+
# 14b. Linked worktrees, which are the same principle one level out. `.git` is
|
|
2037
|
+
# protected because nothing in the working tree can rebuild it; a linked
|
|
2038
|
+
# worktree is protected because nothing OUTSIDE it can rebuild what it holds.
|
|
2039
|
+
# The asymmetry is exact and it is invisible from the prompt: `.git/refs` and
|
|
2040
|
+
# the object store are shared by every worktree of a repository, while
|
|
2041
|
+
# `.git/index` and the working files are per-worktree. So `git commit` moves
|
|
2042
|
+
# work somewhere a deletion cannot reach, and `git add` does not — a staged file
|
|
2043
|
+
# shows green in `git status`, which reads as saved, and dies with the
|
|
2044
|
+
# directory.
|
|
2045
|
+
#
|
|
2046
|
+
# Measured twice in one evening (issue #3863): one worktree lost a 284-line
|
|
2047
|
+
# staged test file, and a second lost nothing from the identical event purely
|
|
2048
|
+
# because a gate had forced its work into a commit first. Neither deletion
|
|
2049
|
+
# announced itself. That is the second reason this is a block rather than a
|
|
2050
|
+
# warning — a worktree deletion destroys the only evidence its own case could
|
|
2051
|
+
# leave, so there is no after-the-fact detection to fall back on.
|
|
2052
|
+
#
|
|
2053
|
+
# `git worktree remove` (guard 12) already refuses a dirty tree, and
|
|
2054
|
+
# `--force` is already blocked. This closes the spelling that walks around both:
|
|
2055
|
+
# `rm -rf` on the directory, which git never sees.
|
|
2056
|
+
#
|
|
2057
|
+
# Matching is by REGISTERED path — `git worktree list` names them — so an
|
|
2058
|
+
# ordinary directory that merely lives near one is untouched. A repository that
|
|
2059
|
+
# cannot be read answers "no": this guard adds protection to a shape that was
|
|
2060
|
+
# fully allowed a moment ago, and failing it closed would deny every recursive
|
|
2061
|
+
# delete on every path outside a repository.
|
|
2062
|
+
#
|
|
2063
|
+
# The comparison is on WHOLE TOKENS, which is the only spelling that gets both
|
|
2064
|
+
# error directions right. Substring containment would let a worktree named
|
|
2065
|
+
# `wt-1` claim `rm -rf wt-10`, an over-block on an unrelated directory; and
|
|
2066
|
+
# hand-written boundary classes leak the other way, because `rm -rf <wt>/`
|
|
2067
|
+
# with one trailing slash ends on a character every such class treats as
|
|
2068
|
+
# "inside a path". So every non-path character — quote, space, parenthesis,
|
|
2069
|
+
# redirection — is folded to a space first, which makes each argument a
|
|
2070
|
+
# space-delimited token regardless of how it was quoted, and the match is then
|
|
2071
|
+
# an equality test on that token. A SUBPATH (`rm -rf <wt>/node_modules`) is
|
|
2072
|
+
# deliberately not this guard's business: it is not a worktree deletion, and
|
|
2073
|
+
# blocking routine cleanup inside a worktree is how a guard gets switched off.
|
|
2074
|
+
names_linked_worktree() {
|
|
2075
|
+
local stmt="$1" probe root listed line primary wt rel
|
|
2076
|
+
root="$(git -C "${CLAUDE_PROJECT_DIR:-$PWD}" rev-parse --show-toplevel 2> /dev/null)" || return 1
|
|
2077
|
+
[ -n "$root" ] || return 1
|
|
2078
|
+
listed="$(git -C "$root" worktree list --porcelain 2> /dev/null)" || return 1
|
|
2079
|
+
probe=" $(printf '%s' "$stmt" | tr -c 'A-Za-z0-9_./-' ' ') "
|
|
2080
|
+
primary=""
|
|
2081
|
+
while IFS= read -r line; do
|
|
2082
|
+
case "$line" in
|
|
2083
|
+
worktree\ *) wt="${line#worktree }" ;;
|
|
2084
|
+
*) continue ;;
|
|
2085
|
+
esac
|
|
2086
|
+
# First entry is the primary checkout, which is not a linked worktree.
|
|
2087
|
+
if [ -z "$primary" ]; then
|
|
2088
|
+
primary="$wt"
|
|
2089
|
+
continue
|
|
2090
|
+
fi
|
|
2091
|
+
case "$probe" in
|
|
2092
|
+
*" $wt "* | *" $wt/ "*) return 0 ;;
|
|
2093
|
+
esac
|
|
2094
|
+
rel="${wt#"$primary"/}"
|
|
2095
|
+
[ "$rel" != "$wt" ] || continue
|
|
2096
|
+
# `./` is the same path spelled the way an agent in the project root types
|
|
2097
|
+
# it, and a token comparison is exact, so it has to be spelled here too.
|
|
2098
|
+
case "$probe" in
|
|
2099
|
+
*" $rel "* | *" $rel/ "* | *" ./$rel "* | *" ./$rel/ "*) return 0 ;;
|
|
2100
|
+
esac
|
|
2101
|
+
done <<EOF
|
|
2102
|
+
$listed
|
|
2103
|
+
EOF
|
|
2104
|
+
return 1
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2036
2107
|
git_segments_status=0
|
|
2037
2108
|
git_segments="$(printf '%s' "$normalized_command_str" \
|
|
2038
2109
|
| awk '{ if (/\|[ \t]*$/) printf "%s ", $0; else print }' \
|
|
@@ -2055,6 +2126,9 @@ while IFS= read -r git_stmt; do
|
|
|
2055
2126
|
if scan -E "$git_stmt" "$GIT_CONTROL_PLANE"; then
|
|
2056
2127
|
block "recursive forced delete of the git control plane (.git holds every commit, branch and stash not already pushed; nothing in the working tree can rebuild it). Delete a specific ignored artifact instead. Re-cloning is NOT a like-for-like replacement: every linked worktree stores a pointer into this .git, so discarding it discards each of them and any uncommitted work they hold — check \`git worktree list\` before treating the checkout as disposable."
|
|
2057
2128
|
fi
|
|
2129
|
+
if names_linked_worktree "$git_stmt"; then
|
|
2130
|
+
block "recursive forced delete of a linked git worktree. A worktree's index and working files are private to it — only commits are shared — so this destroys any staged or unwritten-to-a-commit work another agent left there, and destroys the evidence that it did (CodySwannGT/lisa#3863). Use 'node scripts/lisa-worktree-guard.mjs remove <path>' instead: it deletes clean and already-committed trees without ceremony, names the files when they exist nowhere else, and records an explicit --force override."
|
|
2131
|
+
fi
|
|
2058
2132
|
done <<<"$git_segments"
|
|
2059
2133
|
|
|
2060
2134
|
# 15. Credential stores. Reading one into an agent transcript is disclosure even
|
|
@@ -307,6 +307,11 @@ function foreignTreeIn(text, bound, cwd) {
|
|
|
307
307
|
for (const raw of code.match(/[^\s'";|&()<>]+/gu) ?? []) {
|
|
308
308
|
const token = raw.replace(/^["']|["']$/gu, "");
|
|
309
309
|
if (!token.includes("/")) continue;
|
|
310
|
+
// A null base means an unliteral `cd` left the directory this script runs
|
|
311
|
+
// from unknown. An ABSOLUTE token is unambiguous either way; a relative one
|
|
312
|
+
// is exactly the thing that cannot be placed, so it is skipped rather than
|
|
313
|
+
// resolved against a directory picked for having been handy.
|
|
314
|
+
if (!isAbsolute(token) && cwd === null) continue;
|
|
310
315
|
const candidate = isAbsolute(token) ? token : join(cwd, token);
|
|
311
316
|
let root;
|
|
312
317
|
try {
|
|
@@ -320,6 +325,63 @@ function foreignTreeIn(text, bound, cwd) {
|
|
|
320
325
|
return null;
|
|
321
326
|
}
|
|
322
327
|
|
|
328
|
+
/**
|
|
329
|
+
* A `cd` target this guard cannot turn into a literal directory.
|
|
330
|
+
*
|
|
331
|
+
* Mirrors `parity-safety-net.sh`'s `follow_cd` test exactly, and the mirror is
|
|
332
|
+
* the point: two spellings of one rule are how the guards drift.
|
|
333
|
+
*/
|
|
334
|
+
const UNLITERAL_CD_TARGET = /[$`*?[{]/u;
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* The literal target of a leading `cd`, `""` for a bare `cd`, or null when the
|
|
338
|
+
* segment does not change directory.
|
|
339
|
+
*
|
|
340
|
+
* A bare `cd` is `cd "$HOME"`. Leaving the previous directory in place for it
|
|
341
|
+
* would resolve later tokens from a directory the shell has already left.
|
|
342
|
+
* @param segment - One statement from the command
|
|
343
|
+
* @returns The target text, or null when this segment is not a `cd`
|
|
344
|
+
*/
|
|
345
|
+
function cdTarget(segment) {
|
|
346
|
+
const tokens = tokenise(segment);
|
|
347
|
+
if (tokens.length === 0 || tokens[0] !== "cd") return null;
|
|
348
|
+
return tokens[1] ?? "";
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Apply one `cd` to the directory currently in force.
|
|
353
|
+
*
|
|
354
|
+
* Contract points 1-4 of `cwd-resolution-corpus.json`: a literal target
|
|
355
|
+
* REPLACES the directory rather than joining a list of candidates — the
|
|
356
|
+
* ordering was #3933's entire defect; a relative target composes against the
|
|
357
|
+
* directory in force; a target that is not literal text yields unknown; and
|
|
358
|
+
* unknown is sticky only until the next resolvable `cd`, because this returns
|
|
359
|
+
* a fresh answer for each one.
|
|
360
|
+
* @param current - Directory in force, or null when it is already unknown
|
|
361
|
+
* @param target - Literal `cd` target, `""` for a bare `cd`
|
|
362
|
+
* @returns The new directory, or null when it cannot be known
|
|
363
|
+
*/
|
|
364
|
+
function applyCd(current, target) {
|
|
365
|
+
const home = homedir();
|
|
366
|
+
let value = target === "" ? home : target;
|
|
367
|
+
if (value === "~") value = home;
|
|
368
|
+
else if (value.startsWith("~/")) value = join(home, value.slice(2));
|
|
369
|
+
if (
|
|
370
|
+
value === "" ||
|
|
371
|
+
value.startsWith("-") ||
|
|
372
|
+
UNLITERAL_CD_TARGET.test(value)
|
|
373
|
+
) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
if (!isAbsolute(value) && current === null) return null;
|
|
377
|
+
const next = isAbsolute(value) ? resolve(value) : resolve(current, value);
|
|
378
|
+
try {
|
|
379
|
+
return statSync(next).isDirectory() ? next : null;
|
|
380
|
+
} catch {
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
323
385
|
/**
|
|
324
386
|
* The foreign worktree an executed script reaches, or null.
|
|
325
387
|
*
|
|
@@ -343,41 +405,71 @@ function foreignTreeIn(text, bound, cwd) {
|
|
|
343
405
|
* disagreeing. The scripted form is the one neither covers, and it is the only
|
|
344
406
|
* cell this arm fills.
|
|
345
407
|
*
|
|
346
|
-
* ## A relative script path is resolved against
|
|
347
|
-
*
|
|
408
|
+
* ## A relative script path is resolved against the cwd the command will have
|
|
409
|
+
*
|
|
410
|
+
* A leading `cd` establishes the directory this guard resolves relative tokens
|
|
411
|
+
* from, because that is the directory the shell will be in once the command
|
|
412
|
+
* runs. Resolving against `payload.cwd` instead was CodySwannGT/lisa#3933's
|
|
413
|
+
* defect, inherited here knowingly and fixed in CodySwannGT/lisa#3952: on a
|
|
414
|
+
* machine running many worktrees of one repository, every tree holds its own
|
|
415
|
+
* copy of every script at the same relative path, and they differ by whatever
|
|
416
|
+
* each lane is doing.
|
|
348
417
|
*
|
|
349
|
-
*
|
|
350
|
-
* relative token is resolved against the hook process's working directory, not
|
|
351
|
-
* against the directory the command will have once it runs its own leading
|
|
352
|
-
* `cd`. So `cd <other tree> && bash scripts/foo.sh` reads THIS tree's copy of
|
|
353
|
-
* `scripts/foo.sh` and reports on it with full confidence.
|
|
418
|
+
* ## One contract, two implementations, and why that is the honest answer
|
|
354
419
|
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* the
|
|
360
|
-
*
|
|
420
|
+
* `parity-safety-net.sh` answers this same question — "which file will this
|
|
421
|
+
* command actually execute?" — in POSIX shell, where `follow_cd` mutates
|
|
422
|
+
* file-scope state that `follow_locate` reads. This guard is Node. **There is
|
|
423
|
+
* no call that reaches from here into a shell function's file-scope state**, so
|
|
424
|
+
* the duplication is not a factoring somebody skipped; it is one idea that
|
|
425
|
+
* cannot cross a language boundary as a function call.
|
|
361
426
|
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
* When that lands, this call site should adopt whatever it produces.
|
|
427
|
+
* What binds the two is a written contract and a corpus of rows that BOTH
|
|
428
|
+
* guards are driven over in CI, so a divergence fails a test on the commit that
|
|
429
|
+
* causes it rather than surfacing later as a wrong verdict. Contract and corpus
|
|
430
|
+
* live in `tests/unit/hooks/support/cwd-resolution-corpus.json`.
|
|
367
431
|
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
432
|
+
* This is explicitly NOT unification, and CodySwannGT/lisa#3952 records it as
|
|
433
|
+
* such: two implementations remain. **A behaviour with no row in the corpus is
|
|
434
|
+
* free to diverge — add a row when you add a branch.**
|
|
435
|
+
*
|
|
436
|
+
* ## The two guards agree on the RESOLUTION, not on the verdict
|
|
437
|
+
*
|
|
438
|
+
* When the effective directory cannot be known, both guards decline to resolve
|
|
439
|
+
* the token. What they then do differs, and that difference is deliberate:
|
|
440
|
+
* `parity-safety-net.sh` refuses, because a destructive command it cannot see
|
|
441
|
+
* is the thing it exists to stop. This guard fails open, which is the doctrine
|
|
442
|
+
* every other undecidable case in this file follows. The corpus asserts the
|
|
443
|
+
* resolved path, not the exit code, precisely so that one contract can bind two
|
|
444
|
+
* guards whose fail directions are correctly different.
|
|
371
445
|
*
|
|
372
446
|
* Fails OPEN and says so, like every other undecidable case in this file.
|
|
373
447
|
*/
|
|
374
448
|
function scriptReachingForeignTree(payload, bound) {
|
|
375
449
|
const command = payload.tool_input?.command;
|
|
376
450
|
if (typeof command !== "string" || !command) return null;
|
|
451
|
+
let cwd = payload.cwd;
|
|
377
452
|
for (const segment of command.split(COMMAND_SEPARATORS)) {
|
|
453
|
+
const target = cdTarget(segment);
|
|
454
|
+
if (target !== null) {
|
|
455
|
+
cwd = applyCd(cwd, target);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
378
458
|
const named = executedPath(segment);
|
|
379
459
|
if (!named) continue;
|
|
380
|
-
|
|
460
|
+
if (!isAbsolute(named) && cwd === null) {
|
|
461
|
+
// Contract point 3: the effective directory is unknown, so WHICH copy of
|
|
462
|
+
// a same-named script would run is unknown too. Declining to resolve is
|
|
463
|
+
// the half shared with parity-safety-net.sh; failing open rather than
|
|
464
|
+
// refusing is this guard's own doctrine, and the corpus binds the two
|
|
465
|
+
// guards on the resolution precisely so it does not have to bind them
|
|
466
|
+
// on a verdict one of them would be wrong to reach.
|
|
467
|
+
say(
|
|
468
|
+
`cannot tell which directory ${named} would run from; NOT enforcing on it`
|
|
469
|
+
);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const path = isAbsolute(named) ? resolve(named) : resolve(cwd, named);
|
|
381
473
|
let text;
|
|
382
474
|
try {
|
|
383
475
|
if (statSync(path).size > SCRIPT_READ_LIMIT) {
|
|
@@ -389,7 +481,7 @@ function scriptReachingForeignTree(payload, bound) {
|
|
|
389
481
|
// Not a readable file: nothing was executed that this can read.
|
|
390
482
|
continue;
|
|
391
483
|
}
|
|
392
|
-
const foreign = foreignTreeIn(text, bound,
|
|
484
|
+
const foreign = foreignTreeIn(text, bound, cwd);
|
|
393
485
|
if (foreign) return { script: path, foreign };
|
|
394
486
|
}
|
|
395
487
|
return null;
|