File tree Expand file tree Collapse file tree 3 files changed +30
-13
lines changed Expand file tree Collapse file tree 3 files changed +30
-13
lines changed Original file line number Diff line number Diff line change 66
77<!-- Include any especially major or disruptive changes here -->
88
9+ This release is a milestone: it fixes Black's first CVE security vulnerability. If you
10+ run Black on untrusted input, or if you habitually put thousands of leading tab
11+ characters in your docstrings, you are strongly encouraged to upgrade immediately to fix
12+ [ CVE-2024 -21503] ( https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-21503 ) .
13+
14+ This release also fixes a bug in Black's AST safety check that allowed Black to make
15+ incorrect changes to certain f-strings that are valid in Python 3.12 and higher.
16+
917### Stable style
1018
1119<!-- Changes that affect Black's stable style -->
3644
3745### Performance
3846
39- <!-- Changes that improve Black's performance. -->
47+ - Fix catastrophic performance on docstrings that contain large numbers of leading tab
48+ characters. This fixes
49+ [ CVE-2024 -21503] ( https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-21503 ) .
50+ (#4278 )
4051
4152### Output
4253
Original file line number Diff line number Diff line change 1414STRING_PREFIX_RE : Final = re .compile (
1515 r"^([" + STRING_PREFIX_CHARS + r"]*)(.*)$" , re .DOTALL
1616)
17- FIRST_NON_WHITESPACE_RE : Final = re .compile (r"\s*\t+\s*(\S)" )
1817UNICODE_ESCAPE_RE : Final = re .compile (
1918 r"(?P<backslashes>\\+)(?P<body>"
2019 r"(u(?P<u>[a-fA-F0-9]{4}))" # Character with 16-bit hex value xxxx
@@ -51,18 +50,13 @@ def lines_with_leading_tabs_expanded(s: str) -> List[str]:
5150 """
5251 lines = []
5352 for line in s .splitlines ():
54- # Find the index of the first non-whitespace character after a string of
55- # whitespace that includes at least one tab
56- match = FIRST_NON_WHITESPACE_RE .match (line )
57- if match :
58- first_non_whitespace_idx = match .start (1 )
59-
60- lines .append (
61- line [:first_non_whitespace_idx ].expandtabs ()
62- + line [first_non_whitespace_idx :]
63- )
64- else :
53+ stripped_line = line .lstrip ()
54+ if not stripped_line or stripped_line == line :
6555 lines .append (line )
56+ else :
57+ prefix_length = len (line ) - len (stripped_line )
58+ prefix = line [:prefix_length ].expandtabs ()
59+ lines .append (prefix + stripped_line )
6660 if s .endswith ("\n " ):
6761 lines .append ("" )
6862 return lines
Original file line number Diff line number Diff line change 4848from black .output import color_diff , diff
4949from black .parsing import ASTSafetyError
5050from black .report import Report
51+ from black .strings import lines_with_leading_tabs_expanded
5152
5253# Import other test classes
5354from tests .util import (
@@ -2041,6 +2042,17 @@ def test_line_ranges_in_pyproject_toml(self) -> None:
20412042 b"Cannot use line-ranges in the pyproject.toml file." in result .stderr_bytes
20422043 )
20432044
2045+ def test_lines_with_leading_tabs_expanded (self ) -> None :
2046+ # See CVE-2024-21503. Mostly test that this completes in a reasonable
2047+ # time.
2048+ payload = "\t " * 10_000
2049+ assert lines_with_leading_tabs_expanded (payload ) == [payload ]
2050+
2051+ tab = " " * 8
2052+ assert lines_with_leading_tabs_expanded ("\t x" ) == [f"{ tab } x" ]
2053+ assert lines_with_leading_tabs_expanded ("\t \t x" ) == [f"{ tab } { tab } x" ]
2054+ assert lines_with_leading_tabs_expanded ("\t x\n y" ) == [f"{ tab } x" , " y" ]
2055+
20442056
20452057class TestCaching :
20462058 def test_get_cache_dir (
You can’t perform that action at this time.
0 commit comments