|
| 1 | +From 64423d38b3b27b8f6cb479c554138b6a4d94830a Mon Sep 17 00:00:00 2001 |
| 2 | +From: Seth Michael Larson <seth@python.org> |
| 3 | +Date: Fri, 20 Mar 2026 09:47:13 -0500 |
| 4 | +Subject: [PATCH 1/2] gh-143930: Reject leading dashes in webbrowser URLs |
| 5 | + (cherry picked from commit 82a24a4442312bdcfc4c799885e8b3e00990f02b) |
| 6 | + |
| 7 | +--- |
| 8 | + Lib/test/test_webbrowser.py | 5 +++++ |
| 9 | + Lib/webbrowser.py | 12 ++++++++++++ |
| 10 | + .../2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst | 1 + |
| 11 | + 3 files changed, 18 insertions(+) |
| 12 | + create mode 100644 Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst |
| 13 | + |
| 14 | +diff --git a/Lib/test/test_webbrowser.py b/Lib/test/test_webbrowser.py |
| 15 | +index 2d695bc..60f094f 100644 |
| 16 | +--- a/Lib/test/test_webbrowser.py |
| 17 | ++++ b/Lib/test/test_webbrowser.py |
| 18 | +@@ -59,6 +59,11 @@ class GenericBrowserCommandTest(CommandTestMixin, unittest.TestCase): |
| 19 | + options=[], |
| 20 | + arguments=[URL]) |
| 21 | + |
| 22 | ++ def test_reject_dash_prefixes(self): |
| 23 | ++ browser = self.browser_class(name=CMD_NAME) |
| 24 | ++ with self.assertRaises(ValueError): |
| 25 | ++ browser.open(f"--key=val {URL}") |
| 26 | ++ |
| 27 | + |
| 28 | + class BackgroundBrowserCommandTest(CommandTestMixin, unittest.TestCase): |
| 29 | + |
| 30 | +diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py |
| 31 | +index 13b9e85..ab66519 100755 |
| 32 | +--- a/Lib/webbrowser.py |
| 33 | ++++ b/Lib/webbrowser.py |
| 34 | +@@ -158,6 +158,12 @@ class BaseBrowser(object): |
| 35 | + def open_new_tab(self, url): |
| 36 | + return self.open(url, 2) |
| 37 | + |
| 38 | ++ @staticmethod |
| 39 | ++ def _check_url(url): |
| 40 | ++ """Ensures that the URL is safe to pass to subprocesses as a parameter""" |
| 41 | ++ if url and url.lstrip().startswith("-"): |
| 42 | ++ raise ValueError(f"Invalid URL {url!r}: URLs must not start with '-' after leading whitespace") |
| 43 | ++ |
| 44 | + |
| 45 | + class GenericBrowser(BaseBrowser): |
| 46 | + """Class for all browsers started with a command |
| 47 | +@@ -175,6 +181,7 @@ class GenericBrowser(BaseBrowser): |
| 48 | + |
| 49 | + def open(self, url, new=0, autoraise=True): |
| 50 | + sys.audit("webbrowser.open", url) |
| 51 | ++ self._check_url(url) |
| 52 | + cmdline = [self.name] + [arg.replace("%s", url) |
| 53 | + for arg in self.args] |
| 54 | + try: |
| 55 | +@@ -195,6 +202,7 @@ class BackgroundBrowser(GenericBrowser): |
| 56 | + cmdline = [self.name] + [arg.replace("%s", url) |
| 57 | + for arg in self.args] |
| 58 | + sys.audit("webbrowser.open", url) |
| 59 | ++ self._check_url(url) |
| 60 | + try: |
| 61 | + if sys.platform[:3] == 'win': |
| 62 | + p = subprocess.Popen(cmdline) |
| 63 | +@@ -260,6 +268,7 @@ class UnixBrowser(BaseBrowser): |
| 64 | + |
| 65 | + def open(self, url, new=0, autoraise=True): |
| 66 | + sys.audit("webbrowser.open", url) |
| 67 | ++ self._check_url(url) |
| 68 | + if new == 0: |
| 69 | + action = self.remote_action |
| 70 | + elif new == 1: |
| 71 | +@@ -350,6 +359,7 @@ class Konqueror(BaseBrowser): |
| 72 | + |
| 73 | + def open(self, url, new=0, autoraise=True): |
| 74 | + sys.audit("webbrowser.open", url) |
| 75 | ++ self._check_url(url) |
| 76 | + # XXX Currently I know no way to prevent KFM from opening a new win. |
| 77 | + if new == 2: |
| 78 | + action = "newTab" |
| 79 | +@@ -554,6 +564,7 @@ if sys.platform[:3] == "win": |
| 80 | + class WindowsDefault(BaseBrowser): |
| 81 | + def open(self, url, new=0, autoraise=True): |
| 82 | + sys.audit("webbrowser.open", url) |
| 83 | ++ self._check_url(url) |
| 84 | + try: |
| 85 | + os.startfile(url) |
| 86 | + except OSError: |
| 87 | +@@ -638,6 +649,7 @@ if sys.platform == 'darwin': |
| 88 | + |
| 89 | + def open(self, url, new=0, autoraise=True): |
| 90 | + sys.audit("webbrowser.open", url) |
| 91 | ++ self._check_url(url) |
| 92 | + if self.name == 'default': |
| 93 | + script = 'open location "%s"' % url.replace('"', '%22') # opens in default browser |
| 94 | + else: |
| 95 | +diff --git a/Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst b/Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst |
| 96 | +new file mode 100644 |
| 97 | +index 0000000..0f27eae |
| 98 | +--- /dev/null |
| 99 | ++++ b/Misc/NEWS.d/next/Security/2026-01-16-12-04-49.gh-issue-143930.zYC5x3.rst |
| 100 | +@@ -0,0 +1 @@ |
| 101 | ++Reject leading dashes in URLs passed to :func:`webbrowser.open` |
| 102 | +-- |
| 103 | +2.45.4 |
| 104 | + |
| 105 | + |
| 106 | +From 83c608b5ece9ad2aa88866dee9b52f8895156671 Mon Sep 17 00:00:00 2001 |
| 107 | +From: Pinky <pinky00ch@gmail.com> |
| 108 | +Date: Wed, 25 Mar 2026 00:01:36 +0530 |
| 109 | +Subject: [PATCH 2/2] Simplify error message for invalid URL- backport |
| 110 | + |
| 111 | +Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> |
| 112 | +Upstream-reference: https://github.com/python/cpython/pull/146360.patch |
| 113 | +--- |
| 114 | + Lib/webbrowser.py | 2 +- |
| 115 | + 1 file changed, 1 insertion(+), 1 deletion(-) |
| 116 | + |
| 117 | +diff --git a/Lib/webbrowser.py b/Lib/webbrowser.py |
| 118 | +index ab66519..0bdb644 100755 |
| 119 | +--- a/Lib/webbrowser.py |
| 120 | ++++ b/Lib/webbrowser.py |
| 121 | +@@ -162,7 +162,7 @@ class BaseBrowser(object): |
| 122 | + def _check_url(url): |
| 123 | + """Ensures that the URL is safe to pass to subprocesses as a parameter""" |
| 124 | + if url and url.lstrip().startswith("-"): |
| 125 | +- raise ValueError(f"Invalid URL {url!r}: URLs must not start with '-' after leading whitespace") |
| 126 | ++ raise ValueError(f"Invalid URL: {url}") |
| 127 | + |
| 128 | + |
| 129 | + class GenericBrowser(BaseBrowser): |
| 130 | +-- |
| 131 | +2.45.4 |
| 132 | + |
0 commit comments