Your message dated Sat, 16 May 2026 10:23:16 +0000
with message-id <[email protected]>
and subject line Released with 13.5
has caused the Debian Bug report #1135836,
regarding trixie-pu: package black/25.1.0-3+deb13u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1135836: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1135836
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:black
User: [email protected]
Usertags: pu

  * CVE-2026-32274: Arbitrary file writes from unsanitized user input
    (Closes: #1130657)
diffstat for black-25.1.0 black-25.1.0

 changelog                                                               |    8 
 patches/0001-Fix-some-shenanigans-with-the-cache-file-and-IPython.patch |  175 
++++++++++
 patches/series                                                          |    1 
 3 files changed, 184 insertions(+)

diff -Nru black-25.1.0/debian/changelog black-25.1.0/debian/changelog
--- black-25.1.0/debian/changelog       2025-05-27 16:41:12.000000000 +0300
+++ black-25.1.0/debian/changelog       2026-05-06 12:45:09.000000000 +0300
@@ -1,3 +1,11 @@
+black (25.1.0-3+deb13u1) trixie; urgency=medium
+
+  * Non-maintainer upload.
+  * CVE-2026-32274: Arbitrary file writes from unsanitized user input
+    (Closes: #1130657)
+
+ -- Adrian Bunk <[email protected]>  Wed, 06 May 2026 12:45:09 +0300
+
 black (25.1.0-3) unstable; urgency=medium
 
   * Team upload
diff -Nru 
black-25.1.0/debian/patches/0001-Fix-some-shenanigans-with-the-cache-file-and-IPython.patch
 
black-25.1.0/debian/patches/0001-Fix-some-shenanigans-with-the-cache-file-and-IPython.patch
--- 
black-25.1.0/debian/patches/0001-Fix-some-shenanigans-with-the-cache-file-and-IPython.patch
 1970-01-01 02:00:00.000000000 +0200
+++ 
black-25.1.0/debian/patches/0001-Fix-some-shenanigans-with-the-cache-file-and-IPython.patch
 2026-05-06 12:44:42.000000000 +0300
@@ -0,0 +1,175 @@
+From d3a56c22dc6273dc9d7456b77b6e1f6cb5141b85 Mon Sep 17 00:00:00 2001
+From: Jelle Zijlstra <[email protected]>
+Date: Wed, 11 Mar 2026 19:57:24 -0700
+Subject: Fix some shenanigans with the cache file and IPython (#5038)
+
+---
+ src/black/handle_ipynb_magics.py | 21 +++++++++++++++++----
+ src/black/mode.py                |  7 +++----
+ tests/test_black.py              |  9 +++++++++
+ tests/test_ipynb.py              | 20 ++++++++++++++++++--
+ 4 files changed, 47 insertions(+), 10 deletions(-)
+
+diff --git a/src/black/handle_ipynb_magics.py 
b/src/black/handle_ipynb_magics.py
+index dd680bf..1c6ea4e 100644
+--- a/src/black/handle_ipynb_magics.py
++++ b/src/black/handle_ipynb_magics.py
+@@ -5,7 +5,9 @@
+ import dataclasses
+ import re
+ import secrets
++import string
+ import sys
++from collections.abc import Collection
+ from functools import lru_cache
+ from importlib.util import find_spec
+ from typing import Optional
+@@ -194,6 +196,13 @@ def mask_cell(src: str) -> tuple[str, list[Replacement]]:
+ def create_token(n_chars: int) -> str:
+     """Create a randomly generated token that is n_chars characters long."""
+     assert n_chars > 0
++    if n_chars == 1:
++        return secrets.choice(string.ascii_letters)
++    if n_chars < 4:
++        return "_" + "".join(
++            secrets.choice(string.ascii_letters + string.digits + "_")
++            for _ in range(n_chars - 1)
++        )
+     n_bytes = max(n_chars // 2 - 1, 1)
+     token = secrets.token_hex(n_bytes)
+     if len(token) + 3 > n_chars:
+@@ -203,7 +212,7 @@ def create_token(n_chars: int) -> str:
+     return f'b"{token}"'
+ 
+ 
+-def get_token(src: str, magic: str) -> str:
++def get_token(src: str, magic: str, existing_tokens: Collection[str] = ()) -> 
str:
+     """Return randomly generated token to mask IPython magic with.
+ 
+     For example, if 'magic' was `%matplotlib inline`, then a possible
+@@ -215,7 +224,7 @@ def get_token(src: str, magic: str) -> str:
+     n_chars = len(magic)
+     token = create_token(n_chars)
+     counter = 0
+-    while token in src:
++    while token in src or token in existing_tokens:
+         token = create_token(n_chars)
+         counter += 1
+         if counter > 100:
+@@ -277,6 +286,7 @@ def replace_magics(src: str) -> tuple[str, 
list[Replacement]]:
+     The replacement, along with the transformed code, are returned.
+     """
+     replacements = []
++    existing_tokens: set[str] = set()
+     magic_finder = MagicFinder()
+     magic_finder.visit(ast.parse(src))
+     new_srcs = []
+@@ -292,8 +302,9 @@ def replace_magics(src: str) -> tuple[str, 
list[Replacement]]:
+                 offsets_and_magics[0].col_offset,
+                 offsets_and_magics[0].magic,
+             )
+-            mask = get_token(src, magic)
++            mask = get_token(src, magic, existing_tokens)
+             replacements.append(Replacement(mask=mask, src=magic))
++            existing_tokens.add(mask)
+             line = line[:col_offset] + mask
+         new_srcs.append(line)
+     return "\n".join(new_srcs), replacements
+@@ -313,7 +324,9 @@ def unmask_cell(src: str, replacements: list[Replacement]) 
-> str:
+         foo = bar
+     """
+     for replacement in replacements:
+-        src = src.replace(replacement.mask, replacement.src)
++        if src.count(replacement.mask) != 1:
++            raise NothingChanged
++        src = src.replace(replacement.mask, replacement.src, 1)
+     return src
+ 
+ 
+diff --git a/src/black/mode.py b/src/black/mode.py
+index 7335bd1..1b0965c 100644
+--- a/src/black/mode.py
++++ b/src/black/mode.py
+@@ -267,10 +267,9 @@ def get_cache_key(self) -> str:
+             + "@"
+             + ",".join(sorted(self.python_cell_magics))
+         )
+-        if len(features_and_magics) > _MAX_CACHE_KEY_PART_LENGTH:
+-            features_and_magics = 
sha256(features_and_magics.encode()).hexdigest()[
+-                :_MAX_CACHE_KEY_PART_LENGTH
+-            ]
++        features_and_magics = 
sha256(features_and_magics.encode()).hexdigest()[
++            :_MAX_CACHE_KEY_PART_LENGTH
++        ]
+         parts = [
+             version_str,
+             str(self.line_length),
+diff --git a/tests/test_black.py b/tests/test_black.py
+index ca19c17..a1658e7 100644
+--- a/tests/test_black.py
++++ b/tests/test_black.py
+@@ -2128,6 +2128,15 @@ def test_cache_file_length(self) -> None:
+             # doesn't get too crazy.
+             assert len(cache_file.name) <= 96
+ 
++    def test_cache_file_path_ignores_python_cell_magic_separators(self) -> 
None:
++        mode = replace(DEFAULT_MODE, 
python_cell_magics={"../../../tmp/pwned"})
++        with cache_dir() as workspace:
++            cache_file = get_cache_file(mode)
++            assert cache_file.parent == workspace
++            assert "/" not in cache_file.name
++            assert ".." not in cache_file.name
++            assert "../../../tmp/pwned" not in mode.get_cache_key()
++
+     def test_cache_broken_file(self) -> None:
+         mode = DEFAULT_MODE
+         with cache_dir() as workspace:
+diff --git a/tests/test_ipynb.py b/tests/test_ipynb.py
+index 12afb97..aadf705 100644
+--- a/tests/test_ipynb.py
++++ b/tests/test_ipynb.py
+@@ -6,8 +6,8 @@
+ from dataclasses import replace
+ 
+ import pytest
+-from _pytest.monkeypatch import MonkeyPatch
+ from click.testing import CliRunner
++from pytest import MonkeyPatch
+ 
+ from black import (
+     Mode,
+@@ -17,7 +17,12 @@
+     format_file_in_place,
+     main,
+ )
+-from black.handle_ipynb_magics import jupyter_dependencies_are_installed
++from black.handle_ipynb_magics import (
++    Replacement,
++    create_token,
++    jupyter_dependencies_are_installed,
++    unmask_cell,
++)
+ from tests.util import DATA_DIR, get_case_path, read_jupyter_notebook
+ 
+ with contextlib.suppress(ModuleNotFoundError):
+@@ -39,6 +44,17 @@ def test_noop() -> None:
+         format_cell(src, fast=True, mode=JUPYTER_MODE)
+ 
+ 
[email protected]("n_chars", [1, 2, 3, 4, 5, 17])
++def test_create_token_uses_requested_length(n_chars: int) -> None:
++    assert len(create_token(n_chars)) == n_chars
++
++
++def test_unmask_cell_raises_when_token_is_not_unique() -> None:
++    replacement = Replacement(mask='b"dead"', src="%time")
++    with pytest.raises(NothingChanged):
++        unmask_cell(f"{replacement.mask}\nvalue = {replacement.mask}", 
[replacement])
++
++
+ @pytest.mark.parametrize("fast", [True, False])
+ def test_trailing_semicolon(fast: bool) -> None:
+     src = 'foo = "a" ;'
+-- 
+2.47.3
+
diff -Nru black-25.1.0/debian/patches/series black-25.1.0/debian/patches/series
--- black-25.1.0/debian/patches/series  2025-05-27 16:35:57.000000000 +0300
+++ black-25.1.0/debian/patches/series  2026-05-06 12:45:09.000000000 +0300
@@ -3,3 +3,4 @@
 docs_version.patch
 privacy
 click-mix-stderr.patch
+0001-Fix-some-shenanigans-with-the-cache-file-and-IPython.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 13.5

This update has been released as part of Debian 13.5.

--- End Message ---

Reply via email to