You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
parentDeclarations requires node_modules, so OA009 and OA011 silently no-op on a lockfile-only scan #1232
Note: this is an in-house item already being handled by the maintainer - not open for contribution. Filed for tracking only.
parentDeclarations is populated by walkInstalledTree, which reads manifests out of node_modules. On a checkout with no node_modules, the map is empty and every rule that depends on it silently finds nothing.
That affects OA009 and OA011, and it fails in the worst way: the rules return no findings rather than reporting that they could not look.
Why this matters more than it sounds
Scanning a lockfile without installing is the product. The GitHub App design depends on it explicitly: a scan needs package.json and the lockfile and nothing else, with no checkout, no npm install, no node_modules and no execution of repository code. Any rule that needs an install is unreachable from that path.
It also breaks the way we scan external repositories for case studies and workflow PRs, where we deliberately never install. Concretely: OA011 was written for the unbounded @hono/node-server override in CopilotKit, and running it against that repository finds nothing, because the checkout has no node_modules. The rule is correct; it simply cannot see.
The lockfile already carries this
An npm lockfile records each package's own dependency ranges, not just resolved versions. In examples/nest/package-lock.json, 1,141 packages declare them:
That is exactly what parentDeclarations needs, available without installing anything.
Scope
Source parent declarations from the lockfile, falling back to node_modules when present.
Verify per package manager before promising it. Confirmed for npm only. A first look at a pnpm lockfile showed a resolved version rather than a declared range, so pnpm, Yarn and Bun each need checking and may not all carry it.
Where the data genuinely is not available, say so rather than returning no findings. A rule that cannot run should report that it could not run.
Item 3 stands on its own even if the lockfile route only works for npm.
Hi! I’d like to reproduce the lockfile-only scan path, add regression coverage, and fix the node_modules dependency assumption. Could you confirm the expected lockfile-only behavior?
@anbv29 - thanks for offering. This one is in-house, so I am keeping it.
It carries the in-house label and a note at the top of the body saying so, and I am not surprised you missed the note, since it scrolls past easily. The label is the reliable signal, so -label:in-house is a useful filter if you go looking for something else here.
Why this one specifically: the fix reaches into walkInstalledTree and the parser layer that feeds every override-hygiene rule, and it is load-bearing for the GitHub App design, where a scan gets package.json and the lockfile and nothing else. I would rather own the blast radius on that one.
Your question is a good one though, and deserves an answer rather than a brush-off. The expected lockfile-only behaviour is:
A scan works from package.json plus the lockfile alone. No node_modules, no install, no execution of anything in the repository.
Parent dependency ranges come from the lockfile where it records them. npm does: examples/nest/package-lock.json carries declared ranges for 1,141 packages, which is exactly what the rules need. pnpm, Yarn and Bun each need verifying separately, and a first look at a pnpm lockfile showed a resolved version rather than a range, so I am not assuming they all carry it.
Where the data genuinely is not there, the rule reports that it could not run rather than returning zero findings. A silent no-op that looks like a clean bill of health is the actual bug here, and that part holds even if the lockfile route only ever works for npm.
If you want something else to pick up, there are currently 3 issues labelled good first issue, 4 help wanted and 1 first-timers-only. I am deliberately not choosing one for you: first working PR wins here, so an issue being unassigned does not mean it is unclaimed. Worth checking an issue for an existing open PR before you start, because that is how people end up duplicating each other's work, and I would rather you did not lose an afternoon to it.
Closing this: #1262 shipped most of what it asked for, and what is left is narrower than this issue describes.
What this issue was actually about. The complaint was that override rules depending on parentDeclarations go partially blind on a lockfile-only scan and the output still reads like a clean bill of health. The second half is no longer true. On a tree with an override and no node_modules, the scan now ends with "6 override rules did not run, so this is not a complete override audit", naming OA004, OA006, OA008 and OA009 as missing node_modules, and OA007 and OA010 as needing --check-network. That disclosure reaches the terminal, --json, SARIF and the HTML report.
Two corrections to this issue's own body, for the record. It named the affected rules as OA009 and OA011, which was wrong when it was filed because OA011 did not exist yet; the set is OA006, OA008, OA009 and OA011 today. And "the map is empty" was wrong in a way that mattered: seedRootDeclarations runs unconditionally, so without node_modules the map holds the root project's own declarations. The rules were partially sighted rather than blind, which is why the output looked credible instead of obviously broken.
What remains, filed as #1279. OA011 is deliberately not in the skip list, because a skip makes a detector return nothing and OA011 produces correct findings from root-only declarations. So it runs on partial input and says nothing about it. That wants a third state between "ran" and "did not run", and #1279 also records the open question of whether that state earns its complexity at all.
The other half of this issue, sourcing declared ranges from the lockfile, is parked rather than rejected. It is recorded here so it is not re-derived: npm, Yarn and Bun all carry declared ranges in their lockfiles, and pnpm does not. pnpm's snapshots records resolved versions rather than ranges, and its packages block carries no dependencies keys at all, only peerDependencies. Its importers block has specifier but only for the root project's direct dependencies, so it is a partial source at best. That matters because the largest deployment we know of is moving to pnpm, and CopilotKit, the tree that motivated OA011, is pnpm. pnpm's .pnpm store does contain real manifests, so the gap is specifically the no-install path. If that work is picked up it needs its own issue and its own spec.
Activity
anbv29 commentedon Sep 24, 2026
Hi! I’d like to reproduce the lockfile-only scan path, add regression coverage, and fix the node_modules dependency assumption. Could you confirm the expected lockfile-only behavior?
sonukapoor commentedon Sep 26, 2026
@anbv29 - thanks for offering. This one is in-house, so I am keeping it.
It carries the
in-houselabel and a note at the top of the body saying so, and I am not surprised you missed the note, since it scrolls past easily. The label is the reliable signal, so-label:in-houseis a useful filter if you go looking for something else here.Why this one specifically: the fix reaches into
walkInstalledTreeand the parser layer that feeds every override-hygiene rule, and it is load-bearing for the GitHub App design, where a scan getspackage.jsonand the lockfile and nothing else. I would rather own the blast radius on that one.Your question is a good one though, and deserves an answer rather than a brush-off. The expected lockfile-only behaviour is:
package.jsonplus the lockfile alone. Nonode_modules, no install, no execution of anything in the repository.examples/nest/package-lock.jsoncarries declared ranges for 1,141 packages, which is exactly what the rules need. pnpm, Yarn and Bun each need verifying separately, and a first look at a pnpm lockfile showed a resolved version rather than a range, so I am not assuming they all carry it.If you want something else to pick up, there are currently 3 issues labelled
good first issue, 4help wantedand 1first-timers-only. I am deliberately not choosing one for you: first working PR wins here, so an issue being unassigned does not mean it is unclaimed. Worth checking an issue for an existing open PR before you start, because that is how people end up duplicating each other's work, and I would rather you did not lose an afternoon to it.anbv29 commentedon Sep 26, 2026
Sure! My apologies for not noticing the in-house thing already mentioned. Will see if I can work on other issues.
Thanks
sonukapoor commentedon Oct 3, 2026
Closing this: #1262 shipped most of what it asked for, and what is left is narrower than this issue describes.
What this issue was actually about. The complaint was that override rules depending on
parentDeclarationsgo partially blind on a lockfile-only scan and the output still reads like a clean bill of health. The second half is no longer true. On a tree with an override and nonode_modules, the scan now ends with "6 override rules did not run, so this is not a complete override audit", naming OA004, OA006, OA008 and OA009 as missingnode_modules, and OA007 and OA010 as needing--check-network. That disclosure reaches the terminal,--json, SARIF and the HTML report.Two corrections to this issue's own body, for the record. It named the affected rules as OA009 and OA011, which was wrong when it was filed because OA011 did not exist yet; the set is OA006, OA008, OA009 and OA011 today. And "the map is empty" was wrong in a way that mattered:
seedRootDeclarationsruns unconditionally, so withoutnode_modulesthe map holds the root project's own declarations. The rules were partially sighted rather than blind, which is why the output looked credible instead of obviously broken.What remains, filed as #1279. OA011 is deliberately not in the skip list, because a skip makes a detector return nothing and OA011 produces correct findings from root-only declarations. So it runs on partial input and says nothing about it. That wants a third state between "ran" and "did not run", and #1279 also records the open question of whether that state earns its complexity at all.
The other half of this issue, sourcing declared ranges from the lockfile, is parked rather than rejected. It is recorded here so it is not re-derived: npm, Yarn and Bun all carry declared ranges in their lockfiles, and pnpm does not. pnpm's
snapshotsrecords resolved versions rather than ranges, and itspackagesblock carries nodependencieskeys at all, onlypeerDependencies. Itsimportersblock hasspecifierbut only for the root project's direct dependencies, so it is a partial source at best. That matters because the largest deployment we know of is moving to pnpm, and CopilotKit, the tree that motivated OA011, is pnpm. pnpm's.pnpmstore does contain real manifests, so the gap is specifically the no-install path. If that work is picked up it needs its own issue and its own spec.