# Security and usability improvement for webbrowser.open() on macOS: Replace osascript with /usr/bin/open

**URL:** <https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943>\
**Category:** Ideas\
**Created:** [April 12, 2026, 10:02pm UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943 "2026-04-12T22:02:19Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![secengjeff](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/secengjeff/32/35079_2.png) [@secengjeff](https://discuss.python.org/u/secengjeff)\
**Post date:** [April 12, 2026, 10:02pm UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/1 "2026-04-12T22:02:19Z")

</div>

Hi everyone,

I’d like to discuss modernizing the macOS implementation of `webbrowser.open()` and gather feedback/support for merging [PR #146439](https://github.com/python/cpython/pull/146439).

**Current Issues**

The existing `MacOSXOSAScript` class constructs a short AppleScript and executes it via `osascript`. While this has worked reliably for many years, it introduces two clear concerns:

**User Experience**

On managed enterprise Macs (common in corporate environments using MDM/EDR tools such as CrowdStrike, SentinelOne, Jamf, or Santa), `osascript` is frequently monitored or restricted because of its history of abuse in malware campaigns. When restrictions apply, `webbrowser.open()` can fail silently or with unclear errors, creating a frustrating experience for Python developers and applications.

**Security Risk**

`osascript` is a general-purpose scripting interpreter and a classic Living-Off-the-Land binary (LOOBin). It was used on macOS in the recent Axios npm supply-chain attack (March 31, 2026), where malicious code wrote an AppleScript to a temporary file and executed it silently.

Even though the recent PATH-lookup vulnerability has been addressed in the PR, relying on `osascript` still poses issues:

- It requires constructing and executing AppleScript code, a more powerful mechanism than needed for the simple task of opening a URL. AppleScript can run shell commands, interact with other applications via Apple Events, and perform many other actions.

- Using a general scripting tool (instead of a limited, purpose-built utility) increases the overall attack surface of the standard library and makes the code more likely to be flagged by security tools.

- It keeps Python tied to a binary that security teams often treat with caution due to its real-world abuse in supply-chain attacks and infostealers.

### Proposed Solution

[PR #146439](https://github.com/python/cpython/pull/146439) introduces a new `MacOSX` class that replaces the legacy code with Apple’s purpose-built `/usr/bin/open` utility (called via absolute path and subprocess.run with an argument list):

- Uses `open -b <bundle-id>` for known browsers (e.g., com.google.Chrome), explicitly targeting the intended application.

- Safely routes non-HTTP(S) URLs through a browser to avoid unintended OS file-handler behavior.

- Eliminates AppleScript construction entirely.

- Deprecates the old `MacOSXOSAScript` class with a clear `DeprecationWarning`.

The change preserves full backward compatibility, including support for named browsers, while simplifying the code.

---

<div class="post-metadata">

**Author:** ![paulehoffman](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/paulehoffman/32/10434_2.png) [@paulehoffman](https://discuss.python.org/u/paulehoffman)\
**Post date:** [April 12, 2026, 10:56pm UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/2 "2026-04-12T22:56:11Z")

</div>

This seems like a good change, particularly because it uses Apple’s preferred method for opening “a browser”.

---

<div class="post-metadata">

**Author:** ![frantictyping](https://avatars.discourse-cdn.com/v4/letter/f/6de8d8/32.png) [@frantictyping](https://discuss.python.org/u/frantictyping)\
**Post date:** [April 17, 2026, 12:43pm UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/3 "2026-04-17T12:43:31Z")

</div>

+1 to this idea, I’ve worked in a number of security teams where Python’s use of OSAScript in this manner has been a consistent thorn.

---

<div class="post-metadata">

**Author:** ![skissane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skissane/32/35608_2.png) [@skissane](https://discuss.python.org/u/skissane)\
**Post date:** [May 15, 2026, 1:43am UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/4 "2026-05-15T01:43:05Z")

</div>

> [@secengjeff](#):
>
> Safely routes non-HTTP(S) URLs through a browser to avoid unintended OS file-handler behavior.

This breaks custom application URIs, e.g. `vscode://` or `slack://` at least with some browsers, see [issue 149454](https://github.com/python/cpython/issues/149454)

I understand the logic of wanting to force standard URIs such as `file://` through the browser, but I think doing it for custom application URIs is a bad idea. For Google Chrome, it breaks it completely; for Safari or Firefox, it still works, but with worse user experience – Python script tries to open Slack/VSCode/etc, previously it opened it directly, now it first opens your browser and displays a message saying a web page wants to open it.

If you look at how other platforms handle this – Java [`java.awt.Desktop.browse(URI)`](https://docs.oracle.com/en/java/javase/25/docs/api/java.desktop/java/awt/Desktop.html#browse(java.net.URI)) explicitly documents that custom URI schemes are sent to the OS-registered application instead of the browser. Wouldn’t it make sense for Python to adopt the same behaviour?

---

<div class="post-metadata">

**Author:** ![zware](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/zware/32/58_2.png) [@zware](https://discuss.python.org/u/zware)\
**Post date:** [May 15, 2026, 3:02am UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/5 "2026-05-15T03:02:05Z")

</div>

I think the major difference is in the name: `webbrowser.open` should always _open_ a _webbrowser_. Java’s `...Desktop.browse` still evokes a browser, but the “desktop” in the name at least hints that it might not be a web browser that gets opened.

However, I do agree that we should have a standard API for “do the system default for this URI”, but I’m not sure where it should live. `webbrowser` seems out of place regardless of the function name because the whole point is that we don’t necessarily want to open a web browser. The functionality most reminds me of `os.startfile`, which has always been a Windows-specific way to launch a _file_ the way the OS wants, but `os` doesn’t strike me as a great place either (though it may still my front-runner of the options I’ve come up with) and I’m not sure what we would call the function – `os.starturi`? 🫤 I wonder about something like `urllib.launch(uri)`, but that feels different from anything else in `urllib`, even though that package is specifically about dealing with URIs.

For some further reading, see also [Support for `file://` urls in webbrowser.open](https://discuss.python.org/t/support-for-file-urls-in-webbrowser-open/81612) and [webbrowser.open with file: URLs may launch editor instead of browser (gh-128540)](https://github.com/python/cpython/issues/128540) which have also recently changed `webbrowser` on macOS. Also tangentially related is this old proposal to [Add `shutil.open` (gh-47427)](https://github.com/python/cpython/issues/47427) as a cross-platform `os.startfile`.

---

<div class="post-metadata">

**Author:** ![skissane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skissane/32/35608_2.png) [@skissane](https://discuss.python.org/u/skissane)\
**Post date:** [May 15, 2026, 3:10am UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/6 "2026-05-15T03:10:42Z")

</div>

> [@zware](#):
>
> I think the major difference is in the name: `webbrowser.open` should always _open_ a _webbrowser_.

My viewpoint: that wasn’t actually true on macOS in Python 3.13 and earlier – and still might not be true on other platforms.

You can argue that was a bug, but the documentation wasn’t clear that it was a bug, and it wasn’t unreasonable for people to rely on the long-standing behaviour – which is now broken, albeit inconsistently.

It also isn’t clear that “always open a web browser” is a useful function for app-specific URIs. A web browser isn’t guaranteed to do anything useful with them.

If you look at other platforms, I think most of them just have an “open this URI with whatever app the OS is registered to open it with” – not two separate APIs, one which guarantees to always use a web browser (even if that’s nonsensical), the other which uses the OS-configured app. What’s the use case for splitting the API up like that? Does anything other than Python do it?

I also think, that if people are committed to breaking the pre-existing function – wouldn’t it be right to add a note to the documentation explaining the decision? i.e. “This used to work, but was removed in Python 3.14, because it was never intended to be supported”

---

<div class="post-metadata">

**Author:** ![zware](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/zware/32/58_2.png) [@zware](https://discuss.python.org/u/zware)\
**Post date:** [May 15, 2026, 3:21am UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/7 "2026-05-15T03:21:26Z")

</div>

> [@skissane](#):
>
> It also isn’t clear that “always open a web browser” is a useful function for app-specific URIs. A web browser isn’t guaranteed to do anything useful with them.

My contention is that you shouldn’t be passing app-specific URIs to `webbrowser.open` 🙂

Unfortunately, we don’t have a better option to offer at the moment. Fixing that is where I think effort would be best directed at this point.

> [@skissane](#):
>
> What’s the use case for splitting the API up like that?

Ideally, a proper “toss it to the OS and let it deal with it” API would eventually _replace_ `webbrowser`, which has always been a bit fragile by its very nature.

---

<div class="post-metadata">

**Author:** ![skissane](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/skissane/32/35608_2.png) [@skissane](https://discuss.python.org/u/skissane)\
**Post date:** [May 15, 2026, 3:25am UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/8 "2026-05-15T03:25:28Z")

</div>

> [@zware](#):
>
> My contention is that you shouldn’t be passing app-specific URIs to `webbrowser.open` 🙂

If you come from a Java background, and are familiar with java.awt.Desktop.browse – it is natural to stumble upon webbrowser.open and think “Python has the same thing just with a narrow name”. If the position is “it isn’t meant to be the same thing, custom app URIs aren’t supported, and may not work, only URI schemes the user’s default browser itself supports are officially supported” – then shouldn’t the documentation say that explicitly?

---

<div class="post-metadata">

**Author:** ![minrk](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/minrk/32/918_2.png) [@minrk](https://discuss.python.org/u/minrk)\
**Post date:** [May 15, 2026, 5:06am UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/9 "2026-05-15T05:06:17Z")

</div>

Yes, I think the documentation should be explicit that `webbrowser.open` will do its best to open a URL with a webbrowser. _Some_ implementations _sometimes_ guessed with the default url launcher, depending on your platform, but this has been improving to more consistently launch a browser, as the docs say it will.

FWIW, I think it is fairly explicit already:

> The `webbrowser` module provides a high-level interface to allow displaying web-based documents to users.
> 
> `webbrowser.open(url):` Display url using the default browser

If “launch a non-web url with a default system application” is desired, that should be a new API, and I’d argue it shouldn’t replace or deprecate `webbrowser.open`, since it serves a different purpose. It can also be way simpler. `webbrowser.open` has never served this purpose, though it would sometimes do this accidentally on some systems, depending on their configuration, state, and version. This use case is explicitly not supported in the documentation of the module and hasn’t ever worked reliably, but I’d fully support adding docs to be extra super clear that it’s not supported, if that would help.

FWIW, I’m coming from Jupyter, where we really do need “open a file:// url to an HTML file with a webbrowser” to work. Fortunately, the documentation states quite clearly that Python has exactly this functionality! I hope we don’t change that.

---

<div class="post-metadata">

**Author:** ![secengjeff](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/secengjeff/32/35079_2.png) [@secengjeff](https://discuss.python.org/u/secengjeff)\
**Post date:** [May 16, 2026, 10:08pm UTC](https://discuss.python.org/t/security-and-usability-improvement-for-webbrowser-open-on-macos-replace-osascript-with-usr-bin-open/106943/10 "2026-05-16T22:08:29Z")

</div>

I think it’s fair and helpful to include in documentation (and/or release notes) that non-web URIs (outside of `file://`) were unintentionally supported and that this may break with the `open` replacement.

If there is community consensus on improving how this feature works and/or improving the documentation, I’m happy to take lead in making that change.
