Skip to content

Commit 5a6d842

Browse files
authored
Merge pull request #9 from thaJeztah/fix_panic
fix panic / nil pointer dereference on invalid patterns
2 parents 7f236f5 + e5d80c7 commit 5a6d842

2 files changed

Lines changed: 55 additions & 22 deletions

File tree

patternmatcher.go

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -331,14 +331,20 @@ func (p *Pattern) match(path string) (bool, error) {
331331
// **/foo matches "foo"
332332
return suffix[0] == os.PathSeparator && path == suffix[1:], nil
333333
case regexpMatch:
334+
if p.regexp == nil {
335+
return false, filepath.ErrBadPattern
336+
}
334337
return p.regexp.MatchString(path), nil
338+
case unknownMatch:
339+
return false, filepath.ErrBadPattern
340+
default:
341+
return false, nil
335342
}
336-
337-
return false, nil
338343
}
339344

340345
func (p *Pattern) compile(sl string) error {
341346
regStr := "^"
347+
detectedType := exactMatch // assume exact match
342348
pattern := p.cleanedPattern
343349
// Go through the pattern and convert it to a regexp.
344350
// We use a scanner so we can support utf-8 chars.
@@ -350,7 +356,6 @@ func (p *Pattern) compile(sl string) error {
350356
escSL += `\`
351357
}
352358

353-
p.matchType = exactMatch
354359
for i := 0; scan.Peek() != scanner.EOF; i++ {
355360
ch := scan.Next()
356361

@@ -366,32 +371,32 @@ func (p *Pattern) compile(sl string) error {
366371

367372
if scan.Peek() == scanner.EOF {
368373
// is "**EOF" - to align with .gitignore just accept all
369-
if p.matchType == exactMatch {
370-
p.matchType = prefixMatch
374+
if detectedType == exactMatch {
375+
detectedType = prefixMatch
371376
} else {
372377
regStr += ".*"
373-
p.matchType = regexpMatch
378+
detectedType = regexpMatch
374379
}
375380
} else {
376381
// is "**"
377382
// Note that this allows for any # of /'s (even 0) because
378383
// the .* will eat everything, even /'s
379384
regStr += "(.*" + escSL + ")?"
380-
p.matchType = regexpMatch
385+
detectedType = regexpMatch
381386
}
382387

383388
if i == 0 {
384-
p.matchType = suffixMatch
389+
detectedType = suffixMatch
385390
}
386391
} else {
387392
// is "*" so map it to anything but "/"
388393
regStr += "[^" + escSL + "]*"
389-
p.matchType = regexpMatch
394+
detectedType = regexpMatch
390395
}
391396
} else if ch == '?' {
392397
// "?" is any char except "/"
393398
regStr += "[^" + escSL + "]"
394-
p.matchType = regexpMatch
399+
detectedType = regexpMatch
395400
} else if shouldEscape(ch) {
396401
// Escape some regexp special chars that have no meaning
397402
// in golang's filepath.Match
@@ -408,31 +413,29 @@ func (p *Pattern) compile(sl string) error {
408413
}
409414
if scan.Peek() != scanner.EOF {
410415
regStr += `\` + string(scan.Next())
411-
p.matchType = regexpMatch
416+
detectedType = regexpMatch
412417
} else {
413418
regStr += `\`
414419
}
415420
} else if ch == '[' || ch == ']' {
416421
regStr += string(ch)
417-
p.matchType = regexpMatch
422+
detectedType = regexpMatch
418423
} else {
419424
regStr += string(ch)
420425
}
421426
}
422427

423-
if p.matchType != regexpMatch {
424-
return nil
425-
}
428+
if detectedType == regexpMatch {
429+
regStr += "$"
426430

427-
regStr += "$"
431+
re, err := regexp.Compile(regStr)
432+
if err != nil {
433+
return err
434+
}
428435

429-
re, err := regexp.Compile(regStr)
430-
if err != nil {
431-
return err
436+
p.regexp = re
432437
}
433-
434-
p.regexp = re
435-
p.matchType = regexpMatch
438+
p.matchType = detectedType
436439
return nil
437440
}
438441

patternmatcher_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,3 +545,33 @@ func testCompile(sl string) func(*testing.T) {
545545
}
546546
}
547547
}
548+
549+
// regression test for https://github.com/moby/moby/issues/52203
550+
func TestMatchesOrParentMatchesMalformedPatternDoesNotPanicOnRepeatedCall(t *testing.T) {
551+
pm, err := New([]string{"[Local-Only]/"})
552+
if err != nil {
553+
t.Fatalf("expected pattern to pass initial validation, got %v", err)
554+
}
555+
556+
_, err = pm.MatchesOrParentMatches("x")
557+
if err == nil {
558+
t.Fatal("expected first match to fail with a bad pattern error")
559+
}
560+
if err != filepath.ErrBadPattern {
561+
t.Fatalf("expected %v, got %v", filepath.ErrBadPattern, err)
562+
}
563+
564+
defer func() {
565+
if r := recover(); r != nil {
566+
t.Fatalf("second match panicked: %v", r)
567+
}
568+
}()
569+
570+
_, err = pm.MatchesOrParentMatches("x")
571+
if err == nil {
572+
t.Fatal("expected second match to fail with a bad pattern error")
573+
}
574+
if err != filepath.ErrBadPattern {
575+
t.Fatalf("expected %v on second call, got %v", filepath.ErrBadPattern, err)
576+
}
577+
}

0 commit comments

Comments
 (0)