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
osv-scanner 2.4.0 has no deno.lock extractor, so Deno-first repos in this org get a green check having scanned nothing — the gap flagged in infra#104. This issue is the plan to close the closable part of it.
The insight that makes this cheap
A v5 deno.lock already contains the fully-resolved npm dependency graph. Verified against front-desk-scheduler's lock: 107 entries in the npm section (names, exact versions, integrity hashes) vs 3 in jsr. Deno's npm-compat layer did the resolution work; we only need to re-encode it as something the scanner reads.
Also verified locally against the real v2.4.0 binary: a CycloneDX 1.5 file named *.cdx.json is auto-detected by scan source -r and scoped npm purls (pkg:npm/%40hono/node-server@1.19.15) extract cleanly. No flags, no scanner changes — drop a generated SBOM next to the lock and the existing scan picks it up.
Three buckets, stated honestly
bucket
example
covered?
npm-registry deps
mysql2@3.13.0, @hono/node-server
✅ after this work
JSR deps via npm-compat
@jsr/bounded-systems__verbspec
❌ — served from npm.jsr.io, not npmjs; no OSV advisory can exist under that purl, so including them would inflate the "scanned" count dishonestly. Excluded from the SBOM, counted in the log.
JSR-native / deno.land/x remotes
jsr:@bounded-systems/verbspec
❌ — OSV has no JSR ecosystem (to re-verify: api.osv.dev is egress-blocked from the authoring session)
The lane's log will print all three counts per lock, so a green check states its own blind spot.
Determinism requirements (the Nix-flakes lens)
The converter is a pure function: same deno.lock bytes → byte-identical SBOM, forever, on any machine.
stdlib-only Python 3 — no pip, no third-party action, nothing to SHA-pin beyond what the lane already pins
no timestamp, no random serialNumber (both optional in CycloneDX; the usual UUID default is exactly the nondeterminism to refuse)
components sorted by purl; json.dumps(sort_keys=True), fixed separators, trailing newline
goldens enforce byte-equality, plus a double-run self-check
flake.nix provides the same Python + test runner so nix flake check reproduces CI's verdict locally. CI itself does not install Nix — the lane stays lean; the flake is the local-reproducibility face of the same test suite. (flake.lock needs one nix flake lock run on a machine with Nix — the authoring session has none.)
Single-definition constraint
A reusable workflow's steps run in the caller's checkout — tools/ from this repo isn't on disk there. So the script is embedded in osv-scan.yml as a heredoc, and tracked as tools/deno-lock-cdx.py for tests — with a drift check in self-test.yml that extracts the heredoc and byte-compares it against the file (the same keep-the-copies-honest pattern as infra's proofs/check-sync.mjs). Golden fixtures are synthetic (made-up package names) so a future real-world advisory can never redden the self-test for unrelated reasons; goldens are named *.golden, never *.cdx.json, so the scan job never ingests them.
Considered: deploy conversion/scanning as a service (Cloudflare or elsewhere)
Raised during planning. Split it in two:
As the converter — rejected. A hosted endpoint turns a deterministic 100-line transform into an availability dependency and a trust boundary (the lane would trust a network response instead of a local pure function), and it needs deploy credentials. By the org's own criterion — the zero-standing-grants property that made a reusable workflow beat an App in infra#90 — this is strictly worse.
As continuous monitoring — deferred, and it's the one real thing a service could add: today scans run on PR/push, so an advisory published after a lock merges goes unnoticed until the next change. But the cheap version of that is schedule: cron in each caller (rescan weekly, zero new infrastructure), which adopters can turn on per-repo. A central Worker that fetches locks across repos would need a standing GitHub credential — the exact thing the broker architecture exists to avoid. Revisit only if per-repo crons prove unmanageable at fleet scale.
DAG
flowchart TD
A["A: inspect real deno.lock v5<br/>(107 npm / 3 jsr — done)"] --> B["B: tools/deno-lock-cdx.py<br/>pure, stdlib-only, sorted output"]
A2["A2: verify *.cdx.json detection<br/>vs real binary (done)"] --> B
B --> C["C: golden tests + double-run<br/>byte-equality check"]
B --> D["D: embed heredoc in osv-scan.yml<br/>+ SBOM-generate step"]
C --> E["E: drift check in self-test<br/>(heredoc ≡ tools file)"]
D --> E
C --> F["F: flake.nix — nix flake check<br/>runs the same tests"]
E --> G["G: push; self-test green on runner"]
G --> H["H: front-desk caller SHA bump<br/>→ first real deno.lock scan"]
G --> I["I: infra caller SHA bump<br/>(no deno.lock — pin currency only)"]
H --> J["J: report coverage counts on infra#104<br/>npm / @jsr / jsr-native"]
F -.->|"human: nix flake lock once"| F2["F2: flake.lock committed"]
style A fill:#2d5,stroke:#161,color:#000
style A2 fill:#2d5,stroke:#161,color:#000
Loading
Checklist
A — lock format inspected (v5; npm section keys carry _peer@ver suffixes; names may contain __)
A2 — SBOM filename detection verified against the binary
B — converter
C — golden + determinism tests
D — workflow integration
E — heredoc↔file drift check
F — flake.nix (F2: nix flake lock needs a human with Nix)
G — self-test green
H — front-desk adoption (SHA bump)
I — infra pin currency (optional, defer OK)
J — coverage numbers reported on infra#104
re-verify "OSV has no JSR ecosystem" from a machine with open egress
osv-scanner 2.4.0 has no
deno.lockextractor, so Deno-first repos in this org get a green check having scanned nothing — the gap flagged in infra#104. This issue is the plan to close the closable part of it.The insight that makes this cheap
A v5
deno.lockalready contains the fully-resolved npm dependency graph. Verified againstfront-desk-scheduler's lock: 107 entries in thenpmsection (names, exact versions, integrity hashes) vs 3 injsr. Deno's npm-compat layer did the resolution work; we only need to re-encode it as something the scanner reads.Also verified locally against the real v2.4.0 binary: a CycloneDX 1.5 file named
*.cdx.jsonis auto-detected byscan source -rand scoped npm purls (pkg:npm/%40hono/node-server@1.19.15) extract cleanly. No flags, no scanner changes — drop a generated SBOM next to the lock and the existing scan picks it up.Three buckets, stated honestly
mysql2@3.13.0,@hono/node-server@jsr/bounded-systems__verbspecnpm.jsr.io, not npmjs; no OSV advisory can exist under that purl, so including them would inflate the "scanned" count dishonestly. Excluded from the SBOM, counted in the log.deno.land/xremotesjsr:@bounded-systems/verbspecapi.osv.devis egress-blocked from the authoring session)The lane's log will print all three counts per lock, so a green check states its own blind spot.
Determinism requirements (the Nix-flakes lens)
The converter is a pure function: same
deno.lockbytes → byte-identical SBOM, forever, on any machine.serialNumber(both optional in CycloneDX; the usual UUID default is exactly the nondeterminism to refuse)json.dumps(sort_keys=True), fixed separators, trailing newlineflake.nixprovides the same Python + test runner sonix flake checkreproduces CI's verdict locally. CI itself does not install Nix — the lane stays lean; the flake is the local-reproducibility face of the same test suite. (flake.lockneeds onenix flake lockrun on a machine with Nix — the authoring session has none.)Single-definition constraint
A reusable workflow's steps run in the caller's checkout —
tools/from this repo isn't on disk there. So the script is embedded inosv-scan.ymlas a heredoc, and tracked astools/deno-lock-cdx.pyfor tests — with a drift check inself-test.ymlthat extracts the heredoc and byte-compares it against the file (the same keep-the-copies-honest pattern as infra'sproofs/check-sync.mjs). Golden fixtures are synthetic (made-up package names) so a future real-world advisory can never redden the self-test for unrelated reasons; goldens are named*.golden, never*.cdx.json, so the scan job never ingests them.Considered: deploy conversion/scanning as a service (Cloudflare or elsewhere)
Raised during planning. Split it in two:
schedule:cron in each caller (rescan weekly, zero new infrastructure), which adopters can turn on per-repo. A central Worker that fetches locks across repos would need a standing GitHub credential — the exact thing the broker architecture exists to avoid. Revisit only if per-repo crons prove unmanageable at fleet scale.DAG
flowchart TD A["A: inspect real deno.lock v5<br/>(107 npm / 3 jsr — done)"] --> B["B: tools/deno-lock-cdx.py<br/>pure, stdlib-only, sorted output"] A2["A2: verify *.cdx.json detection<br/>vs real binary (done)"] --> B B --> C["C: golden tests + double-run<br/>byte-equality check"] B --> D["D: embed heredoc in osv-scan.yml<br/>+ SBOM-generate step"] C --> E["E: drift check in self-test<br/>(heredoc ≡ tools file)"] D --> E C --> F["F: flake.nix — nix flake check<br/>runs the same tests"] E --> G["G: push; self-test green on runner"] G --> H["H: front-desk caller SHA bump<br/>→ first real deno.lock scan"] G --> I["I: infra caller SHA bump<br/>(no deno.lock — pin currency only)"] H --> J["J: report coverage counts on infra#104<br/>npm / @jsr / jsr-native"] F -.->|"human: nix flake lock once"| F2["F2: flake.lock committed"] style A fill:#2d5,stroke:#161,color:#000 style A2 fill:#2d5,stroke:#161,color:#000Checklist
npmsection keys carry_peer@versuffixes; names may contain__)nix flake lockneeds a human with Nix)