Skip to content

Open external links from HTML Apps in the user's browser - #274

Open
marcodlk wants to merge 2 commits into
bholmesdev:mainfrom
marcodlk:feat/html-app-links-open
Open

Open external links from HTML Apps in the user's browser#274
marcodlk wants to merge 2 commits into
bholmesdev:mainfrom
marcodlk:feat/html-app-links-open

Conversation

@marcodlk

@marcodlk marcodlk commented Aug 19, 2026

Copy link
Copy Markdown

Description

HTML Apps run in a sandboxed iframe, so anchors and window.open() can't reach external sites. This PR routes external links to the user's default browser while leaving the sandbox untouched, per the discussion in #262.

  • hubble.links.open(url) / safeOpen(url) on the runtime broker, matching the files.* style.
  • Plain <a> links work without the API: the runtime intercepts clicks on anchors (including nested elements) whose resolved href is absolute http(s) and routes them through links.open. Relative and in-page links are untouched, and an app's own click handler can preventDefault to opt out.

Sandboxing is unchanged — same sandbox attribute, CSP, will-frame-navigate guard, and deny-all window-open handler; no new IPC. URLs are validated twice: a renderer schema rejects anything that isn't http(s), and the existing desktop:open-external-url guard rejects again before shell.openExternal. links.open skips the workspace guard because it doesn't touch workspace files; all files.* methods still hit it first.

Considered and rejected: opening URLs from blocked will-frame-navigate events in main — that fires on script-driven redirects with no user gesture and modifies the guard this PR aims to preserve.

ADR-0007 documents both behaviors.

Closes #262

Type of Change

  • New feature (non-breaking change which adds functionality)

Testing

  • Existing tests pass
  • Added new tests for changes
  • Tested manually (describe below)

New tests:

  • IframeView.test.ts — broker validation: http(s) resolves { ok: true } and reaches openExternalUrl; file:, javascript:, scheme-less, and non-string inputs return { ok: false } without reaching the desktop API.
  • htmlAppRuntime.test.ts — runs the real packages/runtime/global.js in happy-dom at a hubble-asset:// document URL: external and nested-element clicks route through links.open, relative and in-page links are untouched, preventDefault opts out.

pnpm --filter @hubble.md/desktop test (221 tests), pnpm build:desktop, and the React Compiler audit pass.

Manual Testing Details:

Drove a real sandboxed iframe over CDP: external and nested-element clicks open the system browser with the default prevented; #section links stay in-page; hubble.links.safeOpen("file:///etc/passwd") returns { ok: false, error } without reaching the desktop API.

Checklist

  • I discussed this change in a GitHub issue before submitting this PR
  • I have run the linter, formatter, and tests to ensure my code is ready for review

HTML Apps render in a sandboxed iframe without allow-popups or
allow-top-navigation, so window.open and target="_blank" are no-ops
and apps have no way to send the user to an external URL.

Add a links.open method to the runtime broker: the injected runtime
exposes hubble.links.open(url) / hubble.links.safeOpen(url) in the
same style as files.*, and the renderer dispatch validates the URL
(http/https only, mirroring the main-process guard) before forwarding
to the existing desktop:open-external-url IPC handler. No new IPC
surface is added.

Closes bholmesdev#262
@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown

@marcodlk is attempting to deploy a commit to the bholmesdev's projects Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Routes HTML App external links through the existing validated desktop:open-external-url path: hubble.links.open / safeOpen, plus bubble-phase intercept of absolute http(s) anchor clicks. Sandbox, CSP, and navigation guards stay unchanged; renderer + main both reject non-http(s).

Concerns

None material. Workspace guard correctly skipped for links.open. Runtime uses resolved anchor.href, bubble + defaultPrevented opt-out, and relative/in-page links stay on hubble-asset://. Tests exercise broker validation and real global.js click behavior.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve


Reviewed by a Warp Factory agent.

The runtime routes anchor clicks on absolute http(s) links through the
same links.open broker request the hubble.links API uses, so authored
HTML works without calling the API. Relative and in-page links resolve
to hubble-asset:// and are untouched; app click handlers can call
preventDefault to opt out.
@marcodlk
marcodlk force-pushed the feat/html-app-links-open branch from 79bff05 to 1b2937f Compare August 19, 2026 23:24

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overview

Routes HTML App external links through the existing validated desktop:open-external-url path: hubble.links.open / safeOpen, plus bubble-phase intercept of absolute http(s) anchor clicks. Sandbox, CSP, and navigation guards stay unchanged; renderer + main both reject non-http(s).

Prior review approved. Delta adds real global.js click tests, ADR plain-anchor docs, changelog issue/link wording, and minor test cleanups — no regressions.

Concerns

None material. Workspace guard correctly skipped for links.open. Runtime uses resolved anchor.href, bubble + defaultPrevented opt-out, and relative/in-page links stay on hubble-asset://. Tests cover broker validation and runtime click behavior.

Verdict

Found: 0 critical, 0 important, 0 suggestions

Approve


Reviewed by a Warp Factory agent.

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.

HTML Apps have no way to open external links in the browser

1 participant