Skip to content

fix: Validate external URL schemes before opening#2494

Merged
charlesvien merged 1 commit into
mainfrom
06-04-validate_external_url_schemes_before_opening
Jun 5, 2026
Merged

fix: Validate external URL schemes before opening#2494
charlesvien merged 1 commit into
mainfrom
06-04-validate_external_url_schemes_before_opening

Conversation

@charlesvien
Copy link
Copy Markdown
Member

@charlesvien charlesvien commented Jun 5, 2026

Problem

URLs were passed straight to the main openExternal handler with no scheme check, so a tampered value could launch file://, smb://, or custom app-handler schemes via shell.openExternal.

Changes

  1. Add isSafeExternalUrl (http/https/mailto allowlist) to @posthog/shared
  2. Reject disallowed schemes in the os.openExternal tRPC input
  3. Short-circuit openUrlInBrowser on unsafe URLs so the window.open fallback can't bypass the guard
  4. Add unit tests covering allowed and blocked schemes

How did you test this?

Manually

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@charlesvien charlesvien changed the title Validate external URL schemes before opening fix: Validate external URL schemes before opening Jun 5, 2026
@charlesvien charlesvien marked this pull request as ready for review June 5, 2026 00:14
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 5, 2026

T-Rex T-Rex Logs

What T-Rex did

  • Ran a focused 24-case URL safety test against the new safe external URL helper, covering uppercase and mixed-case schemes, malformed URLs, scheme-relative URLs, whitespace and control-character bypass attempts, unsafe schemes, and mailto query/header cases; confirmed unsafe URLs return before the tRPC call and the window.open fallback, and that the os.openExternal mutation has server-side input validation.
  • Ran a 24-case Node.js test script exercising isSafeExternalUrl across bug classes; all 24 cases passed with exit code 0, and reviewed browser.ts guard logic and os.ts input validation to confirm defense-in-depth; notes indicate tests ran under Node.js due to ViTest incompatibilities.
Artifacts

24-case URL safety test output

  • Evidence of the test results for the 24-case URL safety validation, used to inspect allowed and blocked URLs.

Generated URL validation edge-case test

  • Source TypeScript test definitions used to generate the edge-case scenarios for the URL safety checks.

24-case URL safety test output: all pass

  • Automated runtime results showing all 24 cases passed; validates isSafeExternalUrl behavior.

URL safety test script (TypeScript)

  • Source TypeScript test script used to exercise isSafeExternalUrl across edge cases.

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "Validate external URL schemes before ope..." | Re-trigger Greptile

@charlesvien charlesvien enabled auto-merge (squash) June 5, 2026 03:33
@DanielVisca DanielVisca self-requested a review June 5, 2026 04:05
Copy link
Copy Markdown

@DanielVisca DanielVisca left a comment

Choose a reason for hiding this comment

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

🔥

@charlesvien charlesvien merged commit 5f1f254 into main Jun 5, 2026
19 checks passed
@charlesvien charlesvien deleted the 06-04-validate_external_url_schemes_before_opening branch June 5, 2026 04:05
Gilbert09 added a commit that referenced this pull request Jun 5, 2026
Port the desktop hardening from #2494 to the mobile app: before handing a
URL to `Linking.openURL` / `WebBrowser.openBrowserAsync`, validate its scheme
against the shared allowlist (`http:`/`https:`/`mailto:`) so tampered or
attacker-supplied URLs from markdown, chat, signal reports, or MCP app content
can't trigger unsafe schemes (`file:`, `data:`, `javascript:`, custom
deep-links, etc.).

- Add `openExternalUrl(url)` helper in `apps/mobile/src/lib` that gates
  `Linking.openURL` behind `@posthog/shared`'s `isSafeExternalUrl` and keeps
  the existing silent no-op on failure.
- Route the untrusted Linking call sites through it: MarkdownText, MarkdownImage,
  GithubRefChip, PostHogRefChip, SignalCard, SuggestedReviewers, PrStatusBadge,
  and the MCP template docs link.
- Gate the MCP app bridge `WebBrowser.openBrowserAsync` behind `isSafeExternalUrl`.
- Add unit tests covering the allow/deny matrix and that a rejected URL never
  reaches `Linking.openURL`. The test also exercises `isSafeExternalUrl` inside
  the mobile package to confirm it resolves and runs there. RN 0.81's `URL`
  extracts schemes via regex in its `protocol` getter, so no polyfill fallback
  is needed.

Generated-By: PostHog Code
Task-Id: 4fe18724-3034-4b84-8924-5b52a4b933fe
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.

2 participants