Skip to content

fix(scan): read the lockfiles the scanner already claimed to support - #143

Merged
ralyodio merged 1 commit into
masterfrom
fix/lockfile-parsers
Aug 14, 2026
Merged

fix(scan): read the lockfiles the scanner already claimed to support#143
ralyodio merged 1 commit into
masterfrom
fix/lockfile-parsers

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The problem

packages/scan/src/node/dependencies.ts listed five lockfiles as supported. Its parser implemented two.

pnpm-lock.yaml, yarn.lock and Pipfile.lock matched by filename, fell through parseDependencies, and resolved to zero dependencies. OSV was never queried, so the scan produced no dependency findings — which looks exactly like a clean result. ThreatCrush is a pnpm repository, so it has been scanning itself this way.

This is the half of the product the outreach leads with, and it was inert for the most common JS lockfile in our own target market.

What changed

Each LOCKFILES entry now carries its parser, so a filename cannot be registered without one.

Format Before After
package-lock.json worked, kept inner node_modules/ prefixes on nested paths resolves to the package name
pnpm-lock.yaml 0 deps v5 / v6 / v9 key shapes, peer suffixes stripped
yarn.lock 0 deps v1 + berry, skips __metadata and local protocols
Pipfile.lock 0 deps both sections, skips VCS refs
requirements.txt == pins matching [0-9.]+ only PEP 440 pre/post-release pins, extras, markers, flag lines

Two silences become output, per PRD 0002 R6 ("parse failures are first-class output"):

  • a lockfile that parses to nothing now says so
  • the MAX_DEPS_PER_LOCKFILE = 50 cap now reports when it truncated, instead of quietly checking 50 of 1876

Queries are also deduplicated by name@version, so repeated lockfile entries don't spend the cap re-asking the same question.

Verification

  • 13 new parser tests over real-shaped fixtures for every dialect; 266 tests pass in @threatcrush/scan
  • tsc --noEmit clean in packages/scan and apps/cli
  • against this repo's own pnpm-lock.yaml: 1876 packages parsed, previously 0, with every name and version passing the existing OSV input validators

Deliberately not in scope

  • Maven/Gradle, and Go/Rust/PHP/Ruby. Worth noting prd/0002 R4 lists the latter five but omits Java entirely. Most Android repos have no Gradle lockfile at all (dependency locking is opt-in), so that work is SBOM ingestion rather than another file parser.
  • POST /v1/querybatch. OSV takes up to 1000 queries per request, which would make the 50-package cap unnecessary rather than merely visible. Bigger change to network behaviour; better on its own.

`pnpm-lock.yaml`, `yarn.lock` and `Pipfile.lock` were listed in `LOCKFILES`,
matched by filename, and then handed to a parser that only implemented
`package-lock.json` and `requirements.txt`. They fell through it and resolved
to zero dependencies, so OSV was never asked a single question about them —
and the scan reported no dependency findings, which is indistinguishable from
a clean result. This repository is pnpm; it has been scanning itself this way.

Every entry in `LOCKFILES` now names its parser, so a filename cannot be added
without one. Added:

- pnpm v5, v6 and v9 key shapes, normalised rather than version-detected, with
  peer suffixes stripped from the version only — `_` is legal in a name.
- yarn v1 and berry, skipping `__metadata` and anything resolved by a
  non-registry protocol (`workspace:`, `file:`, `link:`).
- `Pipfile.lock`, both sections, skipping VCS entries that carry a ref rather
  than a version.
- `requirements.txt` now keeps PEP 440 pre/post-release pins (`5.0rc1`,
  `2.2.2.post1`), which the old `([0-9.]+)` pattern silently dropped, and
  handles extras, markers and flag lines. Wildcard pins stay out: `==1.4.*` is
  a range wearing an equals sign.
- `package-lock.json` v2+ nested paths resolve to the package name instead of
  keeping an inner `node_modules/` prefix.

Two silences also become output, per PRD 0002 R6. A lockfile that parses to
nothing now reports that it did, and the 50-package cap now says when it
truncated — the operator was previously told nothing either way.

Queries are deduplicated by name@version so repeated entries do not spend the
cap re-asking. On this repository's own lockfile the parser returns 1876
packages where it previously returned none; every name and version passes the
existing OSV validators.
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

70 finding(s)

HIGH/CRITICAL: 12 | MEDIUM: 57 | LOW: 1

Severity Rule Location
HIGH secret-aws-access-key prd/0003-detect-hardcoded-secrets-before-they-are-committed-or-served.md:126
HIGH sh-eval-expansion .githooks/pre-commit:26
HIGH js-electron-node-integration apps/desktop/src/main/index.ts:22
HIGH sh-remote-script-execution apps/web/public/install.sh:272
HIGH sh-remote-script-execution apps/web/public/install.sh:320
HIGH secret-generic-credential modules/spend-guard/config/example.conf.toml:13
HIGH secret-generic-credential modules/spend-guard/README.md:84
HIGH secret-generic-credential PRD.md:268
HIGH tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:121
HIGH tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:122
HIGH sh-remote-script-execution scripts/smoke-test.sh:46
HIGH sh-remote-script-execution scripts/smoke-test.sh:47
MEDIUM insecure-temp-file .githooks/commit-msg:16
MEDIUM insecure-temp-file .githooks/post-commit:20
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/init.ts:70
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/init.ts:79
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/service.ts:88
MEDIUM js-shell-exec-interpolation apps/cli/src/commands/service.ts:111
MEDIUM sql-template-interpolation apps/cli/src/core/state.ts:121
MEDIUM sql-template-interpolation apps/cli/src/core/state.ts:125
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:31
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:33
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:34
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:35
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:36
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:43
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:49
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:56
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:63
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:82
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:84
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:85
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:93
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:98
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:105
MEDIUM js-shell-exec-interpolation apps/cli/src/daemon/firewall/adapters.ts:112
MEDIUM js-shell-exec-interpolation apps/cli/src/index.ts:425
MEDIUM insecure-temp-file apps/desktop/README.md:95
MEDIUM js-unescaped-html-sink apps/web/src/app/about/page.tsx:180
MEDIUM js-unescaped-html-sink apps/web/src/app/about/page.tsx:184
MEDIUM js-open-redirect apps/web/src/app/auth/login/page.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:125
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:153
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:157
MEDIUM js-unescaped-html-sink apps/web/src/app/get-whitepaper/page.tsx:346
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:211
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:215
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:219
MEDIUM js-unescaped-html-sink apps/web/src/app/page.tsx:120
MEDIUM js-unescaped-html-sink apps/web/src/app/store/[slug]/page.tsx:107

…and 20 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 83f2177 into master Aug 14, 2026
11 checks passed
@ralyodio
ralyodio deleted the fix/lockfile-parsers branch August 14, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant