Skip to content

Commit 7d2012d

Browse files
[3.15] gh-160420: Reject drive letters in ZoneInfo key components on Windows (GH-160421) (#160447)
Co-authored-by: Gregory P. Smith <68491+gpshead@users.noreply.github.com>
1 parent 29efbf4 commit 7d2012d

3 files changed

Lines changed: 50 additions & 0 deletions

File tree

‎Lib/test/test_zoneinfo/test_zoneinfo.py‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import importlib.metadata
77
import io
88
import json
9+
import ntpath
910
import os
1011
import pathlib
1112
import pickle
@@ -14,9 +15,11 @@
1415
import struct
1516
import sys
1617
import tempfile
18+
import types
1719
import unittest
1820
from datetime import date, datetime, time, timedelta, timezone
1921
from functools import cached_property
22+
from unittest import mock
2023

2124
from test.support import MISSING_C_DOCSTRINGS
2225
from test.support.os_helper import EnvironmentVarGuard, FakePath
@@ -2017,6 +2020,38 @@ def test_tzpath_type_error(self):
20172020
with self.assertRaises(TypeError):
20182021
self.module.reset_tzpath(bad_value)
20192022

2023+
def test_validate_key_windows_paths(self):
2024+
# Check the key validation with Windows path rules on every platform.
2025+
tzpath = self.module._tzpath
2026+
base = ntpath.normpath(ntpath.join("_", "_"))[:-1]
2027+
fake_os = types.SimpleNamespace(path=ntpath)
2028+
2029+
good_keys = [
2030+
"UTC",
2031+
"America/New_York",
2032+
"America/Argentina/Buenos_Aires",
2033+
"Etc/GMT+1",
2034+
]
2035+
bad_keys = [
2036+
"C:\\Windows\\win.ini",
2037+
"America/C:\\Windows\\win.ini", # Drive in a component
2038+
"America/C:/Windows/win.ini",
2039+
"America/D:secret", # Drive-relative component
2040+
"America/Argentina/D:secret",
2041+
"America/\\Windows\\win.ini", # Rooted component
2042+
"America/\\\\server\\share\\file", # UNC component
2043+
]
2044+
2045+
with mock.patch.object(tzpath, "os", fake_os):
2046+
for key in good_keys:
2047+
with self.subTest(key=key):
2048+
tzpath._validate_tzfile_path(key, _base=base)
2049+
2050+
for key in bad_keys:
2051+
with self.subTest(key=key):
2052+
with self.assertRaises(ValueError):
2053+
tzpath._validate_tzfile_path(key, _base=base)
2054+
20202055
def test_tzpath_attribute(self):
20212056
tzpath_0 = [f"{DRIVE}/one", f"{DRIVE}/two"]
20222057
tzpath_1 = [f"{DRIVE}/three"]

‎Lib/zoneinfo/_tzpath.py‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ def _validate_tzfile_path(path, _base=_TEST_PATH):
111111
f"ZoneInfo keys must refer to subdirectories of TZPATH, got: {path}"
112112
)
113113

114+
# load_tzdata() joins the last "/"-separated component of the key to the
115+
# tzdata package directory on its own. On Windows a component with a
116+
# drive or root, such as "C:\\file" in "America/C:\\file", would replace
117+
# that directory instead of being looked up inside it.
118+
for component in path.split("/"):
119+
drive, root, _ = os.path.splitroot(component)
120+
if drive or root:
121+
raise ValueError(
122+
f"ZoneInfo keys must not contain drives or roots, got: {path}"
123+
)
124+
114125

115126
del _TEST_PATH
116127

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :class:`zoneinfo.ZoneInfo` on Windows accepting keys in which a component
2+
after a ``/`` starts with a drive letter, such as ``America/C:\file``. Such a
3+
key could open a file outside the ``tzdata`` package; it now raises
4+
:exc:`ValueError`.

0 commit comments

Comments
 (0)