Repository navigation
Fix npm workspace members named build/dist skipped (#1627) - #1628
Open
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Open
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Mikola Lysenko (mikolalysenko) wants to merge 5 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
A declared workspace member at packages/build, packages/dist, vendor/lib or tools/build is never crawled, so agent apply leaves its nested copy unpatched, apply --check calls it in sync and VEX attests not_affected (#1627). These tests fail on main. Assisted-by: Claude Code:claude-opus-5-5
The npm-family crawler skips every directory named dist, build, coverage, tmp, temp, __pycache__ or vendor. It did so even when the root package.json workspaces field declares that directory as a member. npm, yarn and Bun install a member's unhoistable dependencies into its own node_modules from the root lock, so that copy was never patched. apply --check still passed, and VEX attested not_affected for a vulnerable copy that runs in production. The crawler now also crawls the node_modules of every declared member (a dir with a package.json that any package manager's workspaces reading lists) whose path crosses one of those names. Undeclared build output and cache dirs stay skipped. Fixes #1627 Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups on the #1627 fix: - Also read the workspaces of nested projects the walk finds, so a workspace below the scan root keeps its build/dist members. - Never descend into a tagged cache dir, and read each dir's name from its parent's listing, so a member spelled in another case on a case-insensitive disk is not listed twice. - Walk patterns that start with "!" too: yarn berry reads "!(..)" as an extglob that lists members. - Give each pattern its own dir budget, and reuse the existing workspaces field parser. - The CLI test now checks VEX with the standalone vex command, and the manifest carries a vulnerability so there is a statement to check. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 17, 2026 13:17
Collaborator
Author
|
BugBot review Generated by Claude Code |
A workspace member reached through a literal pattern such as cache/build/pkg was crawled even when cache/ carries a CACHEDIR.TAG, so agent apply could rewrite files in a tool's cache. A tagged scan root instead dropped every member a glob-led pattern lists, although the walk always crawls the scan root. Now every dir below the workspace root is checked for the tag, literal prefix included, and the root itself is exempt. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit a8326e5. Configure here.
Collaborator
Author
|
Labeled Ready for review (burn-down agent).
Slack announcement not sent this run (the Slack send tool was unavailable). The next run will retry. Generated by Claude Code |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1627
Summary
The npm-family crawler skipped every directory named
dist,build,coverage,tmp,temp,__pycache__orvendor, even when the rootpackage.jsonworkspacesfield declares it as a member. npm, yarn and Bun install a member's unhoistable dependencies into<member>/node_modulesfrom the root lock. For a member atpackages/build,packages/dist,vendor/libortools/build, that copy was never crawled. Agentapplyleft it unpatched,apply --checksaid "in sync", andvexattestednot_affectedfor a vulnerable copy that runs in production.The crawler now also crawls the
node_modulesof those declared members. Undeclared build-output and cache dirs stay skipped.Root cause
NpmCrawler::workspace_children(crates/socket-patch-core/src/crawlers/npm_crawler.rs) filters children by name againstSKIP_DIRS. Nothing consulted theworkspacespatterns, so a declared member with one of those names was pruned along with real build output.Fix
CACHEDIR.TAG, and the workspace root itself is exempt, as in the main walk.skipped_workspace_member_rootssits next toconfigured_install_rootsand is merged intofind_local_node_modules_dirs(and into its test oracle, like the configured roots). The workspace roots it reads are the scan root and the project of everynode_modulesthe walk found, so nested workspaces count. For each root it readspackage.jsonworkspaceswith the existinggoverning_root::workspace_patternsparser. It walks each pattern from its literal prefix, to the depth the pattern allows (**= any depth, within the existing 20k-dir budget per pattern), and keeps dirs that:SKIP_DIRSname,workspace_globs::workspaces_include_asoverWorkspacesDialect::ALL, which covers braces, extglobs and negation), andpackage.json.node_modules, noCACHEDIR.TAG-tagged dirs. Names come from directory listings, so on a case-insensitive disk a pattern spelledpackages/buildfor an on-diskpackages/Build(which the walk doesn't prune) is not listed twice.docs/ecosystems.md("npm: whichnode_modulestrees are crawled") documents the exception.The npm / PyPI / gem wrappers only dispatch to the binary, so they need no change.
Tests (red → green)
in_process_alternate_installers::apply_patches_workspace_member_named_like_a_build_dir(membersbuild,dist,vendor,tmp,temp,coverage)packages/build/node_modules/ms/index.jsunpatchedapply --check+vexin_process_alternate_installers::apply_check_and_vex_see_unpatched_member_named_like_a_build_dir--checkexit 0, expected 1vexhas nonot_affecteduntil the member copy is patched, then attests it)crawler_npm_e2e::crawl_all_finds_declared_members_with_skip_dir_names(packages/*,vendor/*, literaltools/build, braces,{packages:[…]}+**, yarn berry!(docs)/*)crawler_npm_e2e::crawl_all_finds_skip_dir_members_of_a_nested_workspacecrawler_npm_e2e::crawl_all_finds_members_of_a_tagged_scan_rootcrawl_all_still_skips_undeclared_skip_dirs,crawl_all_member_walk_keeps_the_walk_rules(glob and literal members under a tagged cache dir, case-mismatched pattern; its literal-prefix case failed on the previous head), the existingcrawl_all_skips_hidden_and_skip_dirsLocal runs:
cargo fmt --all -- --check: cleancargo clippy --workspace --all-features -- -D warnings: cleancargo test -p socket-patch-core --all-features: 7161 passed, 4 failed. The 4 (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_…,pypi_requirements::wire_failure_rolls_back_…) are permission-denial tests that can't fail as root, and this container runs as uid 0. They're in modules this PR doesn't touch, and CI runs them as non-root.cargo test -p socket-patch-cli --all-features --test in_process_alternate_installers: 40/40.--test in_process_agent_reapply: 27/27.cargo test --workspace --all-featuresbuild ran out of this session's disk allowance (more than 19 GB of test binaries), so CI runs the rest of the CLI suites.Review notes (
/code-review high)I fixed these findings: a budget shared across patterns, CACHEDIR-tagged dirs not pruned, nested workspace roots ignored, a case-insensitive duplicate,
!(..)extglob patterns dropped, a duplicatedworkspacesparser, a no-op dedup loop, and a vacuous VEX assertion (apply --checknever writes VEX, so the test now runsvexitself with a manifest that has a vulnerability).Not changed, and why:
/, extglobs, picomatch). That grammar lives inworkspace_globsand isn't built for prefix matching. The separate walk reuses the exact membership check, and it costs onepackage.jsonread per workspace root plus one listing per pattern prefix. A**pattern can cost more, but only up to the per-pattern budget.pnpm-workspace.yamlmembers undernode-linker=hoisted. Out of scope for this npm report. pnpm's default isolated layout keeps copies in the root.pnpmstore, which is already crawled. Follow-up if a hoisted-pnpm repro turns up.**walk is still strictly more coverage than main. Refusing (as the Bun helper does) would need a new error path through the agent crawl.https://claude.ai/code/session_01QE3Va4u8ZT15HpxzAULNcG
Note
Medium Risk
Changes which npm
node_modulestrees are discovered, directly affecting patch apply, drift check, and VEX attestation in monorepos.Overview
Fixes #1627: declared npm/yarn/Bun workspace members whose folders are named like build output (
packages/build,vendor/lib, etc.) were skipped by the crawler, so nestednode_modulesthere were never patched,--checkcould look clean, and vex could claim not_affected incorrectly.The crawler now adds
skipped_workspace_member_roots, which reads rootworkspacespatterns (viaworkspace_patterns, nowpub(crate)), walks each pattern with the same pruning rules as the main tree (including CACHEDIR.TAG on literal prefixes), and includes<member>/node_moduleswhen a listed member crosses a SKIP_DIRS segment and has apackage.json. Those roots are merged intofind_local_node_modules_dirs(and the test oracle). Undeclareddist/build/… trees stay pruned.Tests cover agent apply,
apply --check, vex, andcrawl_allfor globs, braces, nested workspaces, and tagged roots.docs/ecosystems.mddocuments the workspace exception.Reviewed by Cursor Bugbot for commit a8326e5. Configure here.
Generated by Claude Code