| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,6 +8,7 @@ Changelog | |||
| 8 | 8 | Security fixes for | |
| 9 | 9 | ||
| 10 | 10 | * https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-w8jc-g24h-crhw | |
| 11 | + * https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-m64x-33q8-m5h7 | ||
| 11 | 12 | ||
| 12 | 13 | 3.2.0 | |
| 13 | 14 | ===== | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -318,9 +318,10 @@ def parse_date(string_date: Union[str, datetime]) -> Tuple[int, int]: | |||
| 318 | 318 | # END handle exceptions | |
| 319 | 319 | ||
| 320 | 320 | ||
| 321 | - # Precompiled regexes | ||
| 322 | - _re_actor_epoch = re.compile(r"^.+? (.*) (\d+) ([+-]\d+).*$") | ||
| 323 | - _re_only_actor = re.compile(r"^.+? (.*)$") | ||
| 321 | + # Check the line ending once, before parsing fields, to avoid repeated backtracking. | ||
| 322 | + # Keep the field name from consuming spaces belonging to the actor. | ||
| 323 | + _re_actor_epoch = re.compile(r"^(?=[^\n]*$).[^ \n]* (.*) (\d+) ([+-]\d+)") | ||
| 324 | + _re_only_actor = re.compile(r"^.[^ \n]* (.*)$") | ||
| 324 | 325 | ||
| 325 | 326 | ||
| 326 | 327 | def parse_actor_and_date(line: str) -> Tuple[Actor, int, int]: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,7 @@ | |||
| 22 | 22 | from git.objects.util import ( | |
| 23 | 23 | altz_to_utctz_str, | |
| 24 | 24 | from_timestamp, | |
| 25 | + parse_actor_and_date, | ||
| 25 | 26 | parse_date, | |
| 26 | 27 | tzoffset, | |
| 27 | 28 | utctz_to_altz, | |
@@ -571,6 +572,54 @@ def test_actor_from_string(self): | |||
| 571 | 572 | Actor("name last another", "some-very-long-email@example.com"), | |
| 572 | 573 | ) | |
| 573 | 574 | ||
| 575 | + @ddt.data( | ||
| 576 | + ("", Actor("", None), 0, 0), | ||
| 577 | + ("author", Actor("author", None), 0, 0), | ||
| 578 | + ("author Name <email> 42 -0700", Actor("Name", "email"), 42, 25200), | ||
| 579 | + ("committer Name <email> 42 +0530\n", Actor("Name", "email"), 42, -19800), | ||
| 580 | + ("tagger Name <email> 42 +0000\r\n", Actor("Name", "email"), 42, 0), | ||
| 581 | + ("author Name <email> 42 -0700 trailing", Actor("Name", "email"), 42, 25200), | ||
| 582 | + ("author Name <email> 1 +0 42 -0700", Actor("Name", "email"), 42, 25200), | ||
| 583 | + ("author Name <email> invalid -0700", Actor("Name", "email"), 0, 0), | ||
| 584 | + ("author Name <email> 42 invalid", Actor("Name", "email"), 0, 0), | ||
| 585 | + ("author 42 -0700", Actor("42 -0700", None), 0, 0), | ||
| 586 | + ("author 42 -0700", Actor("", None), 42, 25200), | ||
| 587 | + (" author Name <email> 42 -0700", Actor("Name", "email"), 42, 25200), | ||
| 588 | + ("author Näme <email> ١ +٠١٣٠", Actor("Näme", "email"), 1, -5400), | ||
| 589 | + ("author\nName <email> 42 -0700", Actor("author\nName <email> 42 -0700", None), 0, 0), | ||
| 590 | + ("author Name <email> 42 -0700\nextra", Actor("author Name", "email"), 0, 0), | ||
| 591 | + ) | ||
| 592 | + @ddt.unpack | ||
| 593 | + def test_parse_actor_and_date(self, line, actor, epoch, offset): | ||
| 594 | + self.assertEqual(parse_actor_and_date(line), (actor, epoch, offset)) | ||
| 595 | + | ||
| 596 | + def test_parse_actor_and_date_long_malformed_lines(self): | ||
| 597 | + padding = " " * 64_000 | ||
| 598 | + for field in ("author", "committer", "tagger"): | ||
| 599 | + for tail in ("", "<unterminated", "invalid -0700", "42", "-0700", "42 invalid", "42 +"): | ||
| 600 | + actor_text = padding + tail | ||
| 601 | + start = time.process_time() | ||
| 602 | + result = parse_actor_and_date(f"{field} {actor_text}") | ||
| 603 | + elapsed = time.process_time() - start | ||
| 604 | + # Leave ample CPU time for slow runners, but catch excessive backtracking. | ||
| 605 | + self.assertLess(elapsed, 1.0, (field, tail)) | ||
| 606 | + self.assertEqual(result, (Actor(actor_text, None), 0, 0)) | ||
| 607 | + | ||
| 608 | + name = "Long name " * 6_400 | ||
| 609 | + self.assertEqual( | ||
| 610 | + parse_actor_and_date(f"{field} {name}<email> 42 -0700"), | ||
| 611 | + (Actor(name.rstrip(), "email"), 42, 25200), | ||
| 612 | + ) | ||
| 613 | + | ||
| 614 | + def test_parse_actor_and_date_long_multiline_input(self): | ||
| 615 | + for field in ("author", "committer", "tagger"): | ||
| 616 | + line = f"{field} Name <email> 42 +" + "0" * 64_000 + "\nextra" | ||
| 617 | + start = time.process_time() | ||
| 618 | + result = parse_actor_and_date(line) | ||
| 619 | + elapsed = time.process_time() - start | ||
| 620 | + self.assertLess(elapsed, 1.0, field) | ||
| 621 | + self.assertEqual(result, (Actor(f"{field} Name", "email"), 0, 0)) | ||
| 622 | + | ||
| 574 | 623 | @ddt.data( | |
| 575 | 624 | ("name", ""), | |
| 576 | 625 | ("name", "prefix_"), | |
| Back | FazBrowse Home | New Git URL |
0 commit comments