Skip to content

ci: adopt the shared osv-scan lane (infra#104) - #20

Merged
bdelanghe merged 5 commits into
mainfrom
claude/deps-osv-scan
Jul 31, 2026
Merged

ci: adopt the shared osv-scan lane (infra#104)#20
bdelanghe merged 5 commits into
mainfrom
claude/deps-osv-scan

Conversation

@bdelanghe

Copy link
Copy Markdown
Collaborator

Byte-identical copy of templates/deps.yml pinned to osv-scan.yml@162accb, verified with diff. Adopted at hard-fail (ci-workflows#15).

Real coverage

This repo carries deno.lock, so the scan has something to work on — green here will mean "scanned and clean", not "nothing to scan".

Deno is the harder ecosystem to remediate

Worth flagging up front: static-mcp needed a parent bump and a lockfile promotion for the same three transitive packages that npm resolved correctly on a plain relock (bounded-tools-mcp#3 cleared all five findings at rung 1, no manifest change). If this lane reds, rung 1 is less likely to suffice here than it was on the npm repos.

Triage order if red

REMEDIATION.md — rung 1 first, stop at the first rung that works, don't skip to an acceptance. Grace is a dated exception now, not a default.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

osv / osv-scan is red with 9 advisories across 4 packages. I can fix half of it and am blocked on the other half by an environment egress denial, so I'm reporting rather than pushing something unverified.

The findings

Targets are the highest fix per package — fast-uri alone carries four advisories with four different fix lines, and taking the first row would leave three live.

package locked target why
ajv 8.17.1 8.18.0 GHSA-2g4f-4pwh-qvx6 (5.5)
fast-uri 3.1.0 3.1.4 4 advisories, all 7.5
linkify-it 5.0.0 5.0.2 GHSA-22p9-wv53-3rq4 (8.7), GHSA-v245-v573-v5vm
markdown-it 14.1.0 14.2.0 GHSA-38c4-r59v-3vqw, GHSA-6v5v-wf23-fmfq

Rung 1 clears two of the four — verified

ajv is a direct import at ^8.17.1, so the declared range already permits the fix. A relock resolves ajv 8.20.0 and fast-uri 3.1.4 with no deno.json change.

What blocks the other two

linkify-it and markdown-it come in through lume, imported by pinned URL (https://deno.land/x/lume@v3.1.4/). A lockfile relock can't move them — that's a rung 2 parent bump. Evaluating one means resolving lume's graph, and lume pulls vento from cdn.jsdelivr.net, which this environment's network policy 403s at CONNECT:

"kind": "connect_rejected",
"detail": "gateway answered 403 to CONNECT (policy denial or upstream failure)",
"host": "cdn.jsdelivr.net:443"

deno.land, jsr.io and registry.npmjs.org all resolve fine — it's specifically jsdelivr. Same class of gap as the api.osv.dev denial that started this work, and the durable fix is the same: add the host to cloud-environment.json.

A trap worth recording

deno install on its own does produce a lockfile that scans green — because it drops the lume-derived graph entirely, 52 npm entries down to 7. markdown-it and linkify-it stop being findings because they stop being present. Regenerating through the real entrypoints (deno cache src/unfold/server.ts …) keeps all 52 and is what hits the jsdelivr wall.

That green would have been a lie of exactly the kind this rollout exists to remove, so I'm flagging it for REMEDIATION.md: on deno repos, check the lock's entry count before and after a relock. A shrinking lock is a false green, not a fix.

State

The lockfile in this branch is byte-identical to main — I restored it after each experiment; nothing unverified has been pushed. The lane is honestly red.

Unblocking needs cdn.jsdelivr.net allowlisted; then rung 1 + a lume bump can be evaluated properly.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

The jsdelivr blocker is gone — cdn.jsdelivr.net went 000 → 301 live once it was allowlisted, no session restart. 8 of 9 advisories are now cleared. One remains, and it's genuinely stuck upstream.

Cleared

package advisories
ajv 8.17.1 → 8.20.0 GHSA-2g4f-4pwh-qvx6
fast-uri 3.1.0 → 3.1.4 all four
linkify-it 5.0.0 → 5.0.2 GHSA-22p9-wv53-3rq4 (8.7), GHSA-v245-v573-v5vm
markdown-it 14.1.0 → 14.1.1 GHSA-38c4-r59v-3vqw

Everything except markdown-it came from rung 1 with deno.json untouched. markdown-it needed rung 2 — lume v3.1.4 → v3.2.5.

The one left, and why it isn't laziness

GHSA-6v5v-wf23-fmfq on markdown-it 14.1.1 needs 14.2.0. Latest lume pins 14.1.1 exactly, so no lume version reaches it. I checked deps/markdown_it.ts in v3.2.2, v3.2.4 and v3.2.5 directly rather than inferring.

That leaves a lockfile promotion or a dated acceptance. Both are judgment calls — your call, not mine. It's the lowest-severity finding of the nine (5.3).

A correction

My previous comment said "v3.2.1 is the newest lume published." That was wrong, and wrong in an instructive way: I'd been guessing version numbers one at a time against deno.land, which skipped v3.2.2/v3.2.4/v3.2.5 entirely. Once cdn.deno.land was allowlisted the registry answered definitively — 213 versions, latest v3.2.5 — and that's what made the eighth fix possible. Guessing looked like checking right up until it didn't.

Lock went 52 → 46 → 68

The 52 → 46 dip was correct: what left was nunjucks and its four deps plus a duplicate zod. Nothing in src/ imports lume's nunjucks plugin (site.ts imports json_ld, metas, robots, sitemap, vento), so those were stale entries the committed lock had been carrying. markdown-it stayed present and stayed reported — the proof the scan didn't go quiet by omission. Then lume 3.2.5 brought the count to 68.

Verification limit

deno check src/unfold/site/site.ts passes against the new lume. deno test fails, and failed identically before this changemanifest.ts imports src/unfold/site/site_url.ts, which doesn't exist in the repo. Pre-existing and unrelated to dependencies (verified by stashing), but it does mean the test suite can't corroborate the lume major bump.


Generated by Claude Code

claude added 4 commits July 31, 2026 03:26
Byte-identical copy of ci-workflows templates/deps.yml, pinned to
osv-scan.yml@162accb, verified with `diff`. Adopted at HARD-FAIL
(ci-workflows#15 flipped the template default), so green means genuinely clean
rather than green by configuration.

This repo carries deno.lock, so the scan has real coverage — its green will
mean "scanned and clean" rather than "nothing to scan". Several repos in this
wave only earn the second claim, so the distinction is worth stating.

Deno is also the ecosystem where remediation has been hardest across this
rollout: static-mcp needed a parent bump AND a lockfile promotion for the same
transitive packages that npm resolved correctly on a plain relock. If this
lane reds, expect rung 1 to be less likely to suffice here than it was on the
npm repos.

ci-workflows/REMEDIATION.md is the triage order: rung 1 first, stop at the
first rung that works, do not skip to an acceptance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LF9Cu8u4dkCEM8MXC1QeDL
…dvisories

The blocker reported earlier in this PR is gone: cdn.jsdelivr.net went from a
403 at CONNECT to 301 once it was added to the environment's domain allowlist,
live, with no session restart. That made lume's graph resolvable and the
lockfile regenerable.

RUNG 1 — relock only, deno.json UNCHANGED:

  ajv         8.17.1 -> 8.20.0   clears GHSA-2g4f-4pwh-qvx6
  fast-uri    3.1.0  -> 3.1.4    clears all four fast-uri advisories
  linkify-it  5.0.0  -> 5.0.2    clears GHSA-22p9-wv53-3rq4 (CVSS 8.7)
                                 and GHSA-v245-v573-v5vm
  mdurl       2.0.0  -> 2.1.0    (incidental)

Seven of the nine, including the highest-severity finding in this wave.

STILL OPEN: markdown-it 14.1.0 (GHSA-38c4-r59v-3vqw 5.5, GHSA-6v5v-wf23-fmfq
5.3), needs 14.2.0. This is blocked upstream, not by effort. lume pins
markdown-it at EXACTLY 14.1.0 in deps/markdown_it.ts, so no relock can move it,
and rung 2 does not help either: v3.2.1 is the newest lume published and it
carries the same exact pin. Verified by fetching both versions' deps file
rather than assuming. The remaining path is a lockfile promotion or a dated
acceptance, and neither should be taken silently — left for a human call.

LOCK SHRANK 52 -> 46 npm ENTRIES, AND THAT IS CORRECT HERE — checked, per the
rule this wave established. What left: nunjucks and its four deps
(@types/nunjucks, a-sync-waterfall, asap, commander), plus a duplicate zod
3.24.1 collapsing into 3.25.76. Nothing in src/ imports the lume nunjucks
plugin — site.ts imports json_ld, metas, robots, sitemap and vento — so those
were stale entries the committed lock had been carrying, not coverage being
dropped. No package carrying an advisory disappeared; markdown-it is still
present and still reported, which is the proof the scan did not go quiet by
omission.

Verified against api.osv.dev directly: ajv, fast-uri, linkify-it and mdurl all
return zero advisories at the resolved versions; markdown-it still returns its
two. `deno check src/unfold/site/site.ts` passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LF9Cu8u4dkCEM8MXC1QeDL
Correction to the previous commit's claim. It said "v3.2.1 is the newest lume
published"; that was wrong, and wrong because of how it was checked. Version
numbers were guessed one at a time against deno.land, which skipped v3.2.2,
v3.2.4 and v3.2.5. Once cdn.deno.land was allowlisted the authoritative list
was available: 213 versions, latest v3.2.5.

RUNG 2: lume v3.1.4 -> v3.2.5 in deno.json (both the lume/ and lume/plugins/
prefixes), which moves its exact markdown-it pin 14.1.0 -> 14.1.1 and clears
GHSA-38c4-r59v-3vqw.

  8 of 9 advisories now cleared.

STILL OPEN, and genuinely stuck upstream: GHSA-6v5v-wf23-fmfq on markdown-it
14.1.1, which needs 14.2.0. Latest lume pins 14.1.1 exactly, so no lume version
reaches it. Confirmed by reading deps/markdown_it.ts from v3.2.2, v3.2.4 and
v3.2.5 rather than inferring from the version number. Remaining options are a
lockfile promotion or a dated acceptance; both are judgement calls and neither
is being taken silently here.

Lock grew 46 -> 68 npm entries — the opposite of the shrink this wave has been
watching for, and expected, since lume 3.2.5 carries more dependencies than
3.1.4.

Verified: ajv, fast-uri and linkify-it return zero advisories from
api.osv.dev at their resolved versions; markdown-it 14.1.1 returns exactly one.
`deno check src/unfold/site/site.ts` passes against the new lume.

`deno test` fails, and did so identically BEFORE this change: manifest.ts
imports src/unfold/site/site_url.ts, which does not exist in the repo. That is
pre-existing and unrelated to dependencies — verified by stashing these changes
and re-running. It does mean the test suite cannot corroborate the lume major
bump; the typecheck of the lume-importing module is the strongest signal
available here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LF9Cu8u4dkCEM8MXC1QeDL
… 3 fails

Ninth and final finding on this repo. The other eight — including a CVSS 8.7 in
linkify-it — were fixed at rungs 1 and 2 and are not accepted here.

GHSA-6v5v-wf23-fmfq, markdown-it 14.1.1, fixed in 14.2.0, severity 5.3 (the
lowest of the nine).

RUNG 2 IS EXHAUSTED, not skipped. markdown-it is reached only through lume,
whose deps/markdown_it.ts pins npm:markdown-it@14.1.1 EXACTLY. Read that file
from v3.2.2, v3.2.4 and v3.2.5 — the newest published — and all three carry the
same exact pin.

RUNG 3 WAS ATTEMPTED AND DOES NOT APPLY. REMEDIATION.md says to promote the
package to a direct import in deno.json so the resolver has no room to keep a
stale transitive entry. Tried exactly that:

  "markdown-it": "npm:markdown-it@^14.2.0"

and relocked. The lock still resolved markdown-it@14.1.1 and the npm entry
count did not move (68 before, 68 after) — the mapping was inert. An import-map
entry can only redirect a BARE specifier, and lume's is fully qualified, so
there is nothing for the map to intercept; nothing in src/ imports markdown-it
directly either. The dead mapping was reverted rather than committed, since a
manifest diff that fixes no advisory is churn.

That leaves this or vendoring lume, which is a far larger change than a 5.3
warrants.

ignoreUntil = 2026-10-31, three months. Not decoration: osv-scanner fails
closed once the date passes, so this expires on its own rather than becoming
permanent by neglect. The reason field carries the exact command to re-check
whether lume has moved, so whoever hits it next does not have to re-derive any
of the above.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LF9Cu8u4dkCEM8MXC1QeDL
@bdelanghe
bdelanghe force-pushed the claude/deps-osv-scan branch from 20b6585 to 88c9e66 Compare July 31, 2026 03:28
…pinned

Replaces the single-entry acceptance with two, after the lume bump surfaced a
second finding that the earlier commit could not have known about.

GHSA-6v5v-wf23-fmfq  markdown-it 14.1.1  fixed 14.2.0  5.3
GHSA-g7r4-m6w7-qqqr  esbuild     0.28.0  fixed 0.28.1  2.5

The esbuild one was INTRODUCED BY THE LUME BUMP IN THIS PR. Saying so plainly
because it is the kind of thing that otherwise looks like it was always there:
the advisory's range is introduced 0.27.3 / fixed 0.28.1, and lume v3.1.4
pinned 0.27.2 — below the range by luck, not design.

So the bump is a trade, not a free win, and the header comment records it:

  lume v3.1.4  markdown-it 14.1.0 (5.5 + 5.3)  esbuild 0.27.2 (clean)  2 findings
  lume v3.2.5  markdown-it 14.1.1 (5.3)        esbuild 0.28.0 (2.5)    2 findings

Same count either way. v3.2.5 is kept because its worst is 5.3 rather than 5.5,
and because the newest lume is where an upstream fix will actually land.
v3.2.2 was checked as a possible sweet spot and is not one — it pins esbuild
0.27.3, already inside the affected range.

Both entries fail rungs 1-3 for the same structural reason, which is now
written down once and referenced twice: lume pins these packages with exact,
fully-qualified npm specifiers, nothing in src/ imports them, and an import-map
promotion can only redirect a BARE specifier. Verified, not assumed — the
markdown-it promotion was actually attempted and left the lock unchanged at 68
npm entries.

Both carry ignoreUntil 2026-10-31 and the exact curl to re-check whether lume
has moved, so the next person does not re-derive any of this. osv-scanner fails
closed when the date passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LF9Cu8u4dkCEM8MXC1QeDL

Copy link
Copy Markdown
Collaborator Author

Green. 8 of 9 advisories fixed, 2 accepted with expiries — and the second acceptance is one this PR created, which is worth stating plainly.

Rung 3 was tried, and doesn't apply here

REMEDIATION.md says to promote the package to a direct import so the resolver can't keep a stale transitive entry. I did exactly that:

"markdown-it": "npm:markdown-it@^14.2.0"

and relocked. The lock still resolved 14.1.1 and the npm entry count didn't move (68 → 68). The mapping was inert: an import-map entry can only redirect a bare specifier, and lume's is fully qualified (npm:markdown-it@14.1.1). Nothing in src/ imports markdown-it directly either. I reverted the dead mapping rather than commit a manifest diff that fixes nothing.

That's a genuine gap in rung 3's guidance for deno — it works when your code imports the package, not when a URL-imported dependency pins it internally.

The lume bump is a trade, not a free win

Bumping to v3.2.5 cleared one markdown-it advisory and introduced an esbuild one. The esbuild advisory's range is introduced 0.27.3 / fixed 0.28.1, and lume v3.1.4 pinned 0.27.2 — below the range by luck, not design.

markdown-it esbuild findings
lume v3.1.4 14.1.0 — 5.5 + 5.3 0.27.2 clean 2
lume v3.2.5 14.1.1 — 5.3 0.28.0 — 2.5 2

Same count either way. I kept v3.2.5 because its worst is 5.3 rather than 5.5, and the newest lume is where an upstream fix will actually land. v3.2.2 was checked as a possible sweet spot and isn't one — it pins esbuild 0.27.3, already inside the range.

If you'd rather not carry a self-inflicted finding, reverting to v3.1.4 is defensible — it just costs the 5.5.

The acceptances

Both ignoreUntil = 2026-10-31, both carrying the exact curl to re-check whether lume has moved. osv-scanner fails closed once the date passes, so neither becomes permanent by neglect. The esbuild one is CVSS 2.5, local vector, integrity-only (AV:L/AC:H/PR:L/.../C:N/I:L/A:N) — it needs access to a build host.

Net

linkify-it 8.7, four fast-uri 7.5s, ajv, and one markdown-itfixed. Two lows deferred with dates and removal conditions.


Generated by Claude Code

@bdelanghe
bdelanghe marked this pull request as ready for review July 31, 2026 03:39
@bdelanghe
bdelanghe merged commit 06b98e8 into main Jul 31, 2026
5 checks passed
@bounded-systems-front-desk bounded-systems-front-desk Bot moved this from Todo to Done in Front Desk Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants