minimatch 5.1.7 → 5.1.8

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.
Files changed (3) hide show
  1. package/changelog.md +35 -0
  2. package/minimatch.js +150 -101
  3. package/package.json +1 -1
package/changelog.md ADDED
@@ -0,0 +1,35 @@
1
+ # change log
2
+
3
+ ## 5.1
4
+
5
+ - use windowsPathNoEscape/allowWindowsEscape opts
6
+ - make character classes more faithful to bash glob behavior
7
+ - fix handling of escapes
8
+ - treat invalid character classes as non-matching pattern
9
+ rather than escaped literals
10
+
11
+ ## 5.0
12
+
13
+ - brace-expansion: ignore only blocks that begins with $
14
+ - Expect exclusively forward slash as path sep, same as node-glob
15
+
16
+ ## 4.2
17
+
18
+ - makeRe: globstar should match zero+ path portions
19
+ - Fix bug with escaped '@' in patterns
20
+
21
+ ## 4.1
22
+
23
+ - treat `nocase:true` as always having magic
24
+ - expose GLOBSTAR marker
25
+
26
+ ## 4.0
27
+
28
+ - Update to modern JS syntax
29
+ - Add `allowWindowsEscape` option
30
+
31
+ ## 3.x
32
+
33
+ - Added basic redos protection
34
+ - Handle unfinished `!(` extglob patterns
35
+ - Add `partial: true` option
package/minimatch.js CHANGED
@@ -168,6 +168,8 @@ class Minimatch {
168
168
  if (!options) options = {}
169
169
 
170
170
  this.options = options
171
+ this.maxGlobstarRecursion = options.maxGlobstarRecursion !== undefined
172
+ ? options.maxGlobstarRecursion : 200
171
173
  this.set = []
172
174
  this.pattern = pattern
173
175
  this.windowsPathsNoEscape = !!options.windowsPathsNoEscape ||
@@ -255,114 +257,172 @@ class Minimatch {
255
257
  // out of pattern, then that's fine, as long as all
256
258
  // the parts match.
257
259
  matchOne (file, pattern, partial) {
258
- var options = this.options
260
+ if (pattern.indexOf(GLOBSTAR) !== -1) {
261
+ return this._matchGlobstar(file, pattern, partial, 0, 0)
262
+ }
263
+ return this._matchOne(file, pattern, partial, 0, 0)
264
+ }
259
265
 
260
- this.debug('matchOne',
261
- { 'this': this, file: file, pattern: pattern })
266
+ _matchGlobstar (file, pattern, partial, fileIndex, patternIndex) {
267
+ // find first globstar from patternIndex
268
+ let firstgs = -1
269
+ for (let i = patternIndex; i < pattern.length; i++) {
270
+ if (pattern[i] === GLOBSTAR) { firstgs = i; break }
271
+ }
262
272
 
263
- this.debug('matchOne', file.length, pattern.length)
273
+ // find last globstar
274
+ let lastgs = -1
275
+ for (let i = pattern.length - 1; i >= 0; i--) {
276
+ if (pattern[i] === GLOBSTAR) { lastgs = i; break }
277
+ }
264
278
 
265
- for (var fi = 0,
266
- pi = 0,
267
- fl = file.length,
268
- pl = pattern.length
269
- ; (fi < fl) && (pi < pl)
270
- ; fi++, pi++) {
271
- this.debug('matchOne loop')
272
- var p = pattern[pi]
273
- var f = file[fi]
279
+ const head = pattern.slice(patternIndex, firstgs)
280
+ const body = pattern.slice(firstgs + 1, lastgs)
281
+ const tail = pattern.slice(lastgs + 1)
274
282
 
275
- this.debug(pattern, p, f)
283
+ // check the head
284
+ if (head.length) {
285
+ const fileHead = file.slice(fileIndex, fileIndex + head.length)
286
+ if (!this._matchOne(fileHead, head, partial, 0, 0)) {
287
+ return false
288
+ }
289
+ fileIndex += head.length
290
+ }
276
291
 
277
- // should be impossible.
278
- // some invalid regexp stuff in the set.
279
- /* istanbul ignore if */
280
- if (p === false) return false
281
-
282
- if (p === GLOBSTAR) {
283
- this.debug('GLOBSTAR', [pattern, p, f])
284
-
285
- // "**"
286
- // a/**/b/**/c would match the following:
287
- // a/b/x/y/z/c
288
- // a/x/y/z/b/c
289
- // a/b/x/b/x/c
290
- // a/b/c
291
- // To do this, take the rest of the pattern after
292
- // the **, and see if it would match the file remainder.
293
- // If so, return success.
294
- // If not, the ** "swallows" a segment, and try again.
295
- // This is recursively awful.
296
- //
297
- // a/**/b/**/c matching a/b/x/y/z/c
298
- // - a matches a
299
- // - doublestar
300
- // - matchOne(b/x/y/z/c, b/**/c)
301
- // - b matches b
302
- // - doublestar
303
- // - matchOne(x/y/z/c, c) -> no
304
- // - matchOne(y/z/c, c) -> no
305
- // - matchOne(z/c, c) -> no
306
- // - matchOne(c, c) yes, hit
307
- var fr = fi
308
- var pr = pi + 1
309
- if (pr === pl) {
310
- this.debug('** at the end')
311
- // a ** at the end will just swallow the rest.
312
- // We have found a match.
313
- // however, it will not swallow /.x, unless
314
- // options.dot is set.
315
- // . and .. are *never* matched by **, for explosively
316
- // exponential reasons.
317
- for (; fi < fl; fi++) {
318
- if (file[fi] === '.' || file[fi] === '..' ||
319
- (!options.dot && file[fi].charAt(0) === '.')) return false
320
- }
321
- return true
292
+ // check the tail
293
+ let fileTailMatch = 0
294
+ if (tail.length) {
295
+ if (tail.length + fileIndex > file.length) return false
296
+
297
+ const tailStart = file.length - tail.length
298
+ if (this._matchOne(file, tail, partial, tailStart, 0)) {
299
+ fileTailMatch = tail.length
300
+ } else {
301
+ // affordance for stuff like a/**/* matching a/b/
302
+ if (file[file.length - 1] !== '' ||
303
+ fileIndex + tail.length === file.length) {
304
+ return false
305
+ }
306
+ if (!this._matchOne(file, tail, partial, tailStart - 1, 0)) {
307
+ return false
322
308
  }
309
+ fileTailMatch = tail.length + 1
310
+ }
311
+ }
323
312
 
324
- // ok, let's see if we can swallow whatever we can.
325
- while (fr < fl) {
326
- var swallowee = file[fr]
313
+ // if body is empty (single ** between head and tail)
314
+ if (!body.length) {
315
+ let sawSome = !!fileTailMatch
316
+ for (let i = fileIndex; i < file.length - fileTailMatch; i++) {
317
+ const f = String(file[i])
318
+ sawSome = true
319
+ if (f === '.' || f === '..' ||
320
+ (!this.options.dot && f.charAt(0) === '.')) {
321
+ return false
322
+ }
323
+ }
324
+ return sawSome
325
+ }
327
326
 
328
- this.debug('\nglobstar while', file, fr, pattern, pr, swallowee)
327
+ // split body into segments at each GLOBSTAR
328
+ const bodySegments = [[[], 0]]
329
+ let currentBody = bodySegments[0]
330
+ let nonGsParts = 0
331
+ const nonGsPartsSums = [0]
332
+ for (const b of body) {
333
+ if (b === GLOBSTAR) {
334
+ nonGsPartsSums.push(nonGsParts)
335
+ currentBody = [[], 0]
336
+ bodySegments.push(currentBody)
337
+ } else {
338
+ currentBody[0].push(b)
339
+ nonGsParts++
340
+ }
341
+ }
329
342
 
330
- // XXX remove this slice. Just pass the start index.
331
- if (this.matchOne(file.slice(fr), pattern.slice(pr), partial)) {
332
- this.debug('globstar found match!', fr, fl, swallowee)
333
- // found a match.
334
- return true
335
- } else {
336
- // can't swallow "." or ".." ever.
337
- // can only swallow ".foo" when explicitly asked.
338
- if (swallowee === '.' || swallowee === '..' ||
339
- (!options.dot && swallowee.charAt(0) === '.')) {
340
- this.debug('dot detected!', file, fr, pattern, pr)
341
- break
342
- }
343
-
344
- // ** swallows a segment, and continue.
345
- this.debug('globstar swallow a segment, and continue')
346
- fr++
347
- }
343
+ let idx = bodySegments.length - 1
344
+ const fileLength = file.length - fileTailMatch
345
+ for (const b of bodySegments) {
346
+ b[1] = fileLength - (nonGsPartsSums[idx--] + b[0].length)
347
+ }
348
+
349
+ return !!this._matchGlobStarBodySections(
350
+ file, bodySegments, fileIndex, 0, partial, 0, !!fileTailMatch
351
+ )
352
+ }
353
+
354
+ // return false for "nope, not matching"
355
+ // return null for "not matching, cannot keep trying"
356
+ _matchGlobStarBodySections (
357
+ file, bodySegments, fileIndex, bodyIndex, partial, globStarDepth, sawTail
358
+ ) {
359
+ const bs = bodySegments[bodyIndex]
360
+ if (!bs) {
361
+ // just make sure there are no bad dots
362
+ for (let i = fileIndex; i < file.length; i++) {
363
+ sawTail = true
364
+ const f = file[i]
365
+ if (f === '.' || f === '..' ||
366
+ (!this.options.dot && f.charAt(0) === '.')) {
367
+ return false
348
368
  }
369
+ }
370
+ return sawTail
371
+ }
349
372
 
350
- // no match was found.
351
- // However, in partial mode, we can't say this is necessarily over.
352
- // If there's more *pattern* left, then
353
- /* istanbul ignore if */
354
- if (partial) {
355
- // ran out of file
356
- this.debug('\n>>> no match, partial?', file, fr, pattern, pr)
357
- if (fr === fl) return true
373
+ const [body, after] = bs
374
+ while (fileIndex <= after) {
375
+ const m = this._matchOne(
376
+ file.slice(0, fileIndex + body.length),
377
+ body,
378
+ partial,
379
+ fileIndex,
380
+ 0
381
+ )
382
+ // if limit exceeded, no match. intentional false negative,
383
+ // acceptable break in correctness for security.
384
+ if (m && globStarDepth < this.maxGlobstarRecursion) {
385
+ const sub = this._matchGlobStarBodySections(
386
+ file, bodySegments,
387
+ fileIndex + body.length, bodyIndex + 1,
388
+ partial, globStarDepth + 1, sawTail
389
+ )
390
+ if (sub !== false) {
391
+ return sub
358
392
  }
393
+ }
394
+ const f = file[fileIndex]
395
+ if (f === '.' || f === '..' ||
396
+ (!this.options.dot && f.charAt(0) === '.')) {
359
397
  return false
360
398
  }
399
+ fileIndex++
400
+ }
401
+ return null
402
+ }
403
+
404
+ _matchOne (file, pattern, partial, fileIndex, patternIndex) {
405
+ let fi, pi, fl, pl
406
+ for (
407
+ fi = fileIndex, pi = patternIndex, fl = file.length, pl = pattern.length
408
+ ; (fi < fl) && (pi < pl)
409
+ ; fi++, pi++
410
+ ) {
411
+ this.debug('matchOne loop')
412
+ const p = pattern[pi]
413
+ const f = file[fi]
414
+
415
+ this.debug(pattern, p, f)
416
+
417
+ // should be impossible.
418
+ // some invalid regexp stuff in the set.
419
+ /* istanbul ignore if */
420
+ if (p === false || p === GLOBSTAR) return false
361
421
 
362
422
  // something other than **
363
423
  // non-magic patterns just have to match exactly
364
424
  // patterns with magic have been turned into regexps.
365
- var hit
425
+ let hit
366
426
  if (typeof p === 'string') {
367
427
  hit = f === p
368
428
  this.debug('string match', p, f, hit)
@@ -374,17 +434,6 @@ class Minimatch {
374
434
  if (!hit) return false
375
435
  }
376
436
 
377
- // Note: ending in / means that we'll get a final ""
378
- // at the end of the pattern. This can only match a
379
- // corresponding "" at the end of the file.
380
- // If the file ends in /, then it can only match a
381
- // a pattern that ends in /, unless the pattern just
382
- // doesn't have any more for it. But, a/b/ should *not*
383
- // match "a/b/*", even though "" matches against the
384
- // [^/]*? pattern, except in partial mode, where it might
385
- // simply not be reached yet.
386
- // However, a/b/ should still satisfy a/*
387
-
388
437
  // now either we fell off the end of the pattern, or we're done.
389
438
  if (fi === fl && pi === pl) {
390
439
  // ran out of pattern and filename at the same time.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "publishConfig": {
6
6
  "tag": "legacy-v5"
7
7
  },
8
- "version": "5.1.7",
8
+ "version": "5.1.8",
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git://github.com/isaacs/minimatch.git"