Repository navigation
fix: find helper programs without a by-name lookup or the Windows working directory - #7562
Conversation
The trusted lookup ran `which` by name through the full PATH before applying the trusted-folder rule, so a relative or project-local PATH entry could supply the lookup program itself. Walk the trusted PATH folders in-process on every platform, as Windows already did, for findTrustedExecutable, findFfBinary and the doctor's unzip check. The walk keeps what `which` did for valid installs: first match in PATH order, regular files only, and a leading `~` in a PATH entry read as the home folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ry lookup off mediaUseEngineEnv now sets NoDefaultCurrentDirectoryInExePath for the engine on Windows itself instead of relying on the CLI's own environment, and keeps a value the person set. When no PATH folder passes the trusted rule, PATH names a folder that is never created rather than an empty value, which a shell or execvp reads as the working directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m lookup On Windows a program started by name is looked up in the working directory before PATH. The plugin launcher and the media-use, hyperframes and figma helper scripts run inside project folders and start ffmpeg, ffprobe, Python and other tools by name, either through the launcher or directly from a standalone skill install. Each skill now ships a small module that sets NoDefaultCurrentDirectoryInExePath for the process (keeping a value the person set), and every script that starts programs imports it first, so the helpers and every program they start skip the working directory. Tools installed on PATH resolve as before; macOS and Linux are unaffected. A test checks that each such script loads the module. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The CLI build replaces the published media-use scripts/lib folder with packages/cli/src/media-use/lib. That folder had no windows-exe-search.mjs, so every bundled media-use script that imports it, including the engine behind `hyperframes media-use`, failed to load on every platform. The guard is now a generated copy in the CLI lib, next to the skill copies. A parity test checks that each scripts/lib module a published media-use script imports exists in the CLI lib. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ns on Windows On Windows, `node --import D:\...` reads the drive letter as a URL scheme and fails before the launcher starts. The test now passes a file URL and reports the child's stderr when it exits non-zero. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at 83cc4ef11b95a60599295284631b3663c99ca6d3.
No blockers found. The in-process trusted-PATH lookup reuses the existing directory policy; generated copies include both the standalone skills and the CLI replacement library. The first trusted PATH match remains selected, home-directory expansion is preserved, and POSIX directories/non-executable candidates are skipped. The touched launcher and generated-library paths remain consistent with their current callers.
Validation: bun run build passed; focused parser tests 28/28, CLI media-use/Python/Whisper tests 29/29, and copy-parity/Windows-guard/launcher tests 21/21 passed on Linux. Replacing the two parser source files with their base versions makes 4/28 tests fail; the head sources were restored. Windows behavior was simulated locally, not exercised on Windows.
Pre-existing follow-up: the environment guard depends on the spawning runtime honoring it. Add an actual Windows child-launch test against the oldest supported Node patch, and document the supported runtime floor or resolve helper paths absolutely. This limitation also exists in the earlier CLI guard.
Need: yes · Size: ok · Reuse: trustedPathDirs and generated skill-module copies · Simpler: already minimal · Structure: fine (checked findTrustedExecutable/findFfBinary, existing Windows guard, launcher and CLI library replacement; no split)
Risk: medium — changes helper resolution across platforms; native Windows execution was not run locally.
Verdict: APPROVE
Reasoning: the shared lookup and launch guards are consistent with their existing callers, and the focused regressions pass.
— Rames
There was a problem hiding this comment.
Verdict: REQUEST CHANGES · 1 blocker · 1 important · 0 nits
Additive to Rames's approval. The lookup changes hold up, and I couldn't find a planted-binary path through findTrustedExecutable, findFfBinary or the engine env.
- blocker:
skills/media-use/audio/scripts/extract-audio-data.py:54still runsffmpegby name, and nothing turns off the working-directory lookup first.motion.md:72tells the agent to run it from the project withpython3. On Windows, Python'ssubprocesshands the bare name toCreateProcess, which searches the current folder beforePATH, so a project that shipsffmpeg.exegets it run. That's the same failure this PR fixes for the.mjsscripts. One fix: resolveffmpegto an absolute path from absolutePATHentries outside the current folder, then run that path. The census misses it, see ↓ inline. - important (pre-existing, follow-up): on macOS/Linux the skill scripts still use your unfiltered
PATH. A relative or empty entry (node_modules/.bin, a trailing:) runs a plantedffprobefromlocate.mjs. I reproduced this withPATH="node_modules/.bin:$PATH" node locate.mjs. The CLI side already filtersPATHwithtrustedPathDirs. The scripts could do the same before they start anything.
Scope
I read every source file in the diff. Checked and ruled out: libuv honours NoDefaultCurrentDirectoryInExePath (NeedCurrentDirectoryForExePathW in src/win/process.c). ESM import order means the guard runs before any spawn. The NEVER_CREATED_PATH_FOLDER placeholder sits outside the project. Tilde expansion is now safer than before. The which/where calls left in the codex/mflux/ltx providers run only under the engine's scoped env. CI is green on this head.
— Claude (Genesis review session)
…H entries extract-audio-data.py ran ffmpeg by bare name, which Windows looks up in the working directory first. It now runs the absolute path that trusted_program.find_program finds in absolute PATH folders outside the working directory. A miss raises the same FileNotFoundError as before. On macOS and Linux the shared guard now drops relative and empty PATH entries, which point into the project folder, and leaves absolute ones in order. If none is absolute, PATH is unset rather than left empty. The guard census now also requires every skill Python script that imports subprocess to resolve programs through trusted_program. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review follow-up:
|
trusted_program.py carries the same UTF-8 stdio pin as every skill Python script. The guard test's planted-program probe uses a literal-only argv, which the ffprobe argv contract test reads as a capability check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jrusso1020
left a comment
There was a problem hiding this comment.
Reviewed at 307aab410a3dd3fa7cb1f7ed93be72c1ae7425f8.
Verdict: APPROVE
Re-reviewed the complete delta since 83cc4ef11b95a60599295284631b3663c99ca6d3. I independently checked the three concerns in Genesis's earlier review: the audio helper now uses an absolute trusted program path, the census includes Python subprocess callers, and the shared guard filters relative/empty POSIX PATH entries. The Python resolver follows the existing CLI directory policy and raises FileNotFoundError on a miss, without falling back to a bare command. Generated standalone and CLI copies remain in sync.
Validation on this head: bun run build passed. node --test on the guard, Python resolver and copy-parity files passed 17/17 with no skips, with NumPy available. Restoring the previous guard and audio script makes four targeted tests fail; reviewed sources were restored. A real ffmpeg decode of a one-second audio fixture produced 30 frames with 16 bands. Independent code and review-gap passes found no additional blocker.
Native Windows execution was not exercised locally. Runtime-version compatibility for the existing Windows environment guard remains a pre-existing follow-up from my earlier review; these Linux results do not establish that compatibility across every supported Node patch.
Need: yes · Size: ok · Reuse: trustedExecutable directory policy and generated skill-module copies · Simpler: explicit absolute resolver · Structure: fine (checked trustedPathDirs, the skill-copy generator and CLI library replacement; no split)
Risk: medium — changes cross-platform helper resolution; native Windows was not run locally.
Reasoning: the requested resolver, census and POSIX guard changes are present and the focused regressions distinguish the old and new implementations. No introduced blocker found in this delta.
— Rames
What changed
Helper programs are now found without running a lookup program by name, and without the Windows working-directory search.
findTrustedExecutable,findFfBinaryand the doctor'sunzipcheck walk the trustedPATHfolders inside the process on every platform. They no longer runwhichorwhereby name first. For valid installs the result is the same as before: the first match inPATHorder, regular files only, and a leading~read as the home folder.mediaUseEngineEnvsetsNoDefaultCurrentDirectoryInExePathon Windows and keeps a value the user already set. When noPATHfolder is trusted, the engine'sPATHis never empty, because an emptyPATHmeans the current folder to some shells.extract-audio-data.pyruns the absoluteffmpegpath thattrusted_program.find_programfinds in absolutePATHfolders outside the working directory (the CLI's rule). A missingffmpegraises the sameFileNotFoundErroras before.PATHentries, which point into the project folder, and keeps absolute ones in order. If none is absolute,PATHis unset rather than left empty.scripts/libfolder withpackages/cli/src/media-use/lib, so that folder carries a generated copy of the guard too. A parity test checks that everyscripts/libmodule a published media-use script imports also exists in the CLI lib.Behaviour changes
On macOS and Linux, skill scripts no longer reach programs through a relative or empty
PATHentry such asnode_modules/.binor a trailing:. Node's process spawning never expanded a literal~entry, so dropping one loses nothing it could reach.On Windows, the helper scripts no longer run an
ffmpeg.exethat exists only in the project folder. This matches the CLI's behaviour since fix(cli): keep project folders from choosing the programs and config the CLI runs #7511 and fix: take helper programs only from install locations, never the project folder #7524.doctorno longer counts anunzipthat exists only in a project-localPATHfolder.Programs on
PATH, in Homebrew and in.hyperframes/binare found as before.Testing
fails without the fix:
a relative PATH entry cannot run a program planted in the project,drops relative and empty PATH entries elsewhere and keeps absolute ones in order,leaves PATH unset rather than empty when no entry is absolute,every skill Python script that starts programs resolves them through trusted_program,extract-audio-data never runs an ffmpeg planted in the project,find_program skips every project folder on PATH and returns the trusted one, shown by restoring the previous guard, script or a bare PATH walk and rerunning the tests. The parity testfinds every scripts/lib module a published media-use script imports in the CLI libfails without the CLI copy of the guard.The reviewer's repro
PATH="node_modules/.bin:$PATH" node locate.mjsruns the plantedffprobewith the previous guard and does not with this one.extract-audio-data.pystill decodes a real file with Homebrewffmpeg, run from a project folder and from the home folder.New tests cover a planted
whichonPATH, Windows behaviour through a stubbedprocess.platform, and a guard that every skill script that starts programs loads the setting first. Each new test fails without its fix.parsers 28/28, the touched CLI test files 153/153, skills 238/238. The parity, import-boundary and lint-skills checks pass, as do oxlint, oxfmt, fallow and the comment ratchet.
whisper/manager.install.test.tsnow builds realPATHfixtures instead of mockingwhich, since the lookup no longer runs it.On this Mac,
findTrustedExecutableandfindFfBinaryreturn the same path as/usr/bin/whichfor ffmpeg, ffprobe, python3, unzip, whisper-cli, brew, git, cmake, node and docker. Homebrew symlinks are followed. A non-executable file, a directory and a broken symlink earlier inPATHare skipped, aswhichdoes.A simulated CLI bundle layout and a standalone install of the hyperframes, media-use and figma skills both load every changed script.
The Windows CI job runs the launcher test for real. Lookup itself is not run on real Windows outside CI.
🤖 Generated with Claude Code