Notes on coding agents and the pages they leave behind, on moving between notes apps, and on the plain mechanics of keeping what you think. Written and published from a Sumibako vault.
Next.js security update, October 2026: my own site was on the list
Next.js fixed two critical RCEs on 14 October, Node.js four high bugs on 21 October. My site ran 16.3.3. How I checked which applied, and the one that did.
Prashant Shaw
Founder, Sumibako
10 min read
On 14 October, Next.js shipped a security update with two critical remote code execution fixes in it. On 21 October, Node.js shipped ten fixes, four of them high, one of them remote code execution. I read both announcements the way you read a recall notice for a car: with interest, and with the quiet hope that it is somebody else's model.
It was my model. Sumibako runs on Next.js, and until this week it ran on 16.3.3. This is what I found when I checked which of the week's holes were mine, what I did about it, and what I would do faster next time.
🗓️ Three security releases in five weeks
Next.js moved to a formal security release process in July. The announcement is candid about why: "Historically, the team has published ad-hoc patches for security fixes." Since then there has been a scheduled release each month, plus out-of-band ones when an upstream bug will not wait.
Autumn did not wait. On 22 September an out-of-band release fixed a critical bug in the Node.js ImageResponse from next/og, in every version from 16.2.0 up to the one before 16.3.6. "Applications using the Edge ImageResponse implementation are not affected." Mine is not on the edge. On 30 September the scheduled release brought 16.3.8 with a server-side request forgery in Image Optimization and two cache poisonings. On 14 October the second out-of-band release brought 16.4.1, with two critical bugs that had been "postponed from the September security release due to upstream dependency delays".
Where 16.3.3 sat. Dates from the npm registry and the Next.js blog.
That is three security releases between 16.3.3 and the version I should have been on. I was not on any of them. Nothing was wrong with the releases. The version was pinned in my package.json, nothing was watching it, and I was busy writing blog posts about other people's memory limits.
🧪 The one that applied to me
The September bug is the one I least enjoyed reading. Every published Sumibako page has a social card, the picture a link shows when you paste it into a chat. It is drawn on the server by ImageResponse, and it carries the page's own title and its opening line. The advisory for GHSA-vcvr-r3jv-pc5j says which applications are safe: "Applications using the Edge ImageResponse implementation, or applications that do not pass attacker-controlled values into SVG content, attributes, or styles, are not affected."
I run the Node.js implementation, because Cache Components refuse the edge runtime. And a page's title is about as attacker-controlled as a value gets: anyone can open a free account, publish a page and give it any title they like. So between 22 September and 24 October, when the upgrade went live, a crafted title on a published page was a path into my server. I have no evidence anyone walked it, and on a hobby hosting plan I have very little evidence of anything. I am telling you because the post would be worthless if I only told you about the bugs that missed me.
📝NOTE
The advisory's workaround, for anyone who cannot upgrade today: "do not pass attacker-controlled values into SVG content, attributes, or styles" that the Node.js implementation renders. For a social card that quotes a user's title, that means not drawing the title. Upgrading is easier.
🧯 The two that did not, and why I upgraded anyway
The October pair is louder, both rated 9.5 by Vercel, and both missed me, which I checked rather than assumed.
The first, GHSA-673q-mjm6-r5xr, is in use cache, the caching model Next.js now recommends for every app. The condition is specific: "The application needs to have a use cache function that returns multiple typed arrays." I have four use cache functions. They return a document, a string, a list of pages for the sitemap and a Markdown file. No typed arrays. React's own advisory for the underlying bug calls these "extremely rare cases where responses transport multiple typed arrays and an attacker-controlled value."
The second, CVE-2026-96889, is in the SVG decoder that Image Optimization reaches through the sharp package, which reaches librsvg. The release post explains the shape of it: "A memory-related vulnerability in an upstream library used for Image Optimization can lead to remote code execution when Next.js runs on glibc-based Linux." Sumibako turned Image Optimization off two weeks ago, for the unglamorous reason that the free hosting allowance ran out. The endpoint on the live site answers 402. For once, being poor was a security control.
Scores, for what they are worth, disagree on that one. The Hacker News reports that "Red Hat scored it 7.8 and said exploiting it needs user interaction, such as opening a malicious file." Vercel scored the Next.js exposure 9.5, because an image optimizer opens files for a living.
So why upgrade a site that two of three advisories did not reach? Because the release post says to, in words I would like framed: "You should upgrade immediately even if your application does not have similar functions." Because the third October fix, a denial of service through Server Actions, did apply, since Sumibako has a Server Action. And because the next advisory will not care how carefully I read this one.
Advisory
Fixed in
Published
Did it reach Sumibako?
GHSA-vcvr-r3jv-pc5j, next/og ImageResponse RCE
16.3.6
22 Sep
Yes: the social card draws a page's own title
GHSA-673q-mjm6-r5xr, use cache typed arrays RCE
16.4.1
14 Oct
No: no use cache function returns typed arrays
GHSA-2464-fhr4-w9xw, Image Optimization SVG RCE
16.4.1
14 Oct
No: the optimizer is off and the endpoint answers 402
GHSA-p3m5-59mp-jv5r, Server Actions DoS
16.4.1
14 Oct
Yes: there is one Server Action
🔍 What npm audit knew, and when
Before touching anything I ran npm audit. It reported nineteen advisories, and against next alone it listed eleven, including all three from 14 October and the one from 22 September. So the tool knew. It had known for days. I had not run it.
It did not always know. Two days after the release, an engineering blog pointed out that the use cache bug had no CVE and had not yet been reviewed into the advisory database npm reads: "And npm audit was not showing two of the three advisories when this release shipped." Their advice, "Do not trust a clean npm audit yet", is good advice for the first week of any security release. The vendor's blog post is the primary source; the audit tool catches up.
After the upgrade, seventeen advisories remain in my tree. All of them are in tooling that never runs on the server: the linter's template engine, the service worker build, a sanitiser in a dev dependency. They are on my list, below the things that answer HTTP.
🟩 Node's turn: Brotli, fullwidth digits and a certificate authority
Node's 21 October release fixed ten issues across the 26, 24 and 22 lines, with 26.12.1, 24.22.1 and 22.23.4 as the versions to be on. The one that earns the word remote is in the Brotli decoder: "An attacker who can supply Brotli data that an application decompresses with node:zlib can corrupt process memory, which can lead to a crash, disclosure of process memory, or remote code execution."
Two of the four high-severity bugs are stranger. Both are in TLS hostname checking, and both come down to how an IP address is written. One: a certificate without a matching address could authenticate a connection "when the host is written with non-ASCII characters, such as fullwidth digits ( 127.0.0.1 )." The other: a leading zero, as in 0127.0.0.1, parsed differently by the URL rules and by the operating system. Twenty-six years into the language, there are still ways to spell localhost that nobody had agreed on.
And then a line in the list of dependency updates: "ca-cert (eMudhra Intermediate CA Compromise)". Node's bundled list of trusted certificate authorities changed this week because an Indian certificate authority's intermediate keys turned out to be recoverable. That story is a post of its own. The point here is that the fix for a certificate authority's mistake arrives in your runtime's release notes, and only reaches you when you take the release.
Who takes it depends on where the code runs. For the site, Vercel does: "Only major versions are available. Vercel automatically rolls out minor and patch updates when needed, such as to fix a security issue." For anything I run myself, I do. The machine I am typing on is on 22.17.1, which is more than a year old, and writing that sentence down was apparently the reminder I needed. Node's own rule for all of them: "Production applications should only use Active LTS or Maintenance LTS releases."
🧰 What I changed, in one evening
The upgrade itself was the short part, since 16.4.3 was one day old and sharp 0.35.5 is the version the advisory names.
A production build compiled in 32 seconds. The repository's own checks passed, all 1,438 of them, and the linter had nothing new to say. The commit is a change to two lines of package.json and a long one to the lockfile. The seventeen that remain are the tooling advisories from the previous section.
Two things in 16.4 made me wince in hindsight. The release post says: "Starting with Next.js 16.4, we're excited to recommend Cache Components as the best choice for every Next.js app." I have been on Cache Components since 16.0, which is why I read the use cache advisory twice. And 16.4 introduces experimental.agentUpgrade, whose default policy is to "Remind you about upgrades that address known vulnerabilities affecting your installed version." A nag, built into the framework, for exactly the month I just had. It would have fired on 22 September. It only exists from 16.4, which is where I now am.
📋 If you run Next.js or Node, this week
1️⃣ Find out what you are running. npx next --version and node -v, on the machine that serves, not the one you type on. endoflife.date keeps the supported lines; 15's security support ended on 21 October.
2️⃣ Run npm audit and read the next and sharp lines before anything else. If it is clean in the first days after a release, read the vendor's post anyway.
3️⃣ Ask the three questions I asked. Does any use cache function return typed arrays? Does any ImageResponse draw text a stranger can write? Is Image Optimization on, and on glibc Linux? Three greps answer them in a minute. Then upgrade regardless of the answers, because Vercel asked you to.
4️⃣ Check who patches your Node. A managed host usually does the minor versions for you. A machine you own does not patch itself, and the Brotli bug is in every line.
5️⃣ On 16.4, leave agentUpgrade at its default so the next one finds you instead.
The wider week, with PHP, Railway and the rest, is in the state of the internet this month, and the habits that would have saved me the evening are in a checklist for the next one. This post is the one where I admit the checklist is written from experience.
Eight eMudhra keys were integers under 256, one of them 4. Why browsers blocked them in hours, why your Docker image did not, and what to check by 27 October.
10 min read
$ npm audit # condensed; the real output runs to a few screens
19 vulnerabilities (5 low, 1 moderate, 8 high, 5 critical)
next 16.0.0 - 16.4.0
critical Unauthenticated Remote Code Execution in `use cache` functions returning typed arrays
critical Unauthenticated Remote Code Execution in Image Optimization API when SVG files are used
critical Remote Code Execution in next/og ImageResponse
high Denial of Service in App Router using Server Actions
... seven more
sharp <=0.35.5-rc.1
high Vulnerability in librsvg dependency CVE-2026-96889
grep -rln 'use cache' src # which functions are cached; read what each returnsgrep -rn ImageResponse src # which cards are drawn, and from whose textgrep -n unoptimized next.config.* # whether the Image Optimization endpoint is live
typescript
// next.config.ts, from 16.4 on. 'security' is the default; this only makes it explicit.const nextConfig = { experimental: { agentUpgrade: 'security' },};export default nextConfig;