Skip to content

Commit 9b308c3

Browse files
committed
Escaping unterminated openening brackets
1 parent 038ed72 commit 9b308c3

File tree

2 files changed

+38
-19
lines changed

2 files changed

+38
-19
lines changed

src/Util/FileMatcher.php

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,30 +37,31 @@ public static function toRegEx($glob, $flags = 0): string
3737
{
3838
self::assertIsAbsolute($glob);
3939

40-
$inSquare = false;
4140
$regex = '';
4241
$length = strlen($glob);
4342

43+
$brackets = [];
44+
4445
for ($i = 0; $i < $length; ++$i) {
4546
$c = $glob[$i];
4647

4748
switch ($c) {
4849
case '[':
4950
$regex .= '[';
50-
$inSquare = true;
51-
if (isset($glob[$i + 1]) && '^' === $glob[$i + 1]) {
52-
$regex .= '^';
53-
++$i;
54-
}
51+
$brackets[] = $i;
5552
break;
5653
case ']':
57-
$regex .= $inSquare ? ']' : '\\]';
58-
$inSquare = false;
54+
$regex .= ']';
55+
array_pop($brackets);
5956
break;
6057
case '?':
6158
$regex .= '.';
6259
break;
60+
case '-':
61+
$regex .= '-';
62+
break;
6363
case '!':
64+
// complementation/negation
6465
if ($glob[$i - 1] === '[') {
6566
$regex .= '^';
6667
break;
@@ -80,6 +81,7 @@ public static function toRegEx($glob, $flags = 0): string
8081
$regex .= '.*';
8182
break;
8283
case '/':
84+
// code could be refactored - handle globstars
8385
if (isset($glob[$i + 3]) && '**/' === $glob[$i + 1].$glob[$i + 2].$glob[$i + 3]) {
8486
$regex .= '/([^/]+/)*';
8587
$i += 3;
@@ -93,6 +95,8 @@ public static function toRegEx($glob, $flags = 0): string
9395
$regex .= '/';
9496
break;
9597
case '\\':
98+
// escape characters - this code is copy/pasted from webmozart/glob and
99+
// needs revision
96100
if (isset($glob[$i + 1])) {
97101
switch ($glob[$i + 1]) {
98102
case '*':
@@ -113,16 +117,16 @@ public static function toRegEx($glob, $flags = 0): string
113117
break;
114118

115119
default:
116-
$regex .= $c;
120+
$regex .= preg_quote($c);
117121
break;
118122
}
119123
}
120124

121-
if ($inSquare) {
122-
throw new InvalidArgumentException(sprintf(
123-
'Invalid glob: missing ] in %s',
124-
$glob
125-
));
125+
// escape unterminated brackets
126+
$bracketOffset = 0;
127+
foreach ($brackets as $offset) {
128+
$regex = substr($regex, 0, $offset + $bracketOffset) . '\\' . substr($regex, $offset + $bracketOffset);
129+
$bracketOffset++;
126130
}
127131

128132
$regex .= '(/|$)';

tests/unit/Util/FileMatcherTest.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,6 @@ public static function provideQuestionMark(): Generator
293293
*/
294294
public static function provideCharacterGroup(): Generator
295295
{
296-
// TODO: POSIX will interpret an unterminated [ group as a literal while
297-
// Regex will crash -- we'd need to look ahead to see if the [ is
298-
// terminated if we continue using Regex.
299-
//
300296
yield 'unterminated char group' => [
301297
new FileMatcherPattern('/[AB'),
302298
[
@@ -305,7 +301,26 @@ public static function provideCharacterGroup(): Generator
305301
'/[AB' => true,
306302
'/[AB/foo' => true,
307303
],
308-
'Unterminated square bracket',
304+
];
305+
yield 'unterminated char group followed by char group' => [
306+
new FileMatcherPattern('/[AB[a-z]'),
307+
[
308+
'/[' => false,
309+
'/[Ac' => false,
310+
'/[ABc' => true,
311+
'/[ABc/foo' => true,
312+
],
313+
];
314+
yield 'multiple unterminated char groups followed by char group' => [
315+
new FileMatcherPattern('/[AB[CD[a-z]EF'),
316+
[
317+
'/[' => false,
318+
'/[Ac' => false,
319+
'/[AB[C' => false,
320+
'/[AB[CD' => false,
321+
'/[AB[CDz' => false,
322+
'/[AB[CDzEF' => true,
323+
],
309324
];
310325
yield 'single char leaf' => [
311326
new FileMatcherPattern('/[A]'),

0 commit comments

Comments
 (0)