已合并
Fix CVE-2026-32274 #41
已合并
meiwei-space创建于 7月24日
共 2 个文件变更+177-1
@@ -0,0 +1,172 @@
1+From 4937fe6cf241139ddbfc16b0bdbb5b422798909d Mon Sep 17 00:00:00 2001
2+From: Jelle Zijlstra <jelle.zijlstra@gmail.com>
3+Date: Wed, 11 Mar 2026 19:57:24 -0700
4+Subject: [PATCH] Fix some shenanigans with the cache file and IPython (#5038)
5+ 
6+---
7+ src/black/handle_ipynb_magics.py | 59 ++++++++++++++++++++++++++++++++++++++++++--
8+ src/black/mode.py | 7 +++----
9+ tests/test_black.py | 9 +++++++++
10+ tests/test_ipynb.py | 20 ++++++++++++++++---
11+ 4 files changed, 85 insertions(+), 10 deletions(-)
12+ 
13+diff --git a/src/black/handle_ipynb_magics.py b/src/black/handle_ipynb_magics.py
14+index c84fe6219fb..e12537d8309 100644
15+--- a/src/black/handle_ipynb_magics.py
16++++ b/src/black/handle_ipynb_magics.py
17+ import collections
18+ import dataclasses
19+ import secrets
20++import string
21++from collections.abc import Collection
22+ import sys
23+ from functools import lru_cache
24+ from importlib.util import find_spec
25+@@ -160,30 +162,49 @@
26+ return transformed, replacements
27+
28+
29+-def get_token(src: str, magic: str) -> str:
30++def create_token(n_chars: int) -> str:
31++ """Create a randomly generated token that is n_chars characters long."""
32++ assert n_chars > 0
33++ if n_chars == 1:
34++ return secrets.choice(string.ascii_letters)
35++ if n_chars < 4:
36++ return "_" + "".join(
37++ secrets.choice(string.ascii_letters + string.digits + "_")
38++ for _ in range(n_chars - 1)
39++ )
40++ n_bytes = max(n_chars // 2 - 1, 1)
41++ token = secrets.token_hex(n_bytes)
42++ if len(token) + 3 > n_chars:
43++ n_bytes = max(0, n_chars - 3)
44++ token = secrets.token_hex(n_bytes)[: n_chars - 3]
45++ return f'b"{token}"'
46++
47++
48++def get_token(src: str, magic: str, existing_tokens: Collection[str] = ()) -> str:
49+ """Return randomly generated token to mask IPython magic with.
50+
51+ For example, if 'magic' was `%matplotlib inline`, then a possible
52+- token to mask it with would be `"43fdd17f7e5ddc83"`. The token
53+- will be the same length as the magic, and we make sure that it was
54+- not already present anywhere else in the cell.
55++ token is `b"1234"` or `b"abcd"`.
56++
57++ Args:
58++ src: The source string to search for existing tokens.
59++ magic: The magic command string to mask.
60++ existing_tokens: Collection of existing tokens to avoid collision.
61++
62++ Returns:
63++ A randomly generated token to mask `magic`.
64+ """
65+- assert magic
66+- nbytes = max(len(magic) // 2 - 1, 1)
67+- token = TOKEN_HEX(nbytes)
68++ n_chars = len(magic)
69++ token = create_token(n_chars)
70+ counter = 0
71+- while token in src:
72+- token = TOKEN_HEX(nbytes)
73++ while token in src or token in existing_tokens:
74++ token = create_token(n_chars)
75+ counter += 1
76+ if counter > 100:
77+ raise AssertionError(
78+- "INTERNAL ERROR: Black was not able to replace IPython magic. "
79+- "Please report a bug on https://github.com/psf/black/issues. "
80+- f"The magic might be helpful: {magic}"
81++ f"Could not generate a token for magic {magic!r}"
82+ ) from None
83+- if len(token) + 2 < len(magic):
84+- token = f"{token}."
85+- return f'"{token}"'
86++ return f'b"{token}"'
87+
88+
89+ def replace_cell_magics(src: str) -> Tuple[str, List[Replacement]]:
90+@@ -236,6 +257,7 @@
91+ The replacement, along with the transformed code, are returned.
92+ """
93+ replacements = []
94++ existing_tokens: set[str] = set()
95+ magic_finder = MagicFinder()
96+ magic_finder.visit(ast.parse(src))
97+ new_srcs = []
98+@@ -251,8 +273,9 @@
99+ offsets_and_magics[0].col_offset,
100+ offsets_and_magics[0].magic,
101+ )
102+- mask = get_token(src, magic)
103++ mask = get_token(src, magic, existing_tokens)
104+ replacements.append(Replacement(mask=mask, src=magic))
105++ existing_tokens.add(mask)
106+ line = line[:col_offset] + mask
107+ new_srcs.append(line)
108+ return "\n".join(new_srcs), replacements
109+@@ -272,7 +295,9 @@
110+ foo = bar
111+ """
112+ for replacement in replacements:
113+- src = src.replace(replacement.mask, replacement.src)
114++ if src.count(replacement.mask) != 1:
115++ raise NothingChanged
116++ src = src.replace(replacement.mask, replacement.src, 1)
117+ return src
118+
119+
120+ 
121+diff --git a/src/black/mode.py b/src/black/mode.py
122+index 51a825f30c4..2ed82d55885 100644
123+--- a/src/black/mode.py
124++++ b/src/black/mode.py
125+ + "@"
126+ + ",".join(sorted(self.python_cell_magics))
127+ )
128+- if len(features_and_magics) > _MAX_CACHE_KEY_PART_LENGTH:
129+- features_and_magics = sha256(features_and_magics.encode()).hexdigest()[
130+- :_MAX_CACHE_KEY_PART_LENGTH
131+- ]
132++ features_and_magics = sha256(features_and_magics.encode()).hexdigest()[
133++ :_MAX_CACHE_KEY_PART_LENGTH
134++ ]
135+ parts = [
136+ version_str,
137+ str(self.line_length),
138+ 
139+diff --git a/tests/test_black.py b/tests/test_black.py
140+index a8152e5cc10..ee04a7e9408 100644
141+--- a/tests/test_black.py
142++++ b/tests/test_black.py
143+ 
144+diff --git a/tests/test_ipynb.py b/tests/test_ipynb.py
145+index 12afb971e2c..aadf7058a83 100644
146+--- a/tests/test_ipynb.py
147++++ b/tests/test_ipynb.py
148+ from typing import ContextManager
149+
150+ import pytest
151+-from _pytest.monkeypatch import MonkeyPatch
152+ from click.testing import CliRunner
153++from pytest import MonkeyPatch
154+
155+ from black import (
156+ Mode,
157+@@ -17,7 +17,12 @@
158+ format_file_in_place,
159+ main,
160+ )
161+-from black.handle_ipynb_magics import jupyter_dependencies_are_installed
162++from black.handle_ipynb_magics import (
163++ Replacement,
164++ create_token,
165++ jupyter_dependencies_are_installed,
166++ unmask_cell,
167++)
168+ from tests.util import DATA_DIR, get_case_path, read_jupyter_notebook
169+
170+ with contextlib.suppress(ModuleNotFoundError):
171+--
172+2.37.2.windows.2
@@ -3,7 +3,7 @@
3 3 
4Name: python-%{pypi_name}4Name: python-%{pypi_name}
5Version: 24.2.05Version: 24.2.0
6-Release: 26+Release: 3
7Summary: The uncompromising code formatter7Summary: The uncompromising code formatter
8License: MIT8License: MIT
9URL: https://github.com/psf/black9URL: https://github.com/psf/black
@@ -12,6 +12,7 @@ Source0: %{url}/archive/%{version}/%{pypi_name}-%{version}.tar.gz
12BuildArch: noarch12BuildArch: noarch
13 13 
14Patch0: CVE-2024-21503.patch14Patch0: CVE-2024-21503.patch
15+Patch1: CVE-2026-32274.patch
15 16 
16BuildRequires: python3-devel17BuildRequires: python3-devel
17 18 
@@ -67,6 +68,9 @@ done
67%{python3_sitelib}/blib2to3/*68%{python3_sitelib}/blib2to3/*
68 69 
69%changelog70%changelog
71+* Fri Jul 24 2026 zhangchaorui <zhangchaorui@kylinos.cn> - 24.2.0-3
72+- Fix CVE-2026-32274: Cache file path manipulation vulnerability
73+ 
70* Wed Apr 17 2024 yanjianqing <yanjianqing@kylinos.cn> - 24.2.0-274* Wed Apr 17 2024 yanjianqing <yanjianqing@kylinos.cn> - 24.2.0-2
71- Fix CVE-2024-2150375- Fix CVE-2024-21503
72 76