chore(deps): update dependency minimatch to v10 [security]#2197
chore(deps): update dependency minimatch to v10 [security]#2197renovate[bot] wants to merge 12 commits intomainfrom
Conversation
Edited/Blocked NotificationRenovate will not automatically rebase this PR, because it does not recognize the last commit author and assumes somebody else may have edited the PR. You can manually request rebase by checking the rebase/retry box above. |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Updates the workspace dependency graph to use minimatch v10 (addressing the referenced minimatch CVE) and adds an install-time shim to keep downstream tooling working with minimatch’s export changes.
Changes:
- Adds a root
postinstallscript to patch@eslint/eslintrcand@vscode/vsceundernode_modulesfor minimatch v10 compatibility. - Updates npm
overridesto forceminimatchv10 across several transitive dependency trees and pins vulnerable minimatch v10 ranges to10.2.4. - Upgrades ESLint to
^10.0.2(root andcalm-server).
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| scripts/patch-eslint-minimatch.mjs | New postinstall patcher that rewrites node_modules files to handle minimatch v10 export behavior. |
| package.json | Adds postinstall, bumps ESLint to v10, and updates overrides to force minimatch v10 / pin minimatch v10 patch version. |
| calm-server/package.json | Bumps ESLint to v10 for the calm-server workspace. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "copyfiles": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "@stoplight/spectral-core": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "eslint": { | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "eslint-plugin-react": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "eslint-plugin-import": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "commitizen": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "serve-handler": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| }, | ||
| "npm": { | ||
| "minimatch": "^10.2.4" | ||
| }, | ||
| "@vscode/vsce": { | ||
| "minimatch": "^3.1.4" | ||
| "minimatch": "^10.0.0" | ||
| } |
There was a problem hiding this comment.
These overrides force minimatch v10 for multiple dependencies that (per the lockfile) still declare minimatch ^3.x. This kind of major-version override can create fragile installs and runtime breakages when upstream packages assume the older CommonJS/default-export behavior. Prefer narrowing overrides to the vulnerable v10 range, or upgrading/replacing the specific upstream packages that need minimatch v10 support (so patches/overrides aren’t carrying API-compat risk).
scripts/patch-eslint-minimatch.mjs
Outdated
| if (!fs.existsSync(filePath)) { | ||
| throw new Error(`Expected file for ${label} at ${filePath}`); | ||
| } |
There was a problem hiding this comment.
applyPatch hard-fails the install when the target file isn’t present. Since this script is invoked from the root postinstall, it’s safer to treat missing targets as “package not installed” and skip the patch (or detect the relevant package/version first) so installs with dev dependencies omitted don’t fail unexpectedly.
scripts/patch-eslint-minimatch.mjs
Outdated
| const importLine = 'const minimatch_1 = __importDefault(require("minimatch"));'; | ||
| const oldShim = | ||
| 'if (typeof minimatch_1.default !== "function" && typeof minimatch_1.default?.minimatch === "function") {\n' + | ||
| ' minimatch_1.default = minimatch_1.default.minimatch;\n' + | ||
| '}'; | ||
| const newShim = | ||
| 'if (typeof minimatch_1.default !== "function") {\n' + | ||
| ' const minimatchFallback = typeof minimatch_1.minimatch === "function"\n' + | ||
| ' ? minimatch_1.minimatch\n' + | ||
| ' : (typeof minimatch_1.default?.minimatch === "function" ? minimatch_1.default.minimatch : undefined);\n' + | ||
| ' if (minimatchFallback) {\n' + | ||
| ' minimatch_1.default = minimatchFallback;\n' + | ||
| ' }\n' + | ||
| '}'; | ||
|
|
||
| if (source.includes(newShim)) { | ||
| console.log('@vscode/vsce minimatch runtime shim patch already applied'); | ||
| } else if (source.includes(oldShim)) { | ||
| fs.writeFileSync(filePath, source.replace(oldShim, newShim)); | ||
| console.log('patched @vscode/vsce minimatch runtime shim'); | ||
| } else if (source.includes(importLine)) { | ||
| fs.writeFileSync(filePath, source.replace(importLine, `${importLine}\n${newShim}`)); | ||
| console.log('patched @vscode/vsce minimatch runtime shim'); | ||
| } else { | ||
| throw new Error('Expected pattern not found for @vscode/vsce minimatch runtime shim'); |
There was a problem hiding this comment.
The @vscode/vsce patching logic relies on exact string matches against compiled output (including indentation and \n newlines) and throws if it can’t find them. This is brittle across vsce updates and may also be sensitive to line-ending differences. Consider version-gating the patch (check @vscode/vsce version) and/or using a more resilient match strategy (eg regex anchored to the relevant AST/statement) to avoid breaking installs on minor upstream changes.
| const importLine = 'const minimatch_1 = __importDefault(require("minimatch"));'; | |
| const oldShim = | |
| 'if (typeof minimatch_1.default !== "function" && typeof minimatch_1.default?.minimatch === "function") {\n' + | |
| ' minimatch_1.default = minimatch_1.default.minimatch;\n' + | |
| '}'; | |
| const newShim = | |
| 'if (typeof minimatch_1.default !== "function") {\n' + | |
| ' const minimatchFallback = typeof minimatch_1.minimatch === "function"\n' + | |
| ' ? minimatch_1.minimatch\n' + | |
| ' : (typeof minimatch_1.default?.minimatch === "function" ? minimatch_1.default.minimatch : undefined);\n' + | |
| ' if (minimatchFallback) {\n' + | |
| ' minimatch_1.default = minimatchFallback;\n' + | |
| ' }\n' + | |
| '}'; | |
| if (source.includes(newShim)) { | |
| console.log('@vscode/vsce minimatch runtime shim patch already applied'); | |
| } else if (source.includes(oldShim)) { | |
| fs.writeFileSync(filePath, source.replace(oldShim, newShim)); | |
| console.log('patched @vscode/vsce minimatch runtime shim'); | |
| } else if (source.includes(importLine)) { | |
| fs.writeFileSync(filePath, source.replace(importLine, `${importLine}\n${newShim}`)); | |
| console.log('patched @vscode/vsce minimatch runtime shim'); | |
| } else { | |
| throw new Error('Expected pattern not found for @vscode/vsce minimatch runtime shim'); | |
| const eol = source.includes('\r\n') ? '\r\n' : '\n'; | |
| const importLinePattern = /const\s+minimatch_1\s*=\s*__importDefault\(require\("minimatch"\)\);/; | |
| const oldShimPattern = | |
| /if\s*\(typeof\s+minimatch_1\.default\s*!==\s*"function"\s*&&\s*typeof\s+minimatch_1\.default\?\.\minimatch\s*===\s*"function"\)\s*\{\s*minimatch_1\.default\s*=\s*minimatch_1\.default\.minimatch;\s*\}/; | |
| const newShimTemplateLines = [ | |
| 'if (typeof minimatch_1.default !== "function") {', | |
| ' const minimatchFallback = typeof minimatch_1.minimatch === "function"', | |
| ' ? minimatch_1.minimatch', | |
| ' : (typeof minimatch_1.default?.minimatch === "function" ? minimatch_1.default.minimatch : undefined);', | |
| ' if (minimatchFallback) {', | |
| ' minimatch_1.default = minimatchFallback;', | |
| ' }', | |
| '}' | |
| ]; | |
| const newShim = newShimTemplateLines.join(eol); | |
| const newShimPattern = new RegExp( | |
| 'if\\s*\\(typeof\\s+minimatch_1\\.default\\s*!==\\s*\\"function\\"\\)\\s*\\{[\\s\\S]*?minimatchFallback[\\s\\S]*?minimatch_1\\.default\\s*=\\s*minimatchFallback;[\\s\\S]*?\\}', | |
| 'm' | |
| ); | |
| if (newShimPattern.test(source)) { | |
| console.log('@vscode/vsce minimatch runtime shim patch already applied'); | |
| } else if (oldShimPattern.test(source)) { | |
| const updated = source.replace(oldShimPattern, newShim); | |
| fs.writeFileSync(filePath, updated); | |
| console.log('patched @vscode/vsce minimatch runtime shim (replaced legacy shim)'); | |
| } else if (importLinePattern.test(source)) { | |
| const updated = source.replace(importLinePattern, (match) => `${match}${eol}${newShim}`); | |
| fs.writeFileSync(filePath, updated); | |
| console.log('patched @vscode/vsce minimatch runtime shim (added shim after import)'); | |
| } else { | |
| console.warn('Warning: could not find expected pattern for @vscode/vsce minimatch runtime shim; skipping patch.'); |
package.json
Outdated
| "calm-hub-ui:prod": "npm run prod --workspace calm-hub-ui", | ||
| "prepare": "husky" | ||
| "prepare": "husky", | ||
| "postinstall": "node scripts/patch-eslint-minimatch.mjs" |
There was a problem hiding this comment.
postinstall runs for all installs (including npm ci --omit=dev / production-style installs). The patch script currently throws if the target files are missing, which will fail installs where @eslint/eslintrc or @vscode/vsce aren’t present. Consider making the postinstall step conditional (only when those packages are installed) or make the patch script a no-op when the expected files don’t exist.
| "postinstall": "node scripts/patch-eslint-minimatch.mjs" | |
| "postinstall": "node scripts/patch-eslint-minimatch.mjs || echo \"Skipping eslint minimatch patch\"" |
| "commitizen": "^4.3.0", | ||
| "cz-conventional-changelog": "^3.3.0", | ||
| "eslint": "^9.39.2", | ||
| "eslint": "^10.0.2", |
There was a problem hiding this comment.
The PR metadata indicates this is a minimatch security update, but this change also bumps ESLint to a new major version. Please update the PR description/changelog notes to reflect the ESLint major upgrade (or split it into a separate PR) since it can have non-trivial config/behavior impact.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 9 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "@vscode/vsce": { | ||
| "minimatch": "^3.1.4" | ||
| "npm": { | ||
| "minimatch": "^10.2.4" |
There was a problem hiding this comment.
The overrides block no longer addresses eslint-plugin-import (used by calm-plugins/vscode) and @vscode/vsce, and package-lock still resolves minimatch@3.1.5 under those packages. If the intent is to move all consumers to minimatch v10 for the security fix, add/update overrides (or bump those packages) so they no longer pull minimatch v3; otherwise, clarify the intended scope in this PR.
| "minimatch": "^10.2.4" | |
| "minimatch": "^10.2.4" | |
| }, | |
| "eslint-plugin-import": { | |
| "minimatch": "^10.0.0" | |
| }, | |
| "@vscode/vsce": { | |
| "minimatch": "^10.0.0" |
| "@vscode/dts": "^0.4.1", | ||
| "@vscode/vsce": "^3.7.1", | ||
| "copyfiles": "^2.4.1", | ||
| "eslint": "^10.0.2", |
There was a problem hiding this comment.
Adding ESLint ^10.0.2 here combined with eslint-plugin-import ^2.32.0 is likely incompatible: eslint-plugin-import declares a peerDependency up to ESLint v9 (per package-lock), and it also brings in minimatch@3.1.5 in this workspace. Please upgrade eslint-plugin-import to a version that supports ESLint v10 (and ideally minimatch v10), or pin ESLint to a compatible major for this workspace.
| "eslint": "^10.0.2", | |
| "eslint": "^9.0.0", |
Renovate Ignore NotificationBecause you closed this PR without merging, Renovate will ignore this update. You will not get PRs for any future If you accidentally closed this PR, or if you changed your mind: rename this PR to get a fresh replacement PR. |
This PR contains the following updates:
^3.1.4→^10.0.0GitHub Vulnerability Alerts
CVE-2026-27903
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: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:
PoC
Tested on minimatch@10.2.2, Node.js 20.
Step 1 -- inline script
To scale the effect, increase k:
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:
Terminal 1 -- start the server:
Terminal 2 -- send the attack request (k=11, ~5s stall) and immediately return to shell:
Terminal 3 -- while the attack is in-flight, send a benign request:
Observed output (Terminal 3):
The 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: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.Release Notes
isaacs/minimatch (minimatch)
v10.2.3Compare Source
v10.2.2Compare Source
v10.2.1Compare Source
v10.2.0Compare Source
v10.1.3Compare Source
v10.1.2Compare Source
v10.1.1Compare Source
v10.1.0Compare Source
v10.0.3Compare Source
v10.0.2Compare Source
v10.0.1Compare Source
v10.0.0Compare Source
v9.0.9Compare Source
v9.0.8Compare Source
v9.0.7Compare Source
v9.0.6Compare Source
v9.0.5Compare Source
v9.0.4Compare Source
v9.0.3Compare Source
v9.0.2Compare Source
v9.0.1Compare Source
v9.0.0Compare Source
v8.0.7Compare Source
v8.0.6Compare Source
v8.0.5Compare Source
v8.0.4Compare Source
v8.0.3Compare Source
v8.0.2Compare Source
v8.0.1Compare Source
v8.0.0Compare Source
v7.4.9Compare Source
v7.4.8Compare Source
v7.4.7Compare Source
v7.4.6Compare Source
v7.4.5Compare Source
v7.4.4Compare Source
v7.4.3Compare Source
v7.4.2Compare Source
v7.4.1Compare Source
v7.4.0Compare Source
v7.3.0Compare Source
v7.2.0Compare Source
v7.1.4Compare Source
v7.1.3Compare Source
v7.1.2Compare Source
v7.1.1Compare Source
v7.1.0Compare Source
v7.0.1Compare Source
v7.0.0Compare Source
v6.2.3Compare Source
v6.2.2Compare Source
v6.2.1Compare Source
v6.2.0Compare Source
v6.1.10Compare Source
v6.1.9Compare Source
v6.1.8Compare Source
v6.1.7Compare Source
v6.1.6Compare Source
v6.1.5Compare Source
v6.1.4Compare Source
v6.1.3Compare Source
v6.1.2Compare Source
v6.1.1Compare Source
v6.1.0Compare Source
v6.0.4Compare Source
v6.0.3Compare Source
v6.0.2Compare Source
v6.0.1Compare Source
v6.0.0Compare Source
v5.1.9Compare Source
v5.1.8Compare Source
v5.1.7Compare Source
v5.1.6Compare Source
v5.1.5Compare Source
v5.1.4Compare Source
v5.1.3Compare Source
v5.1.2Compare Source
v5.1.1Compare Source
v5.1.0Compare Source
v5.0.1Compare Source
v5.0.0Compare Source
v4.2.6Compare Source
v4.2.5Compare Source
v4.2.4Compare Source
v4.2.3Compare Source
v4.2.2Compare Source
v4.2.1Compare Source
v4.2.0Compare Source
v4.1.1Compare Source
v4.1.0Compare Source
v4.0.0Compare Source
Configuration
📅 Schedule: Branch creation - "" in timezone UTC, Automerge - At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.