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
17 changes: 14 additions & 3 deletions lib/src/matching.dart
Original file line number Diff line number Diff line change
Expand Up @@ -658,15 +658,26 @@ class matching {
if (rx_match == null) {
break;
}
// rx_match.start/index are relative to `pattern` (the substring),
// not to `password`. Treating them as absolute positions, and
// re-including the match's last character in the next substring
// (`... - 1`), can make `last_index` oscillate between two values
// forever for overlapping matches (e.g. two overlapping year-like
// digit runs against the recent_year regex), growing `matches`
// without bound until the process runs out of memory. Track the
// absolute offset explicitly and advance strictly past the
// matched token so the loop always makes forward progress.
String? token = rx_match.group(0);
last_index = rx_match.start + rx_match.group(0)!.length - 1;
final int abs_start = last_index + rx_match.start;
final int abs_end = abs_start + token!.length - 1;
matches.add(PasswordMatch()
..pattern = 'regex'
..token = token
..i = rx_match.start
..j = rx_match.start + rx_match.group(0)!.length - 1
..i = abs_start
..j = abs_end
..regex_name = name
..regex_match = rx_match);
last_index = abs_end + 1;
}
});
return sorted(matches);
Expand Down
11 changes: 11 additions & 0 deletions test/src/matching_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,17 @@ void main() {
}
});

test('regex matching terminates on overlapping matches', () {
// Regression test: two overlapping recent_year matches (here "2011"
// and "2001" sharing the digit at index 3) used to make `regex_match`
// compute a non-monotonic cursor and loop forever, growing its match
// list without bound until the process ran out of memory.
final matches = matching.regex_match('20112001Vi-');
expect(matches.length, 2);
expect(matches[0].token, '2011');
expect(matches[1].token, '2001');
});

test('date matching', () {
for (final sep in ['', ' ', '-', '/', '\\', '_', '.']) {
final password = "13${sep}2${sep}1921";
Expand Down