name: optimizationLevel 2 lets .. erase the Windows drive root and UNC share from patterns
about: L2 pattern optimizer collapses C:/foo/../.. to a pattern that matches / instead of C:/, and //srv/share/../x to a pattern that no longer matches the original target
title: optimizationLevel 2 lets .. erase the Windows drive root and UNC share from patterns
labels: bug
assignees: ''
Follow-up to 7953af1 ("do not allow .. to consume drive letter on Windows"), tested on master (10.2.6, ded1bbd).
That commit guards a single .. from consuming a drive-letter part. But the guard only inspects the part immediately preceding each individual ... With two consecutive .., the first one eats the literal segment, so by the time the loop reaches the second .. the preceding part is an ordinary string and the drive gets eaten too — the whole pattern collapses to a single empty part:
import { minimatch, Minimatch } from 'minimatch'
minimatch('C:/', 'C:/foo/../..', { platform: 'win32', optimizationLevel: 2 }) // false — should be true
minimatch('/', 'C:/foo/../..', { platform: 'win32', optimizationLevel: 2 }) // true — should be false
// L1 is correct on both:
minimatch('C:/', 'C:/foo/../..', { platform: 'win32', optimizationLevel: 1 }) // true
minimatch('/', 'C:/foo/../..', { platform: 'win32', optimizationLevel: 1 }) // false
const m = new Minimatch('C:/foo/../..', { platform: 'win32', optimizationLevel: 2 })
// m.set === [['']] — drive letter fully erased
UNC shares are eaten as well. The L2 docs say empty/. parts near a possible UNC prefix are preserved, but .. still consumes the share component (and can then consume the server name):
minimatch('//srv/share/x', '//srv/share/../x', { platform: 'win32', optimizationLevel: 2 }) // false — should be true
minimatch('//srv/x', '//srv/share/../x', { platform: 'win32', optimizationLevel: 2 }) // true — should be false
const m2 = new Minimatch('//srv/share/../x', { platform: 'win32', optimizationLevel: 2 })
// m2.set === [['', '', 'srv', 'x']] — 'share' was consumed
Impact: for a file-walker (the documented use case for optimizationLevel: 2) a pattern like C:/build/../** or a UNC ignore rule silently stops matching paths it was written for, or starts matching unrelated ones (/). All results above verified against master with npx tsx.
Failing tests: test/optimizer-drive-root.ts (tap; all 4 assertions fail on master).
Suggested fix direction: after each splice in the ..-elimination loop, re-evaluate the new preceding part under the same guard (drive letter), and skip splices where the removed pair would expose/consume a //-prefixed server+share root.
name: optimizationLevel 2 lets
..erase the Windows drive root and UNC share from patternsabout: L2 pattern optimizer collapses
C:/foo/../..to a pattern that matches/instead ofC:/, and//srv/share/../xto a pattern that no longer matches the original targettitle: optimizationLevel 2 lets
..erase the Windows drive root and UNC share from patternslabels: bug
assignees: ''
Follow-up to 7953af1 ("do not allow .. to consume drive letter on Windows"), tested on master (10.2.6, ded1bbd).
That commit guards a single
..from consuming a drive-letter part. But the guard only inspects the part immediately preceding each individual... With two consecutive.., the first one eats the literal segment, so by the time the loop reaches the second..the preceding part is an ordinary string and the drive gets eaten too — the whole pattern collapses to a single empty part:UNC shares are eaten as well. The L2 docs say empty/
.parts near a possible UNC prefix are preserved, but..still consumes thesharecomponent (and can then consume the server name):Impact: for a file-walker (the documented use case for
optimizationLevel: 2) a pattern likeC:/build/../**or a UNC ignore rule silently stops matching paths it was written for, or starts matching unrelated ones (/). All results above verified against master withnpx tsx.Failing tests:
test/optimizer-drive-root.ts(tap; all 4 assertions fail on master).Suggested fix direction: after each splice in the
..-elimination loop, re-evaluate the new preceding part under the same guard (drive letter), and skip splices where the removed pair would expose/consume a//-prefixed server+share root.