Skip to content

Correct brace-expansion ranges in GHSA-mh99-v99m-4gvg: fix was backported to 1.x, 2.x and 3.x - #8878

Closed
TechPro01 wants to merge 1 commit into
github:TechPro01/advisory-improvement-8878from
TechPro01:techpro01-GHSA-mh99-v99m-4gvg
Closed

Correct brace-expansion ranges in GHSA-mh99-v99m-4gvg: fix was backported to 1.x, 2.x and 3.x#8878
TechPro01 wants to merge 1 commit into
github:TechPro01/advisory-improvement-8878from
TechPro01:techpro01-GHSA-mh99-v99m-4gvg

Conversation

@TechPro01

Copy link
Copy Markdown

GHSA-mh99-v99m-4gvg / CVE-2026-14257 currently uses a single range for brace-expansion:

{ "introduced": "0" }, { "fixed": "5.0.8" }

Under npm/semver ordering that marks every version below 5.0.8 as vulnerable. But the fix was backported to the 1.x, 2.x and 3.x maintenance branches, so five already-patched releases are currently reported as vulnerable: 1.1.17, 2.1.3, 3.0.3, 3.0.4, 3.0.5.

The package carries maintenance-v1, maintenance-v2 and maintenance-v3 dist-tags, which is consistent with the backports.

Proposed ranges

{ "introduced": "0" },     { "fixed": "1.1.17" },
{ "introduced": "2.0.0" }, { "fixed": "2.1.3" },
{ "introduced": "3.0.0" }, { "fixed": "3.0.3" },
{ "introduced": "4.0.0" }, { "fixed": "5.0.8" }

Note the last pair is deliberate: 4.x has no patched release4.0.1 (2025-06-11) is the newest 4.x and is still vulnerable — so the range from 4.0.0 correctly continues to 5.0.8, i.e. a 4.x user's only fix is the 5.x major.

First patched version per branch

branch first patched published
1.x 1.1.17 2026-07-29
2.x 2.1.3 2026-07-28
3.x 3.0.3 2026-07-27
4.x (none) 4.0.1 is the last 4.x
5.x 5.0.8 2026-07-23

How this was determined

Behaviourally, per version, against the entry point npm actually resolves — not by grepping for a marker and not from advisory metadata. Each version was installed in an isolated project and exercised with an input whose unbounded expansion exceeds the new cap:

const { createRequire } = require('module');
const req = createRequire(require('path').join(process.cwd(), 'package.json'));
const m = req('brace-expansion');
const fn = typeof m === 'function' ? m : (m.expand || m.default);
const total = fn('a'.repeat(50000) + '{1..100}').reduce((s, x) => s + x.length, 0);
// unfixed -> 5,000,192 chars (100 items) | fixed -> 3,950,149 chars (79 items)

Results (all measured, not inferred):

version chars
1.0.0, 1.1.16 5,000,192 vulnerable
1.1.17 3,950,149 fixed
2.0.0, 2.1.2 5,000,192 vulnerable
2.1.3 3,950,149 fixed
3.0.0, 3.0.1, 3.0.2 5,000,192 vulnerable
3.0.3, 3.0.4, 3.0.5 3,950,149 fixed
4.0.0, 4.0.1 5,000,192 vulnerable
5.0.7 5,000,192 vulnerable
5.0.8 3,950,149 fixed

Cross-checking both ranges against those 16 measured versions with an OSV range evaluator: the current range mis-classifies 5 of 16; the proposed range mis-classifies 0 of 16.

Two notes for anyone verifying this themselves:

  • On the 1.x and 2.x lines, package.json is main: "index.js" with no exports. Some of those releases also ship a fixed build under dist/ that nothing resolves to, so the presence of a patched dist/ (or of an advisory file in the tarball) does not mean the installed entry point is fixed — 2.1.2 is exactly this case. Check the file require.resolve() returns.
  • The export shape changes across majors: 1.x/2.x export a function, 3.x/4.x use default, 5.x exports expand.

Not changed

database_specific.last_known_affected_version_range is left as <= 5.0.7. It's still defensible as an upper bound on the highest affected version, and I'd rather flag it than edit a field whose exact intended semantics I haven't confirmed — happy to update it if maintainers prefer.

One advisory, ranges only; no other fields touched.

…x and 3.x

The single range (introduced 0, fixed 5.0.8) reports 1.1.17, 2.1.3, 3.0.3,
3.0.4 and 3.0.5 as vulnerable, but all five carry the fix. Split into
per-branch ranges. 4.x has no patched release, so its range continues to
5.0.8.
@github-actions
github-actions Bot changed the base branch from main to TechPro01/advisory-improvement-8878 July 29, 2026 21:47
@TechPro01

Copy link
Copy Markdown
Author

Adding in-repo corroboration so the per-branch boundaries can be checked by reading rather than by running anything, per CONTRIBUTING's "public, referenceable sources".

The fix introduces an EXPANSION_MAX_LENGTH bound. Checking the upstream source at each tagged release, on both sides of every proposed boundary:

tag file at that tag EXPANSION_MAX_LENGTH commit
v1.1.16 index.js absent
v1.1.17 index.js present 9dc1c9b
v2.1.2 index.js absent
v2.1.3 index.js present f7b0057
v3.0.2 index.js absent
v3.0.3 index.js present 45795f0
v5.0.7 src/index.ts absent
v5.0.8 src/index.ts present b5a8ce8

Each "present" is the first release on its branch to carry the bound, and each predecessor lacks it — which is what makes 1.1.17 / 2.1.3 / 3.0.3 / 5.0.8 the four fixed boundaries in the proposed ranges. This agrees with the behavioural results in the description, arrived at independently (published tarballs there, repository source here).

Note the 5.x path differs because 5.x is a TypeScript build (src/index.ts, published as dist/), whereas 1.x–3.x publish index.js directly.

Two things I'd flag for whoever picks this up rather than decide unilaterally:

  1. database_specific.last_known_affected_version_range is left at <= 5.0.7. It is still defensible as an upper bound on the highest affected version, but it no longer describes the affected set. Happy to update it in whatever form you prefer.
  2. 4.x still has no patched release (4.0.1, 2025-06-11, is the newest and lacks the bound), which is why the final range runs introduced: 4.0.0 → fixed: 5.0.8 — a 4.x consumer's only remedy is the 5.x major. If that's better expressed some other way in this schema, say so and I'll adjust.

@G-Rath

G-Rath commented Jul 30, 2026

Copy link
Copy Markdown

This update is invalid, as an affected entry should be a pair of introduced and fixed - the correct update to the advisory is handled by #8832

@TechPro01

Copy link
Copy Markdown
Author

Apologies for the noise here — #8832 was already open covering this, and I should have checked the repo's open PRs before filing. Thanks for the pointer, and for the correction on the affected structure; that's on me.

Closing in favour of #8832.

@TechPro01 TechPro01 closed this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants