From e81dcf27b08c18a81f12c7c0535b9bbd931ec215 Mon Sep 17 00:00:00 2001 From: maximilliangrand <214999687+maximilliangrand@users.noreply.github.com> Date: Wed, 12 Aug 2026 20:34:46 +0200 Subject: [PATCH] fix: don't let a null comparator set mask the minimum of another union branch `minVersion` derives a candidate for each comparator set while ignoring `<` / `<=` comparators, so a set that is a null set (eg `^1 ^2`) still produces one. The candidate was only validated against the range once, after the global minimum had already been chosen, so a null set whose candidate is lower than every satisfiable branch's would win the comparison, fail the final check, and make the whole call return null: semver.satisfies('3.0.0', '^1 ^2 || >=3') // true semver.minVersion('^1 ^2 || >=3') // null, expected 3.0.0 Validate each candidate before it becomes the running minimum instead. Ranges that genuinely match nothing still return null, since every candidate then fails the check. Co-Authored-By: Claude Opus 5 (1M context) --- ranges/min-version.js | 12 ++++++------ test/ranges/min-version.js | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ranges/min-version.js b/ranges/min-version.js index 09a65aa3..d9d79574 100644 --- a/ranges/min-version.js +++ b/ranges/min-version.js @@ -49,15 +49,15 @@ const minVersion = (range, loose) => { throw new Error(`Unexpected operation: ${comparator.operator}`) } }) - if (setMin && (!minver || gt(minver, setMin))) { + // Maximum versions are ignored above, so a comparator set that is a null + // set (eg `^1 ^2`) still yields a candidate. Check the candidate against + // the range here rather than once at the end, so that such a set cannot + // mask the minimum of another set in the union. + if (setMin && (!minver || gt(minver, setMin)) && range.test(setMin)) { minver = setMin } } - if (minver && range.test(minver)) { - return minver - } - - return null + return minver } module.exports = minVersion diff --git a/test/ranges/min-version.js b/test/ranges/min-version.js index eeb9b725..984b1aa1 100644 --- a/test/ranges/min-version.js +++ b/test/ranges/min-version.js @@ -64,8 +64,15 @@ test('minimum version in range tests', (t) => { ['>2 || >1.0.0-0', '1.0.0-0.0'], ['>2 || >1.0.0-beta', '1.0.0-beta.0'], + // A null set in a union must not mask the minimum of the other sets + ['^1 ^2 || >=3', '3.0.0'], + ['>=2 <1 || >=3', '3.0.0'], + ['1.x 2.x || >=3', '3.0.0'], + ['>=3 || ^1 ^2', '3.0.0'], + // Impossible range ['>4 <3', null], + ['^1 ^2 || >4 <3', null], ].forEach((tuple) => { const range = tuple[0] const version = tuple[1]