Skip to content

Commit 37797ab

Browse files
committed
safe_join prevents windows special device names with compound extensions
1 parent 3db44c7 commit 37797ab

File tree

3 files changed

+23
-8
lines changed

3 files changed

+23
-8
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Version 3.1.5
55

66
Unreleased
77

8+
- ``safe_join`` on Windows does not allow more special device names, regardless
9+
of extension or surrounding spaces. :ghsa:`87hc-h4r5-73f7`
810
- The multipart form parser handles a ``\r\n`` sequence at a chunk boundary.
911
This fixes the previous attempt, which caused incorrect content lengths.
1012
:issue:`3065` :issue:`3077`

src/werkzeug/security.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
_os_alt_seps: list[str] = list(
1313
sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/"
1414
)
15+
# https://chrisdenton.github.io/omnipath/Special%20Dos%20Device%20Names.html
1516
_windows_device_files = {
16-
"CON",
17-
"PRN",
1817
"AUX",
18+
"CON",
19+
"CONIN$",
20+
"CONOUT$",
21+
*(f"COM{c}" for c in "123456789¹²³"),
22+
*(f"LPT{c}" for c in "123456789¹²³"),
1923
"NUL",
20-
*(f"COM{i}" for i in range(10)),
21-
*(f"LPT{i}" for i in range(10)),
24+
"PRN",
2225
}
2326

2427

@@ -148,8 +151,12 @@ def safe_join(directory: str, *pathnames: str) -> str | None:
148151
base directory.
149152
:return: A safe path, otherwise ``None``.
150153
154+
.. versionchanged:: 3.1.5
155+
More special device names, regardless of extension or trailing spaces,
156+
are not allowed on Windows.
157+
151158
.. versionchanged:: 3.1.4
152-
Special device names are disallowed on Windows.
159+
Special device names are not allowed on Windows.
153160
"""
154161
if not directory:
155162
# Ensure we end up with ./path if directory="" is given,
@@ -166,7 +173,7 @@ def safe_join(directory: str, *pathnames: str) -> str | None:
166173
any(sep in filename for sep in _os_alt_seps)
167174
or (
168175
os.name == "nt"
169-
and os.path.splitext(filename)[0].upper() in _windows_device_files
176+
and filename.partition(".")[0].strip().upper() in _windows_device_files
170177
)
171178
or os.path.isabs(filename)
172179
# ntpath.isabs doesn't catch this on Python < 3.11

tests/test_security.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,15 @@ def test_safe_join_empty_trusted():
7272
assert safe_join("", "c:test.txt") == "./c:test.txt"
7373

7474

75-
def test_safe_join_windows_special(monkeypatch: pytest.MonkeyPatch) -> None:
75+
@pytest.mark.parametrize(
76+
"name", ["CON", "CON.txt", "CON.txt.html", "CON ", "CON . txt"]
77+
)
78+
def test_safe_join_windows_special(monkeypatch: pytest.MonkeyPatch, name: str) -> None:
7679
"""Windows special device name is not allowed on Windows."""
7780
monkeypatch.setattr("os.name", "nt")
78-
assert safe_join("a", "CON") is None
81+
assert safe_join("a", name) is None
82+
83+
84+
def test_safe_join_not_windows_special(monkeypatch: pytest.MonkeyPatch) -> None:
7985
monkeypatch.setattr("os.name", "posix")
8086
assert safe_join("a", "CON") == "a/CON"

0 commit comments

Comments
 (0)