https://github.com/python/cpython/commit/0615b5c0d9ee3854bc50fe2bcc68e6799cead14a
commit: 0615b5c0d9ee3854bc50fe2bcc68e6799cead14a
branch: main
author: Gregory P. Smith <[email protected]>
committer: gpshead <[email protected]>
date: 2026-10-23T17:20:53Z
summary:
gh-160420: Reject drive letters in ZoneInfo key components on Windows
(GH-160421)
A key such as "America/C:\file" passed validation, and the tzdata
loader then joined "C:\file" to the package directory, which replaced it.
ANT-2026-IZMAOLCQ
files:
A Misc/NEWS.d/next/Security/2026-10-22-08-05-32.gh-issue-160420.tZ9dRv.rst
M Lib/test/test_zoneinfo/test_zoneinfo.py
M Lib/zoneinfo/_tzpath.py
diff --git a/Lib/test/test_zoneinfo/test_zoneinfo.py
b/Lib/test/test_zoneinfo/test_zoneinfo.py
index 40696b09d95719..6d00040babbd72 100644
--- a/Lib/test/test_zoneinfo/test_zoneinfo.py
+++ b/Lib/test/test_zoneinfo/test_zoneinfo.py
@@ -6,6 +6,7 @@
import importlib.metadata
import io
import json
+import ntpath
import os
import pathlib
import pickle
@@ -13,9 +14,11 @@
import shutil
import struct
import tempfile
+import types
import unittest
from datetime import date, datetime, time, timedelta, timezone
from functools import cached_property
+from unittest import mock
from test.support import MISSING_C_DOCSTRINGS
from test.support.os_helper import EnvironmentVarGuard, FakePath
@@ -2016,6 +2019,38 @@ def test_tzpath_type_error(self):
with self.assertRaises(TypeError):
self.module.reset_tzpath(bad_value)
+ def test_validate_key_windows_paths(self):
+ # Check the key validation with Windows path rules on every platform.
+ tzpath = self.module._tzpath
+ base = ntpath.normpath(ntpath.join("_", "_"))[:-1]
+ fake_os = types.SimpleNamespace(path=ntpath)
+
+ good_keys = [
+ "UTC",
+ "America/New_York",
+ "America/Argentina/Buenos_Aires",
+ "Etc/GMT+1",
+ ]
+ bad_keys = [
+ "C:\\Windows\\win.ini",
+ "America/C:\\Windows\\win.ini", # Drive in a component
+ "America/C:/Windows/win.ini",
+ "America/D:secret", # Drive-relative component
+ "America/Argentina/D:secret",
+ "America/\\Windows\\win.ini", # Rooted component
+ "America/\\\\server\\share\\file", # UNC component
+ ]
+
+ with mock.patch.object(tzpath, "os", fake_os):
+ for key in good_keys:
+ with self.subTest(key=key):
+ tzpath._validate_tzfile_path(key, _base=base)
+
+ for key in bad_keys:
+ with self.subTest(key=key):
+ with self.assertRaises(ValueError):
+ tzpath._validate_tzfile_path(key, _base=base)
+
def test_tzpath_attribute(self):
tzpath_0 = [f"{DRIVE}/one", f"{DRIVE}/two"]
tzpath_1 = [f"{DRIVE}/three"]
diff --git a/Lib/zoneinfo/_tzpath.py b/Lib/zoneinfo/_tzpath.py
index 3661c837daa0a4..7b6396444fd911 100644
--- a/Lib/zoneinfo/_tzpath.py
+++ b/Lib/zoneinfo/_tzpath.py
@@ -111,6 +111,17 @@ def _validate_tzfile_path(path, _base=_TEST_PATH):
f"ZoneInfo keys must refer to subdirectories of TZPATH, got:
{path}"
)
+ # load_tzdata() joins the last "/"-separated component of the key to the
+ # tzdata package directory on its own. On Windows a component with a
+ # drive or root, such as "C:\\file" in "America/C:\\file", would replace
+ # that directory instead of being looked up inside it.
+ for component in path.split("/"):
+ drive, root, _ = os.path.splitroot(component)
+ if drive or root:
+ raise ValueError(
+ f"ZoneInfo keys must not contain drives or roots, got: {path}"
+ )
+
del _TEST_PATH
diff --git
a/Misc/NEWS.d/next/Security/2026-10-22-08-05-32.gh-issue-160420.tZ9dRv.rst
b/Misc/NEWS.d/next/Security/2026-10-22-08-05-32.gh-issue-160420.tZ9dRv.rst
new file mode 100644
index 00000000000000..2255056dac577e
--- /dev/null
+++ b/Misc/NEWS.d/next/Security/2026-10-22-08-05-32.gh-issue-160420.tZ9dRv.rst
@@ -0,0 +1,4 @@
+Fix :class:`zoneinfo.ZoneInfo` on Windows accepting keys in which a component
+after a ``/`` starts with a drive letter, such as ``America/C:\file``. Such a
+key could open a file outside the ``tzdata`` package; it now raises
+:exc:`ValueError`.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]