minimatch 3.1.3 → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/minimatch.js +157 -102
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,43 @@ This is the matching library used internally by npm.
|
|
|
10
10
|
It works by converting glob expressions into JavaScript `RegExp`
|
|
11
11
|
objects.
|
|
12
12
|
|
|
13
|
+
## Important Security Consideration!
|
|
14
|
+
|
|
15
|
+
> [!WARNING]
|
|
16
|
+
> This library uses JavaScript regular expressions. Please read
|
|
17
|
+
> the following warning carefully, and be thoughtful about what
|
|
18
|
+
> you provide to this library in production systems.
|
|
19
|
+
|
|
20
|
+
_Any_ library in JavaScript that deals with matching string
|
|
21
|
+
patterns using regular expressions will be subject to
|
|
22
|
+
[ReDoS](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)
|
|
23
|
+
if the pattern is generated using untrusted input.
|
|
24
|
+
|
|
25
|
+
Efforts have been made to mitigate risk as much as is feasible in
|
|
26
|
+
such a library, providing maximum recursion depths and so forth,
|
|
27
|
+
but these measures can only ultimately protect against accidents,
|
|
28
|
+
not malice. A dedicated attacker can _always_ find patterns that
|
|
29
|
+
cannot be defended against by a bash-compatible glob pattern
|
|
30
|
+
matching system that uses JavaScript regular expressions.
|
|
31
|
+
|
|
32
|
+
To be extremely clear:
|
|
33
|
+
|
|
34
|
+
> [!WARNING]
|
|
35
|
+
> **If you create a system where you take user input, and use
|
|
36
|
+
> that input as the source of a Regular Expression pattern, in
|
|
37
|
+
> this or any extant glob matcher in JavaScript, you will be
|
|
38
|
+
> pwned.**
|
|
39
|
+
|
|
40
|
+
A future version of this library _may_ use a different matching
|
|
41
|
+
algorithm which does not exhibit backtracking problems. If and
|
|
42
|
+
when that happens, it will likely be a sweeping change, and those
|
|
43
|
+
improvements will **not** be backported to legacy versions.
|
|
44
|
+
|
|
45
|
+
In the near term, it is not reasonable to continue to play
|
|
46
|
+
whack-a-mole with security advisories, and so any future ReDoS
|
|
47
|
+
reports will be considered "working as intended", and resolved
|
|
48
|
+
entirely by this warning.
|
|
49
|
+
|
|
13
50
|
## Usage
|
|
14
51
|
|
|
15
52
|
```javascript
|
package/minimatch.js
CHANGED
|
@@ -142,6 +142,8 @@ function Minimatch (pattern, options) {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
this.options = options
|
|
145
|
+
this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
|
|
146
|
+
? options.maxGlobstarRecursion : 200
|
|
145
147
|
this.set = []
|
|
146
148
|
this.pattern = pattern
|
|
147
149
|
this.regexp = null
|
|
@@ -787,109 +789,173 @@ Minimatch.prototype.match = function match (f, partial) {
|
|
|
787
789
|
// out of pattern, then that's fine, as long as all
|
|
788
790
|
// the parts match.
|
|
789
791
|
Minimatch.prototype.matchOne = function (file, pattern, partial) {
|
|
790
|
-
|
|
792
|
+
if (pattern.indexOf(GLOBSTAR) !== -1) {
|
|
793
|
+
return this._matchGlobstar(file, pattern, partial, 0, 0)
|
|
794
|
+
}
|
|
795
|
+
return this._matchOne(file, pattern, partial, 0, 0)
|
|
796
|
+
}
|
|
791
797
|
|
|
792
|
-
|
|
793
|
-
|
|
798
|
+
Minimatch.prototype._matchGlobstar = function (file, pattern, partial, fileIndex, patternIndex) {
|
|
799
|
+
var i
|
|
794
800
|
|
|
795
|
-
|
|
801
|
+
// find first globstar from patternIndex
|
|
802
|
+
var firstgs = -1
|
|
803
|
+
for (i = patternIndex; i < pattern.length; i++) {
|
|
804
|
+
if (pattern[i] === GLOBSTAR) { firstgs = i; break }
|
|
805
|
+
}
|
|
796
806
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
; fi++, pi++) {
|
|
803
|
-
this.debug('matchOne loop')
|
|
804
|
-
var p = pattern[pi]
|
|
805
|
-
var f = file[fi]
|
|
807
|
+
// find last globstar
|
|
808
|
+
var lastgs = -1
|
|
809
|
+
for (i = pattern.length - 1; i >= 0; i--) {
|
|
810
|
+
if (pattern[i] === GLOBSTAR) { lastgs = i; break }
|
|
811
|
+
}
|
|
806
812
|
|
|
807
|
-
|
|
813
|
+
var head = pattern.slice(patternIndex, firstgs)
|
|
814
|
+
var body = partial ? pattern.slice(firstgs + 1) : pattern.slice(firstgs + 1, lastgs)
|
|
815
|
+
var tail = partial ? [] : pattern.slice(lastgs + 1)
|
|
808
816
|
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
if (
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
//
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
// - matchOne(x/y/z/c, c) -> no
|
|
836
|
-
// - matchOne(y/z/c, c) -> no
|
|
837
|
-
// - matchOne(z/c, c) -> no
|
|
838
|
-
// - matchOne(c, c) yes, hit
|
|
839
|
-
var fr = fi
|
|
840
|
-
var pr = pi + 1
|
|
841
|
-
if (pr === pl) {
|
|
842
|
-
this.debug('** at the end')
|
|
843
|
-
// a ** at the end will just swallow the rest.
|
|
844
|
-
// We have found a match.
|
|
845
|
-
// however, it will not swallow /.x, unless
|
|
846
|
-
// options.dot is set.
|
|
847
|
-
// . and .. are *never* matched by **, for explosively
|
|
848
|
-
// exponential reasons.
|
|
849
|
-
for (; fi < fl; fi++) {
|
|
850
|
-
if (file[fi] === '.' || file[fi] === '..' ||
|
|
851
|
-
(!options.dot && file[fi].charAt(0) === '.')) return false
|
|
852
|
-
}
|
|
853
|
-
return true
|
|
817
|
+
// check the head
|
|
818
|
+
if (head.length) {
|
|
819
|
+
var fileHead = file.slice(fileIndex, fileIndex + head.length)
|
|
820
|
+
if (!this._matchOne(fileHead, head, partial, 0, 0)) {
|
|
821
|
+
return false
|
|
822
|
+
}
|
|
823
|
+
fileIndex += head.length
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// check the tail
|
|
827
|
+
var fileTailMatch = 0
|
|
828
|
+
if (tail.length) {
|
|
829
|
+
if (tail.length + fileIndex > file.length) return false
|
|
830
|
+
|
|
831
|
+
var tailStart = file.length - tail.length
|
|
832
|
+
if (this._matchOne(file, tail, partial, tailStart, 0)) {
|
|
833
|
+
fileTailMatch = tail.length
|
|
834
|
+
} else {
|
|
835
|
+
// affordance for stuff like a/**/* matching a/b/
|
|
836
|
+
if (file[file.length - 1] !== '' ||
|
|
837
|
+
fileIndex + tail.length === file.length) {
|
|
838
|
+
return false
|
|
839
|
+
}
|
|
840
|
+
tailStart--
|
|
841
|
+
if (!this._matchOne(file, tail, partial, tailStart, 0)) {
|
|
842
|
+
return false
|
|
854
843
|
}
|
|
844
|
+
fileTailMatch = tail.length + 1
|
|
845
|
+
}
|
|
846
|
+
}
|
|
855
847
|
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
// found a match.
|
|
866
|
-
return true
|
|
867
|
-
} else {
|
|
868
|
-
// can't swallow "." or ".." ever.
|
|
869
|
-
// can only swallow ".foo" when explicitly asked.
|
|
870
|
-
if (swallowee === '.' || swallowee === '..' ||
|
|
871
|
-
(!options.dot && swallowee.charAt(0) === '.')) {
|
|
872
|
-
this.debug('dot detected!', file, fr, pattern, pr)
|
|
873
|
-
break
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
// ** swallows a segment, and continue.
|
|
877
|
-
this.debug('globstar swallow a segment, and continue')
|
|
878
|
-
fr++
|
|
879
|
-
}
|
|
848
|
+
// if body is empty (single ** between head and tail)
|
|
849
|
+
if (!body.length) {
|
|
850
|
+
var sawSome = !!fileTailMatch
|
|
851
|
+
for (i = fileIndex; i < file.length - fileTailMatch; i++) {
|
|
852
|
+
var f = String(file[i])
|
|
853
|
+
sawSome = true
|
|
854
|
+
if (f === '.' || f === '..' ||
|
|
855
|
+
(!this.options.dot && f.charAt(0) === '.')) {
|
|
856
|
+
return false
|
|
880
857
|
}
|
|
858
|
+
}
|
|
859
|
+
return partial || sawSome
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// split body into segments at each GLOBSTAR
|
|
863
|
+
var bodySegments = [[[], 0]]
|
|
864
|
+
var currentBody = bodySegments[0]
|
|
865
|
+
var nonGsParts = 0
|
|
866
|
+
var nonGsPartsSums = [0]
|
|
867
|
+
for (var bi = 0; bi < body.length; bi++) {
|
|
868
|
+
var b = body[bi]
|
|
869
|
+
if (b === GLOBSTAR) {
|
|
870
|
+
nonGsPartsSums.push(nonGsParts)
|
|
871
|
+
currentBody = [[], 0]
|
|
872
|
+
bodySegments.push(currentBody)
|
|
873
|
+
} else {
|
|
874
|
+
currentBody[0].push(b)
|
|
875
|
+
nonGsParts++
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
var idx = bodySegments.length - 1
|
|
880
|
+
var fileLength = file.length - fileTailMatch
|
|
881
|
+
for (var si = 0; si < bodySegments.length; si++) {
|
|
882
|
+
bodySegments[si][1] = fileLength -
|
|
883
|
+
(nonGsPartsSums[idx--] + bodySegments[si][0].length)
|
|
884
|
+
}
|
|
881
885
|
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
886
|
+
return !!this._matchGlobStarBodySections(
|
|
887
|
+
file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
|
|
888
|
+
)
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// return false for "nope, not matching"
|
|
892
|
+
// return null for "not matching, cannot keep trying"
|
|
893
|
+
Minimatch.prototype._matchGlobStarBodySections = function (
|
|
894
|
+
file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
|
|
895
|
+
) {
|
|
896
|
+
var bs = bodySegments[bodyIndex]
|
|
897
|
+
if (!bs) {
|
|
898
|
+
// just make sure there are no bad dots
|
|
899
|
+
for (var i = fileIndex; i < file.length; i++) {
|
|
900
|
+
sawTail = true
|
|
901
|
+
var f = file[i]
|
|
902
|
+
if (f === '.' || f === '..' ||
|
|
903
|
+
(!this.options.dot && f.charAt(0) === '.')) {
|
|
904
|
+
return false
|
|
890
905
|
}
|
|
906
|
+
}
|
|
907
|
+
return sawTail
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
var body = bs[0]
|
|
911
|
+
var after = bs[1]
|
|
912
|
+
while (fileIndex <= after) {
|
|
913
|
+
var m = this._matchOne(
|
|
914
|
+
file.slice(0, fileIndex + body.length),
|
|
915
|
+
body,
|
|
916
|
+
partial,
|
|
917
|
+
fileIndex,
|
|
918
|
+
0
|
|
919
|
+
)
|
|
920
|
+
// if limit exceeded, no match. intentional false negative,
|
|
921
|
+
// acceptable break in correctness for security.
|
|
922
|
+
if (m && globStarDepth < this.maxGlobstarRecursion) {
|
|
923
|
+
var sub = this._matchGlobStarBodySections(
|
|
924
|
+
file, bodySegments,
|
|
925
|
+
fileIndex + body.length, bodyIndex + 1,
|
|
926
|
+
partial, globStarDepth + 1, sawTail
|
|
927
|
+
)
|
|
928
|
+
if (sub !== false) {
|
|
929
|
+
return sub
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
var f = file[fileIndex]
|
|
933
|
+
if (f === '.' || f === '..' ||
|
|
934
|
+
(!this.options.dot && f.charAt(0) === '.')) {
|
|
891
935
|
return false
|
|
892
936
|
}
|
|
937
|
+
fileIndex++
|
|
938
|
+
}
|
|
939
|
+
return partial || null
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
Minimatch.prototype._matchOne = function (file, pattern, partial, fileIndex, patternIndex) {
|
|
943
|
+
var fi, pi, fl, pl
|
|
944
|
+
for (
|
|
945
|
+
fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
|
|
946
|
+
; (fi < fl) && (pi < pl)
|
|
947
|
+
; fi++, pi++
|
|
948
|
+
) {
|
|
949
|
+
this.debug('matchOne loop')
|
|
950
|
+
var p = pattern[pi]
|
|
951
|
+
var f = file[fi]
|
|
952
|
+
|
|
953
|
+
this.debug(pattern, p, f)
|
|
954
|
+
|
|
955
|
+
// should be impossible.
|
|
956
|
+
// some invalid regexp stuff in the set.
|
|
957
|
+
/* istanbul ignore if */
|
|
958
|
+
if (p === false || p === GLOBSTAR) return false
|
|
893
959
|
|
|
894
960
|
// something other than **
|
|
895
961
|
// non-magic patterns just have to match exactly
|
|
@@ -906,17 +972,6 @@ Minimatch.prototype.matchOne = function (file, pattern, partial) {
|
|
|
906
972
|
if (!hit) return false
|
|
907
973
|
}
|
|
908
974
|
|
|
909
|
-
// Note: ending in / means that we'll get a final ""
|
|
910
|
-
// at the end of the pattern. This can only match a
|
|
911
|
-
// corresponding "" at the end of the file.
|
|
912
|
-
// If the file ends in /, then it can only match a
|
|
913
|
-
// a pattern that ends in /, unless the pattern just
|
|
914
|
-
// doesn't have any more for it. But, a/b/ should *not*
|
|
915
|
-
// match "a/b/*", even though "" matches against the
|
|
916
|
-
// [^/]*? pattern, except in partial mode, where it might
|
|
917
|
-
// simply not be reached yet.
|
|
918
|
-
// However, a/b/ should still satisfy a/*
|
|
919
|
-
|
|
920
975
|
// now either we fell off the end of the pattern, or we're done.
|
|
921
976
|
if (fi === fl && pi === pl) {
|
|
922
977
|
// ran out of pattern and filename at the same time.
|