Moderate severityOSV Advisory· Published Jan 8, 2026· Updated Jan 8, 2026
Werkzeug safe_join() allows Windows special device names with compound extensions
CVE-2026-21860
Description
Werkzeug is a comprehensive WSGI web application library. Prior to version 3.1.5, Werkzeug's safe_join function allows path segments with Windows device names that have file extensions or trailing spaces. On Windows, there are special device names such as CON, AUX, etc that are implicitly present and readable in every directory. Windows still accepts them with any file extension, such as CON.txt, or trailing spaces such as CON. This issue has been patched in version 3.1.5.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
WerkzeugPyPI | < 3.1.5 | 3.1.5 |
Affected products
1Patches
13 files changed · +23 −8
CHANGES.rst+2 −0 modified@@ -5,6 +5,8 @@ Version 3.1.5 Unreleased +- ``safe_join`` on Windows does not allow more special device names, regardless + of extension or surrounding spaces. :ghsa:`87hc-h4r5-73f7` - The multipart form parser handles a ``\r\n`` sequence at a chunk boundary. This fixes the previous attempt, which caused incorrect content lengths. :issue:`3065` :issue:`3077`
src/werkzeug/security.py+13 −6 modified@@ -12,13 +12,16 @@ _os_alt_seps: list[str] = list( sep for sep in [os.sep, os.path.altsep] if sep is not None and sep != "/" ) +# https://chrisdenton.github.io/omnipath/Special%20Dos%20Device%20Names.html _windows_device_files = { - "CON", - "PRN", "AUX", + "CON", + "CONIN$", + "CONOUT$", + *(f"COM{c}" for c in "123456789¹²³"), + *(f"LPT{c}" for c in "123456789¹²³"), "NUL", - *(f"COM{i}" for i in range(10)), - *(f"LPT{i}" for i in range(10)), + "PRN", } @@ -148,8 +151,12 @@ def safe_join(directory: str, *pathnames: str) -> str | None: base directory. :return: A safe path, otherwise ``None``. + .. versionchanged:: 3.1.5 + More special device names, regardless of extension or trailing spaces, + are not allowed on Windows. + .. versionchanged:: 3.1.4 - Special device names are disallowed on Windows. + Special device names are not allowed on Windows. """ if not directory: # Ensure we end up with ./path if directory="" is given, @@ -166,7 +173,7 @@ def safe_join(directory: str, *pathnames: str) -> str | None: any(sep in filename for sep in _os_alt_seps) or ( os.name == "nt" - and os.path.splitext(filename)[0].upper() in _windows_device_files + and filename.partition(".")[0].strip().upper() in _windows_device_files ) or os.path.isabs(filename) # ntpath.isabs doesn't catch this on Python < 3.11
tests/test_security.py+8 −2 modified@@ -72,9 +72,15 @@ def test_safe_join_empty_trusted(): assert safe_join("", "c:test.txt") == "./c:test.txt" -def test_safe_join_windows_special(monkeypatch: pytest.MonkeyPatch) -> None: +@pytest.mark.parametrize( + "name", ["CON", "CON.txt", "CON.txt.html", "CON ", "CON . txt"] +) +def test_safe_join_windows_special(monkeypatch: pytest.MonkeyPatch, name: str) -> None: """Windows special device name is not allowed on Windows.""" monkeypatch.setattr("os.name", "nt") - assert safe_join("a", "CON") is None + assert safe_join("a", name) is None + + +def test_safe_join_not_windows_special(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setattr("os.name", "posix") assert safe_join("a", "CON") == "a/CON"
Vulnerability mechanics
Synthesis attempt was rejected by the grounding validator. Re-run pending.
References
4- github.com/advisories/GHSA-87hc-h4r5-73f7ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-21860ghsaADVISORY
- github.com/pallets/werkzeug/commit/7ae1d254e04a0c33e241ac1cca4783ce6c875ca3ghsax_refsource_MISCWEB
- github.com/pallets/werkzeug/security/advisories/GHSA-87hc-h4r5-73f7ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.