unpopulardev

The version policy lost to a CVE

My rule is to run the latest stable minor that has been out three months. For Astro that points at 6.4.8, which carries an unpatched critical RCE. The rule and the security floor cannot both hold, so one of them gives.

Picking a version for a blog should take a minute. This one took an afternoon, because the house rule and the security floor pointed at different releases and I had to decide which one was actually load-bearing.

The rule: run the latest stable minor that has been out at least three months. It buys the post-release point-fix wave — the X.Y.0 → X.Y.2 churn — without sitting on something EOL. On 2026-09-16 that pointed at Astro 6.4.8. The 6.4 line opened 2026-05-28, comfortably past the window.

Then I checked the advisory database.

What the advisories say

Three, all on the same shape.

GHSA-26w7-cxv4-gfx2 — critical, remote code execution through AVIF image optimization. Affected range < 7.2.8. First patched version 7.2.8.

That range swallows every 6.x release that exists. The fix was not backported. Two others rhyme with it: CVE-2026-73422 (reflected XSS via View Transition animation properties, <= 7.0.9, patched 7.1.0) and CVE-2026-84376 (authorization bypass, <= 7.2.3, patched 7.2.4). Neither backported either.

Astro 7.0 shipped 2026-06-22. Every security fix since has landed on 7.x only. So 6.x isn’t merely older — it is unmaintained for security, and “three months old” and “no known vulnerabilities” have stopped being satisfiable at the same time.

The part I got wrong first

My first instinct was to take the newest release and move on. 7.3.3 is latest.

That’s the wrong read of my own rule. The rule exists to avoid being first to find a regression, and 7.3 opened 2026-09-03 and took three patches in its first five days. It is mid-wave. Jumping to it trades a known CVE for an unknown regression, which is not obviously a trade at all.

The rule, applied properly, says: take the oldest line that carries the fix. That’s 7.2. It opened 2026-08-06, has had eleven patches, and the last was 2026-08-31 — its wave is over.

So: 7.2.10. Latest patch on the oldest line that clears the critical.

"astro": "7.2.10",

Exact, not ^7.2.10. A caret here would silently walk me onto 7.3 the first time someone ran a fresh install, which is the entire thing I just decided against.

Receipts

Resolved tree, Node 24.21.0:

$ npm audit --audit-level=high
found 0 vulnerabilities

That’s the number that matters, and it’s the resolved tree rather than the six direct dependencies. Astro pulls Vite transitively and Vite has a long advisory history, so auditing only what’s in package.json would be auditing the wrong thing.

The install is pinned by a committed package-lock.json and npm ci in CI — never npm install. Without that the table of pins is aspirational; with it, it’s true at build time.

I have not verified the AVIF exploit myself. I’m taking the advisory’s affected range at face value, which is the normal thing to do and worth saying out loud rather than implying I reproduced it.

What I’d do differently

Check the advisory database before applying the version rule, not after. I spent the first half of this deciding on 6.4.8 using a process that could never have produced a safe answer, because the input it needed wasn’t in front of me yet. The rule isn’t wrong — it just isn’t the first step.

The revisit date is 2026-12-03, when 7.3 turns three months old, or whenever the next advisory forces it.