From 0cee0c4d94fe1b3fc86041416cd60170212ed108 Mon Sep 17 00:00:00 2001 From: barneygale Date: Tue, 3 Sep 2024 19:34:38 +0100 Subject: [PATCH] GH-123599: Reject non-local authority in `pathlib.Path.from_uri()` on POSIX Raise `ValueError` in `pathlib.Path.from_uri()` if the given `file:` URI specifies a non-empty, non-`localhost` authority, and we're running on a platform without support for UNC paths. --- Doc/library/pathlib.rst | 6 ++++++ Lib/pathlib/_local.py | 3 +++ Lib/test/test_pathlib/test_pathlib.py | 7 ++++--- .../Library/2024-09-03-19-36-33.gh-issue-123599.TzDIha.rst | 3 +++ 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2024-09-03-19-36-33.gh-issue-123599.TzDIha.rst diff --git a/Doc/library/pathlib.rst b/Doc/library/pathlib.rst index 4380122eb1be7d..ea399f84b23628 100644 --- a/Doc/library/pathlib.rst +++ b/Doc/library/pathlib.rst @@ -869,6 +869,12 @@ conforming to :rfc:`8089`. :exc:`ValueError` is raised if the URI does not start with ``file:``, or the parsed path isn't absolute. + On POSIX systems, :exc:`ValueError` is raised if the URI specifies a + non-local authority:: + + >>> Path.from_uri('file://server/share') + ValueError: URI is not local: 'file://server/share' + .. versionadded:: 3.13 diff --git a/Lib/pathlib/_local.py b/Lib/pathlib/_local.py index 1c02e4168d3a9e..86e3d97f655a31 100644 --- a/Lib/pathlib/_local.py +++ b/Lib/pathlib/_local.py @@ -904,6 +904,9 @@ def from_uri(cls, uri): elif path[:12] == '//localhost/': # Remove 'localhost' authority path = path[11:] + elif path[:2] == '//' and os.name != 'nt': + # UNC paths aren't supported on POSIX + raise ValueError(f"URI is not local: {uri!r}") if path[:3] == '///' or (path[:1] == '/' and path[2:3] in ':|'): # Remove slash before DOS device/UNC path path = path[1:] diff --git a/Lib/test/test_pathlib/test_pathlib.py b/Lib/test/test_pathlib/test_pathlib.py index b47b4a194cfaa9..5849612566da4f 100644 --- a/Lib/test/test_pathlib/test_pathlib.py +++ b/Lib/test/test_pathlib/test_pathlib.py @@ -1698,21 +1698,22 @@ def test_handling_bad_descriptor(self): def test_from_uri_posix(self): P = self.cls self.assertEqual(P.from_uri('file:/foo/bar'), P('/foo/bar')) - self.assertEqual(P.from_uri('file://foo/bar'), P('//foo/bar')) self.assertEqual(P.from_uri('file:///foo/bar'), P('/foo/bar')) self.assertEqual(P.from_uri('file:////foo/bar'), P('//foo/bar')) self.assertEqual(P.from_uri('file://localhost/foo/bar'), P('/foo/bar')) + self.assertEqual(P.from_uri('file://localhost//foo/bar'), P('//foo/bar')) self.assertRaises(ValueError, P.from_uri, 'foo/bar') self.assertRaises(ValueError, P.from_uri, '/foo/bar') self.assertRaises(ValueError, P.from_uri, '//foo/bar') self.assertRaises(ValueError, P.from_uri, 'file:foo/bar') + self.assertRaises(ValueError, P.from_uri, 'file://foo/bar') self.assertRaises(ValueError, P.from_uri, 'http://foo/bar') @needs_posix def test_from_uri_pathname2url_posix(self): P = self.cls - self.assertEqual(P.from_uri('file:' + pathname2url('/foo/bar')), P('/foo/bar')) - self.assertEqual(P.from_uri('file:' + pathname2url('//foo/bar')), P('//foo/bar')) + self.assertEqual(P.from_uri('file://' + pathname2url('/foo/bar')), P('/foo/bar')) + self.assertEqual(P.from_uri('file://' + pathname2url('//foo/bar')), P('//foo/bar')) @needs_windows def test_absolute_windows(self): diff --git a/Misc/NEWS.d/next/Library/2024-09-03-19-36-33.gh-issue-123599.TzDIha.rst b/Misc/NEWS.d/next/Library/2024-09-03-19-36-33.gh-issue-123599.TzDIha.rst new file mode 100644 index 00000000000000..f6dd88eb661b9b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-03-19-36-33.gh-issue-123599.TzDIha.rst @@ -0,0 +1,3 @@ +Fix issue where :meth:`pathlib.Path.from_uri` accepted URIs with non-local +authorities on POSIX. This method now raises `ValueError` when given a URI +like ``file://server/share`` on a non-Windows system.