From: Stan Ulbrych <stan@python.org>
Date: Mon, 13 Apr 2026 22:41:51 +0100
Subject: gh-148169: Fix webbrowser `%action` substitution bypass of
 dash-prefix check (GH-148170) (#148520)

(cherry picked from commit d22922c8a7958353689dc4763dd72da2dea03fff)

Origin: cpython, https://github.com/python/cpython/commit/f4654824ae0850ac87227fb270f9057477946769
---
 lib-python/3/test/test_webbrowser.py | 8 ++++++++
 lib-python/3/webbrowser.py           | 5 +++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/lib-python/3/test/test_webbrowser.py b/lib-python/3/test/test_webbrowser.py
index 522219b..ae86347 100644
--- a/lib-python/3/test/test_webbrowser.py
+++ b/lib-python/3/test/test_webbrowser.py
@@ -103,6 +103,14 @@ class ChromeCommandTest(CommandTestMixin, unittest.TestCase):
                    options=[],
                    arguments=[URL])
 
+    def test_reject_action_dash_prefixes(self):
+        browser = self.browser_class(name=CMD_NAME)
+        with self.assertRaises(ValueError):
+            browser.open('%action--incognito')
+        # new=1: action is "--new-window", so "%action" itself expands to
+        # a dash-prefixed flag even with no dash in the original URL.
+        with self.assertRaises(ValueError):
+            browser.open('%action', new=1)
 
 class MozillaCommandTest(CommandTestMixin, unittest.TestCase):
 
diff --git a/lib-python/3/webbrowser.py b/lib-python/3/webbrowser.py
index 52ad205..c24748f 100755
--- a/lib-python/3/webbrowser.py
+++ b/lib-python/3/webbrowser.py
@@ -265,7 +265,6 @@ class UnixBrowser(BaseBrowser):
 
     def open(self, url, new=0, autoraise=True):
         sys.audit("webbrowser.open", url)
-        self._check_url(url)
         if new == 0:
             action = self.remote_action
         elif new == 1:
@@ -279,7 +278,9 @@ class UnixBrowser(BaseBrowser):
             raise Error("Bad 'new' parameter to open(); " +
                         "expected 0, 1, or 2, got %s" % new)
 
-        args = [arg.replace("%s", url).replace("%action", action)
+        self._check_url(url.replace("%action", action))
+
+        args = [arg.replace("%action", action).replace("%s", url)
                 for arg in self.remote_args]
         args = [arg for arg in args if arg]
         success = self._invoke(args, True, autoraise, url)
