Skip to content
Merged
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
1 change: 1 addition & 0 deletions docs/changelog/199.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Reject format widths in interpreter sysconfig paths before they allocate oversized strings.
5 changes: 4 additions & 1 deletion src/python_discovery/_py_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from collections import OrderedDict
from itertools import product
from os.path import normcase, realpath
from string import digits
from string import Formatter, digits
from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple, TypedDict

from ._py_info_collect import PythonInfoCollector
Expand Down Expand Up @@ -136,6 +136,9 @@ def sysconfig_path(self, key: str, config_var: dict[str, str | int | None] | Non
pattern = self.sysconfig_paths.get(key)
if pattern is None:
return ""
if any(format_spec for _, _, format_spec, _ in Formatter().parse(pattern)):
msg = f"sysconfig path {key!r} contains format specifiers"
raise ValueError(msg)
if config_var is None:
config_var = self.sysconfig_vars
else:
Expand Down
8 changes: 8 additions & 0 deletions tests/test_py_info_extra.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,14 @@ def test_install_path() -> None:
assert isinstance(CURRENT.install_path("purelib"), str)


def test_install_path_rejects_format_width() -> None:
info = copy.deepcopy(CURRENT)
info.distutils_install = {}
info.sysconfig_paths["purelib"] = "{base:>1000000}/lib"
with pytest.raises(ValueError, match="format specifiers"):
info.install_path("purelib")


def test_system_include() -> None:
result = CURRENT.system_include
assert isinstance(result, str)
Expand Down