Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@ const expandRange = (args, options) => {
/* eslint-disable-next-line no-new */
new RegExp(value);
} catch (ex) {
return args.map(v => utils.escapeRegex(v)).join('..');
// The range cannot be expressed as a character class (for example
// `{01..03}`, which yields the invalid class `[01-03]`), so fall back to
// matching the range as literal text. The dots must be escaped as well,
// otherwise they are emitted as regex wildcards and the pattern matches
// far more than the literal it is meant to represent.
return args.map(v => utils.escapeRegex(v)).join('\\.\\.');
}

return value;
Expand Down
15 changes: 15 additions & 0 deletions test/braces.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ describe('braces', () => {
assert(isMatch('a/c', 'a/{a..c}'));
});

it('should escape the dots of ranges that fall back to literal matching', () => {
// `{01..03}` cannot be expressed as a character class, since `[01-03]`
// is an invalid range, so it falls back to matching literal text. The
// dots must not leak into the regex as wildcards.
assert(isMatch('01..03', '{01..03}'));
assert(!isMatch('01ab03', '{01..03}'));
assert(!isMatch('01XY03', '{01..03}'));
assert(!isMatch('01__03', '{01..03}'));

// A fallback range must not match across a path separator either.
assert(!isMatch('01/x03', '{01..03}'));
assert(isMatch('logs/01..03.txt', 'logs/{01..03}.txt'));
assert(!isMatch('logs/01ab03.txt', 'logs/{01..03}.txt'));
});

it('should support Kleene stars', () => {
assert(isMatch('ab', '{ab,c}*'));
assert(isMatch('abab', '{ab,c}*'));
Expand Down