@samitouri / QOSami-HFS / commits / 87e2b8cb

fix: possible DoS with bad search patterns

Massimo Melina committed Apr 9, 2026 at 21:04 UTC 87e2b8cbc2b07bfd08bfc47907fc5cf6d8c29e95
2 files changed +8 -4
src/cross.ts
+7 -3
@@ -486,9 +486,13 @@ export function runAt(ts: number, cb: Callback) {
486 }
487 }
488
489 -export function makeMatcher(mask: string, emptyMaskReturns=false) {
490 - return mask ? picomatch(mask.replace(/^(!)?/, '$1(') + ')', { nocase: true}) // adding () will allow us to use the pipe at root level
491 - : () => emptyMaskReturns
489 +export function makeMatcher(mask: string, emptyMaskReturns=false, extglobs=true) {
490 + if (!mask) return () => emptyMaskReturns
491 + const wrapped = mask.replace(/^(!)?/, '$1(') + ')' // adding () will allow us to use the pipe at root level
492 + const opts = { nocase: true, noextglob: !extglobs }
493 + // reject patterns that compile to nested quantified groups, causing catastrophic backtracking (CVE-2026-33671)
494 + return /\)\)[+*]/.test(picomatch.makeRe(wrapped, opts).source) ? () => false
495 + : picomatch(wrapped, opts)
496 }
497
498 // this is caching all matchers, so don't use it with frequently changing masks. Benchmarks revealed that _.memoize make it slower than not using it, while this simple cache can speed up to 30x
src/misc.ts
+1 -1
@@ -18,7 +18,7 @@ import _ from 'lodash'
18
19 export function pattern2filter(pattern: string){
20 const matcher = makeMatcher(pattern.includes('*') ? pattern // if you specify *, we'll respect its position
21 - : pattern.split('|').map(x => `*${x}*`).join('|'))
21 + : pattern.split('|').map(x => `*${x}*`).join('|'), false, false)
22 return (s: string) =>
23 !pattern || matcher(basename(s||''))
24 }