From bac51e5ecfde6c9a29b422e51a89f32f8cc265a0 Mon Sep 17 00:00:00 2001 From: Xinjiang Shao Date: Tue, 30 Jun 2026 10:13:01 -0500 Subject: [PATCH 1/2] fix(adapter-nextjs): allow Next.js preview/prerelease version tags Next.js now ships builds under a preview tag (e.g. next@preview), which resolves to prerelease versions like "16.3.0-preview". semver's satisfies() excludes prerelease versions from a range unless a matching prerelease comparator already exists for the same [major, minor, patch] tuple, so these safe preview builds were wrongly flagged as vulnerable and blocked by checkNextJSVersion. Pass { includePrerelease: true } so preview/canary builds are matched against SAFE_NEXTJS_VERSIONS. All existing canary boundary checks (e.g. <14.3.0-canary.77) still behave identically. --- packages/@apphosting/adapter-nextjs/src/utils.spec.ts | 4 ++++ packages/@apphosting/adapter-nextjs/src/utils.ts | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/@apphosting/adapter-nextjs/src/utils.spec.ts b/packages/@apphosting/adapter-nextjs/src/utils.spec.ts index bea48526f..c9906406e 100644 --- a/packages/@apphosting/adapter-nextjs/src/utils.spec.ts +++ b/packages/@apphosting/adapter-nextjs/src/utils.spec.ts @@ -57,6 +57,10 @@ describe("block vulnerable nextjs versions", () => { assert.doesNotThrow(() => { checkNextJSVersion("16.0.7"); }); + + assert.doesNotThrow(() => { + checkNextJSVersion("16.3.0-preview"); + }); }); }); diff --git a/packages/@apphosting/adapter-nextjs/src/utils.ts b/packages/@apphosting/adapter-nextjs/src/utils.ts index 5ea568d7e..db8aa175e 100644 --- a/packages/@apphosting/adapter-nextjs/src/utils.ts +++ b/packages/@apphosting/adapter-nextjs/src/utils.ts @@ -27,7 +27,12 @@ export function checkNextJSVersion(version: string | undefined) { if (!version) { return; } - if (!satisfies(version, SAFE_NEXTJS_VERSIONS)) { + // includePrerelease lets preview/canary builds (e.g. the `next@preview` tag, + // which resolves to versions like "16.3.0-preview") be matched against the + // range. Without it, semver excludes any prerelease whose [major, minor, patch] + // tuple isn't already present as a prerelease comparator in the range, so a safe + // preview build would be wrongly flagged as vulnerable and blocked. + if (!satisfies(version, SAFE_NEXTJS_VERSIONS, { includePrerelease: true })) { throw new Error( `CVE-2025-55182: Vulnerable Next version ${version} detected. Deployment blocked. Update your app's dependencies to a patched Next.js version and redeploy: https://nextjs.org/blog/CVE-2025-66478#fixed-versions`, ); From a85a9e9ac2891f305c11b2833955d4beb35bc169 Mon Sep 17 00:00:00 2001 From: Xinjiang Shao Date: Tue, 30 Jun 2026 10:27:22 -0500 Subject: [PATCH 2/2] fix(adapter-nextjs): scope includePrerelease to the >=16.1.0 range Per review: applying { includePrerelease: true } to the whole SAFE_NEXTJS_VERSIONS range relies on semver's prerelease-aware tilde bounds and loosens the bounded `~`/`<` comparators of older lines. Instead, run the default strict check (stable versions + canary boundary) and only enable includePrerelease for the open-ended >=16.1.0 range, where every prerelease is guaranteed to be on the patched line. Adds regression tests asserting prereleases of unpatched minors (16.1.0-canary.2, 15.1.0-canary.2) stay blocked. --- .../adapter-nextjs/src/utils.spec.ts | 11 ++++++++++ .../@apphosting/adapter-nextjs/src/utils.ts | 21 +++++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/@apphosting/adapter-nextjs/src/utils.spec.ts b/packages/@apphosting/adapter-nextjs/src/utils.spec.ts index c9906406e..03ebc469b 100644 --- a/packages/@apphosting/adapter-nextjs/src/utils.spec.ts +++ b/packages/@apphosting/adapter-nextjs/src/utils.spec.ts @@ -61,6 +61,17 @@ describe("block vulnerable nextjs versions", () => { assert.doesNotThrow(() => { checkNextJSVersion("16.3.0-preview"); }); + + // A prerelease of an unpatched minor must NOT slip past the bounded ranges: + // 16.1.0-canary.2 sorts below 16.1.0 (the first patched release) and + // 15.1.0-canary.2 below the patched 15.1.9, so both stay blocked. + assert.throws(() => { + checkNextJSVersion("16.1.0-canary.2"); + }); + + assert.throws(() => { + checkNextJSVersion("15.1.0-canary.2"); + }); }); }); diff --git a/packages/@apphosting/adapter-nextjs/src/utils.ts b/packages/@apphosting/adapter-nextjs/src/utils.ts index db8aa175e..3819a0b8f 100644 --- a/packages/@apphosting/adapter-nextjs/src/utils.ts +++ b/packages/@apphosting/adapter-nextjs/src/utils.ts @@ -23,16 +23,25 @@ export const { satisfies } = semVer; const SAFE_NEXTJS_VERSIONS = ">=16.1.0 || ~16.0.7 || ~v15.5.7 || ~v15.4.8 || ~v15.3.6 || ~v15.2.6 || ~v15.1.9 || ~v15.0.5 || <14.3.0-canary.77"; +// The latest line is patched from 16.1.0 onward, so any prerelease of a version +// >= 16.1.0 (e.g. the `next@preview` tag, which resolves to "16.3.0-preview") is +// safe. We only enable includePrerelease for this open-ended range. Applying it +// to the whole SAFE_NEXTJS_VERSIONS range would loosen the bounded `~`/`<` +// comparators of older lines, potentially letting a prerelease of an unpatched +// minor slip through. +const SAFE_NEXTJS_PRERELEASE_RANGE = ">=16.1.0"; + export function checkNextJSVersion(version: string | undefined) { if (!version) { return; } - // includePrerelease lets preview/canary builds (e.g. the `next@preview` tag, - // which resolves to versions like "16.3.0-preview") be matched against the - // range. Without it, semver excludes any prerelease whose [major, minor, patch] - // tuple isn't already present as a prerelease comparator in the range, so a safe - // preview build would be wrongly flagged as vulnerable and blocked. - if (!satisfies(version, SAFE_NEXTJS_VERSIONS, { includePrerelease: true })) { + // Default (strict) matching covers stable versions and the canary boundary, + // then scoped includePrerelease admits only prereleases of the patched >=16.1.0 + // line. A version is vulnerable only if it satisfies neither. + if ( + !satisfies(version, SAFE_NEXTJS_VERSIONS) && + !satisfies(version, SAFE_NEXTJS_PRERELEASE_RANGE, { includePrerelease: true }) + ) { throw new Error( `CVE-2025-55182: Vulnerable Next version ${version} detected. Deployment blocked. Update your app's dependencies to a patched Next.js version and redeploy: https://nextjs.org/blog/CVE-2025-66478#fixed-versions`, );