npm version

This commit is contained in:
ChK
2026-03-11 08:52:04 +01:00
parent 702f5cd1cb
commit adbe4a8c83
7 changed files with 208 additions and 114 deletions

37
app/node_modules/minimatch/README.md generated vendored
View File

@@ -10,6 +10,43 @@ This is the matching library used internally by npm.
It works by converting glob expressions into JavaScript `RegExp`
objects.
## Important Security Consideration!
> [!WARNING]
> This library uses JavaScript regular expressions. Please read
> the following warning carefully, and be thoughtful about what
> you provide to this library in production systems.
_Any_ library in JavaScript that deals with matching string
patterns using regular expressions will be subject to
[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
if the pattern is generated using untrusted input.
Efforts have been made to mitigate risk as much as is feasible in
such a library, providing maximum recursion depths and so forth,
but these measures can only ultimately protect against accidents,
not malice. A dedicated attacker can _always_ find patterns that
cannot be defended against by a bash-compatible glob pattern
matching system that uses JavaScript regular expressions.
To be extremely clear:
> [!WARNING]
> **If you create a system where you take user input, and use
> that input as the source of a Regular Expression pattern, in
> this or any extant glob matcher in JavaScript, you will be
> pwned.**
A future version of this library _may_ use a different matching
algorithm which does not exhibit backtracking problems. If and
when that happens, it will likely be a sweeping change, and those
improvements will **not** be backported to legacy versions.
In the near term, it is not reasonable to continue to play
whack-a-mole with security advisories, and so any future ReDoS
reports will be considered "working as intended", and resolved
entirely by this warning.
## Usage
```javascript

View File

@@ -168,6 +168,8 @@ class Minimatch {
if (!options) options = {}
this.options = options
this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
? options.maxGlobstarRecursion : 200
this.set = []
this.pattern = pattern
this.windowsPathsNoEscape = !!options.windowsPathsNoEscape ||
@@ -255,114 +257,172 @@ class Minimatch {
// out of pattern, then that's fine, as long as all
// the parts match.
matchOne (file, pattern, partial) {
var options = this.options
if (pattern.indexOf(GLOBSTAR) !== -1) {
return this._matchGlobstar(file, pattern, partial, 0, 0)
}
return this._matchOne(file, pattern, partial, 0, 0)
}
this.debug('matchOne',
{ 'this': this, file: file, pattern: pattern })
_matchGlobstar (file, pattern, partial, fileIndex, patternIndex) {
// find first globstar from patternIndex
let firstgs = -1
for (let i = patternIndex; i < pattern.length; i++) {
if (pattern[i] === GLOBSTAR) { firstgs = i; break }
}
this.debug('matchOne', file.length, pattern.length)
// find last globstar
let lastgs = -1
for (let i = pattern.length - 1; i >= 0; i--) {
if (pattern[i] === GLOBSTAR) { lastgs = i; break }
}
for (var fi = 0,
pi = 0,
fl = file.length,
pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++) {
const head = pattern.slice(patternIndex, firstgs)
const body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs)
const tail = partial ? [] : pattern.slice(lastgs + 1)
// check the head
if (head.length) {
const fileHead = file.slice(fileIndex, fileIndex + head.length)
if (!this._matchOne(fileHead, head, partial, 0, 0)) {
return false
}
fileIndex += head.length
}
// check the tail
let fileTailMatch = 0
if (tail.length) {
if (tail.length + fileIndex > file.length) return false
const tailStart = file.length - tail.length
if (this._matchOne(file, tail, partial, tailStart, 0)) {
fileTailMatch = tail.length
} else {
// affordance for stuff like a/**/* matching a/b/
if (file[file.length - 1] !== '' ||
fileIndex + tail.length === file.length) {
return false
}
if (!this._matchOne(file, tail, partial, tailStart - 1, 0)) {
return false
}
fileTailMatch = tail.length + 1
}
}
// if body is empty (single ** between head and tail)
if (!body.length) {
let sawSome = !!fileTailMatch
for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
const f = String(file[i])
sawSome = true
if (f === '.' || f === '..' ||
(!this.options.dot && f.charAt(0) === '.')) {
return false
}
}
return partial || sawSome
}
// split body into segments at each GLOBSTAR
const bodySegments = [[[], 0]]
let currentBody = bodySegments[0]
let nonGsParts = 0
const nonGsPartsSums = [0]
for (const b of body) {
if (b === GLOBSTAR) {
nonGsPartsSums.push(nonGsParts)
currentBody = [[], 0]
bodySegments.push(currentBody)
} else {
currentBody[0].push(b)
nonGsParts++
}
}
let idx = bodySegments.length - 1
const fileLength = file.length - fileTailMatch
for (const b of bodySegments) {
b[1] = fileLength - (nonGsPartsSums[idx--] + b[0].length)
}
return !!this._matchGlobStarBodySections(
file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
)
}
// return false for "nope, not matching"
// return null for "not matching, cannot keep trying"
_matchGlobStarBodySections (
file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
) {
const bs = bodySegments[bodyIndex]
if (!bs) {
// just make sure there are no bad dots
for (let i = fileIndex; i < file.length; i++) {
sawTail = true
const f = file[i]
if (f === '.' || f === '..' ||
(!this.options.dot && f.charAt(0) === '.')) {
return false
}
}
return sawTail
}
const [body, after] = bs
while (fileIndex <= after) {
const m = this._matchOne(
file.slice(0, fileIndex + body.length),
body,
partial,
fileIndex,
0
)
// if limit exceeded, no match. intentional false negative,
// acceptable break in correctness for security.
if (m && globStarDepth < this.maxGlobstarRecursion) {
const sub = this._matchGlobStarBodySections(
file, bodySegments,
fileIndex + body.length, bodyIndex + 1,
partial, globStarDepth + 1, sawTail
)
if (sub !== false) {
return sub
}
}
const f = file[fileIndex]
if (f === '.' || f === '..' ||
(!this.options.dot && f.charAt(0) === '.')) {
return false
}
fileIndex++
}
return partial || null
}
_matchOne (file, pattern, partial, fileIndex, patternIndex) {
let fi, pi, fl, pl
for (
fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
; (fi < fl) && (pi < pl)
; fi++, pi++
) {
this.debug('matchOne loop')
var p = pattern[pi]
var f = file[fi]
const p = pattern[pi]
const f = file[fi]
this.debug(pattern, p, f)
// should be impossible.
// some invalid regexp stuff in the set.
/* istanbul ignore if */
if (p === false) return false
if (p === GLOBSTAR) {
this.debug('GLOBSTAR', [pattern, p, f])
// "**"
// a/**/b/**/c would match the following:
// a/b/x/y/z/c
// a/x/y/z/b/c
// a/b/x/b/x/c
// a/b/c
// To do this, take the rest of the pattern after
// the **, and see if it would match the file remainder.
// If so, return success.
// If not, the ** "swallows" a segment, and try again.
// This is recursively awful.
//
// a/**/b/**/c matching a/b/x/y/z/c
// - a matches a
// - doublestar
// - matchOne(b/x/y/z/c, b/**/c)
// - b matches b
// - doublestar
// - matchOne(x/y/z/c, c) -> no
// - matchOne(y/z/c, c) -> no
// - matchOne(z/c, c) -> no
// - matchOne(c, c) yes, hit
var fr = fi
var pr = pi + 1
if (pr === pl) {
this.debug('** at the end')
// a ** at the end will just swallow the rest.
// We have found a match.
// however, it will not swallow /.x, unless
// options.dot is set.
// . and .. are *never* matched by **, for explosively
// exponential reasons.
for (; fi < fl; fi++) {
if (file[fi] === '.' || file[fi] === '..' ||
(!options.dot && file[fi].charAt(0) === '.')) return false
}
return true
}
// ok, let's see if we can swallow whatever we can.
while (fr < fl) {
var swallowee = file[fr]
this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
// XXX remove this slice. Just pass the start index.
if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
this.debug('globstar found match!', fr, fl, swallowee)
// found a match.
return true
} else {
// can't swallow "." or ".." ever.
// can only swallow ".foo" when explicitly asked.
if (swallowee === '.' || swallowee === '..' ||
(!options.dot && swallowee.charAt(0) === '.')) {
this.debug('dot detected!', file, fr, pattern, pr)
break
}
// ** swallows a segment, and continue.
this.debug('globstar swallow a segment, and continue')
fr++
}
}
// no match was found.
// However, in partial mode, we can't say this is necessarily over.
// If there's more *pattern* left, then
/* istanbul ignore if */
if (partial) {
// ran out of file
this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
if (fr === fl) return true
}
return false
}
if (p === false || p === GLOBSTAR) return false
// something other than **
// non-magic patterns just have to match exactly
// patterns with magic have been turned into regexps.
var hit
let hit
if (typeof p === 'string') {
hit = f === p
this.debug('string match', p, f, hit)
@@ -374,17 +434,6 @@ class Minimatch {
if (!hit) return false
}
// Note: ending in / means that we'll get a final ""
// at the end of the pattern. This can only match a
// corresponding "" at the end of the file.
// If the file ends in /, then it can only match a
// a pattern that ends in /, unless the pattern just
// doesn't have any more for it. But, a/b/ should *not*
// match "a/b/*", even though "" matches against the
// [^/]*? pattern, except in partial mode, where it might
// simply not be reached yet.
// However, a/b/ should still satisfy a/*
// now either we fell off the end of the pattern, or we're done.
if (fi === fl && pi === pl) {
// ran out of pattern and filename at the same time.
@@ -533,6 +582,9 @@ class Minimatch {
continue
}
// coalesce consecutive non-globstar * characters
if (c === '*' && stateChar === '*') continue
// if we already have a stateChar, then it means
// that there was something like ** or +? in there.
// Handle the stateChar, then proceed with this one.

View File

@@ -5,7 +5,7 @@
"publishConfig": {
"tag": "legacy-v5"
},
"version": "5.1.6",
"version": "5.1.9",
"repository": {
"type": "git",
"url": "git://github.com/isaacs/minimatch.git"