Skip to content

Ctrl+click on terminal URLs doesn't raise the browser window on Wayland #85

@manuacl

Description

@manuacl

Symptoms

Ctrl+clicking a URL (auto-detected or OSC 8 hyperlink) in a Limux pane on Wayland/Plasma 6:

  • The URL is opened in the default browser, but the browser window is not brought to the foreground. Konsole, by contrast, raises the window correctly.
  • In some configurations (AppImage with a stale Firefox instance, or gio failing to dispatch silently), Ctrl+click appears to do nothing at all.

Root cause

open_url_in_external_browser in rust/limux-host-linux/src/pane.rs calls:

gtk::gio::AppInfo::launch_default_for_uri(url, None::<&gtk::gio::AppLaunchContext>)

Passing None for the AppLaunchContext means GIO does not generate an xdg-activation token, so under Wayland focus-stealing prevention the target window has no permission to raise itself. KIO (used by Konsole) wires this token via its own launch path.

Proposed fix

  1. Activation token. Pass Some(&display.app_launch_context()) from the default GdkDisplay, so GIO emits the xdg-activation token. Window raise works under Wayland.

  2. Fallback to xdg-open (with a small reaper thread to avoid zombie children) if gio returns Err — for AppImage / sandboxed contexts where the bundled GIO can't dispatch.

  3. Strict URI scheme allow-list. Validate the scheme against a hard-coded list (http / https / mailto), case-insensitive per RFC 3986 §3.1. Schemes outside the list are refused before any handler is invoked.

How we arrived at this allow-list

The threat model is hostile terminal output: anything that ends up in a pane's scrollback can craft an OSC 8 hyperlink, and clicking it must not lead to code execution. A cat of a malicious file, a compromised SSH session, or a colleague's repro that includes a crafted link should be safe to Ctrl+click without triggering RCE.

The starting "obvious" list was http/https/mailto/ftp/ftps/file. After reviewing the literature on URI-handler attacks we trimmed it down:

Scheme Verdict Why
http://, https:// ✅ keep Web navigation, sandboxed by the browser.
mailto: ✅ keep Email client takes care of the rest; low attack surface.
javascript:, vbscript:, data: ❌ block Classic XSS / scripting sinks. Gitea blocks these unconditionally even when other schemes are allowed (gitea#25960).
file:// ❌ drop Local file handler can execute .desktop files, scripts, binaries. A hostile cat of a crafted file is RCE (positive.security/blog/url-open-rce).
ftp://, ftps://, smb://, nfs://, dav://, davs://, sftp:// ❌ drop All auto-mount via gvfs and let an attacker execute binaries from the mounted share. Single-click RCE in the Positive Security writeup.
Custom URIs (vscode://, slack://, obsidian://, cursor://, …) ❌ drop App-specific URI handlers have a long CVE history (VS Code, Slack, Zoom, ...). We don't second-guess that surface area; users who need them can open via their own shell.
Windows-relays (ms-msdt:, search-ms:) ❌ drop Follina (CVE-2022-30190) and related. Mostly irrelevant on Linux but Wine/Proton could reroute.

We also drop syntactically-malformed inputs: bare paths, leading whitespace, missing ://, etc. RFC 3986 §3.1 case-insensitivity is honoured (HTTPS://example.com is valid and accepted).

The bar to add a scheme to the allow-list later: a clear use case, an audit of the handler's attack surface, and ideally an opt-in switch in the limux config rather than a default.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions