Skip to content

feat(wsl): open links in the Windows browser - #677

Merged
DevSecNinja merged 1 commit into
mainfrom
feat/wsl-browser-integration
Aug 7, 2026
Merged

feat(wsl): open links in the Windows browser#677
DevSecNinja merged 1 commit into
mainfrom
feat/wsl-browser-integration

Conversation

@DevSecNinja

Copy link
Copy Markdown
Owner

Why

A WSL distro has no graphical browser and no xdg-open. Anything that "opens a browser" — gh auth login, OAuth flows, npm docs — looks for xdg-open or $BROWSER, and when neither points anywhere useful it falls back to whatever terminal browser happens to be installed. On this machine that was lynx, which is a miserable way to complete an OAuth flow.

Fixing it per-tool (gh config set browser …) treats the symptom. Setting $BROWSER fixes every tool at once.

What

  • ~/.local/bin/wsl-browser — hands the URL to Windows via powershell.exe Start-Process, which opens it in the real default browser.
  • $BROWSER exported from conf.d/wsl-browser.fish and shell/functions/wsl-browser.sh, mirroring the existing wsl-ssh pair so bash/zsh and fish stay in step.

Guarded on WSL_DISTRO_NAME and on powershell.exe being reachable — same guards as wsl-ssh, so nothing leaks into a native Linux or macOS session, or when interop is disabled.

Two deliberate choices in the script

  • It accepts only http(s) URLs or files that exist. Start-Process is Windows' general "run this" verb, not a browser — handed calc.exe or a path to an executable, it would run it. Since $BROWSER gets invoked by other programs with whatever string they happen to have, anything else is refused rather than quietly launched.
  • The URL is passed through WSLENV, not interpolated into the PowerShell command string, so a URL containing quotes or semicolons is always data and never code. (My first draft did interpolate; powershell.exe -Command '…' "$url" appends the argument to the command text, which is exactly the footgun you'd expect.)

Why not wslu?

wslu provides wslview, which does this job properly and would have been the obvious dependency — it's what I reached for first. Two problems:

  • It was archived upstream in 2025; last release 4.1.3, April 2024.
  • It is not packaged for Debian 13 (apt-cache policy wslu → nothing; it's an Ubuntu-universe package, and this machine is Debian trixie with only main).

Installing an unmaintained .deb outside apt is worse than shipping ~30 lines we control. Documented in docs/wsl.md, with a note that wslview is a drop-in replacement if it ever returns to the archives — point BROWSER at it and delete the script.

.editorconfig

Adds a rule for home/dot_local/bin/*. Scripts there are extensionless by convention, so [*.sh] missed them and [*] would have formatted them at 2 spaces instead of the 4 every other shell script here uses.

Verification

Applied on this machine and tested:

Check Result
chezmoi apply → exec bit from executable_ prefix -rwxr-xr-x
fresh fish session BROWSER=/home/jean-paul/.local/bin/wsl-browser
bash/zsh path same ✓
guard with WSL_DISTRO_NAME unset (both shells) BROWSER unset ✓
wsl-browser calc.exe / javascript:alert(1) refused ✓
wsl-browser https://github.com/… opened on Windows, exit 0 ✓

shellcheck, shfmt, dprint and fish -n all clean.

Note

chezmoi diff on my machine also wants to revert ~/.config/git/config, removing the gh auth git-credential helper entries that gh auth setup-git wrote. That's pre-existing drift, unrelated to this PR — I applied only the three files here rather than a full chezmoi apply. Worth deciding separately whether those entries should be managed.

A WSL distro has no graphical browser and no xdg-open, so anything that
"opens a browser" — `gh auth login`, OAuth flows, `npm docs` — falls back
to whatever terminal browser happens to be installed. Completing an OAuth
flow in lynx is not a good time.

Ship `~/.local/bin/wsl-browser`, which hands the URL to Windows via
`powershell.exe Start-Process`, and export BROWSER at it under WSL. That
fixes every such tool at once rather than configuring them one by one.

Guarded on WSL_DISTRO_NAME and on powershell.exe being reachable, so it
stays inert on native Linux/macOS and when WSL interop is disabled —
matching the existing wsl-ssh guards.

Two deliberate choices in the script:

- It accepts only http(s) URLs or files that exist. Start-Process is
  Windows' general "run this" verb, not a browser: handed calc.exe or a
  path to an executable it would run it. Since $BROWSER is invoked by
  other programs, anything else is refused rather than quietly launched.
- The URL is passed through WSLENV rather than interpolated into the
  PowerShell command string, so a URL containing quotes or semicolons is
  always data and never code.

wslu's `wslview` would have been the obvious dependency, but it was
archived upstream in 2025 (last release 4.1.3, April 2024) and is not
packaged for Debian 13 — installing an unmaintained .deb outside apt is
worse than shipping these few lines. Documented, with a note that wslview
is a drop-in replacement if it ever returns.

.editorconfig gains a rule for home/dot_local/bin/*: scripts there are
extensionless by convention, so [*.sh] missed them and they would have
been formatted at 2 spaces rather than 4 like every other shell script.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@DevSecNinja
DevSecNinja force-pushed the feat/wsl-browser-integration branch from 65c53a5 to f3b8740 Compare August 7, 2026 11:57
@DevSecNinja
DevSecNinja merged commit 1ceb811 into main Aug 7, 2026
19 checks passed
@DevSecNinja
DevSecNinja deleted the feat/wsl-browser-integration branch August 7, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant