fix(previews): a page can no longer pose as a YouTube (etc.) player via its og:url (#326) #403

Merged
jared merged 1 commits from fix/embed-ogurl-provider into lotus 2026-10-18 05:54:22 -04:00
Owner

Fixes #326.

resolveEmbed fell back to the preview's og:url for any posted link. og:url comes from the linked page itself, so https://evil.example/free-gift could serve og:url = youtube.com/watch?v=…. It then rendered as a playable YouTube card of a video the attacker chose, with labels linking to evil.example.

Change: new sameProvider(url, ogUrl). The og:url fallback is used only when it's on the posted link's own provider: the same last two domain labels, with the provider's short-link domains folded in (youtu.be, youtube-nocookie.com → youtube.com; spotify.link → spotify.com; redd.it → reddit.com; x.com → twitter.com; …). vm.tiktok.com and on.soundcloud.com match on their base domain.

Verified in Chromium, with preview_url routed to a hostile page's metadata (og:url YouTube, og:site_name YouTube):

  • https://evil.example/free-gift, before: a YouTube card with a Play button. After: no embed and no Play.
  • https://youtu.be/… with the same og:url: still embeds, with Play.

Unit tests cover evil.example, a youtube.com.evil.example look-alike, youtu.be, m.youtube.com, vm.tiktok.com, spotify.link, on.soundcloud.com, redd.it, and garbage input.

Note: the plain card still shows whatever og:site_name / og:title the page claims, which is how every link preview works. Showing the real destination host on plain cards would be a separate change, alongside #286.

tsc clean · ESLint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Fixes #326. `resolveEmbed` fell back to the preview's `og:url` for any posted link. `og:url` comes from the linked page itself, so `https://evil.example/free-gift` could serve `og:url = youtube.com/watch?v=…`. It then rendered as a **playable YouTube card** of a video the attacker chose, with labels linking to evil.example. **Change:** new `sameProvider(url, ogUrl)`. The `og:url` fallback is used only when it's on the posted link's own provider: the same last two domain labels, with the provider's short-link domains folded in (`youtu.be`, `youtube-nocookie.com` → youtube.com; `spotify.link` → spotify.com; `redd.it` → reddit.com; `x.com` → twitter.com; …). `vm.tiktok.com` and `on.soundcloud.com` match on their base domain. **Verified** in Chromium, with `preview_url` routed to a hostile page's metadata (`og:url` YouTube, `og:site_name` YouTube): - `https://evil.example/free-gift`, **before:** a YouTube card with a **Play** button. **After:** no embed and no Play. - `https://youtu.be/…` with the same `og:url`: still embeds, with Play. Unit tests cover evil.example, a `youtube.com.evil.example` look-alike, youtu.be, m.youtube.com, vm.tiktok.com, spotify.link, on.soundcloud.com, redd.it, and garbage input. Note: the plain card still shows whatever `og:site_name` / `og:title` the page claims, which is how every link preview works. Showing the real destination host on plain cards would be a separate change, alongside #286. `tsc` clean · ESLint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-17 23:54:55 -04:00
fix(previews): a page can no longer pose as a YouTube (etc.) player via its og:url (#326)
CI / Build & Quality Checks (pull_request) Successful in 4m30s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 15s
CI / Playwright smoke (e2e) (pull_request) Successful in 17m17s
2c11bd3b8e
The embed fallback used the preview's og:url whatever site was posted.
og:url is set by the page, so evil.example could render as a playable
YouTube card (attacker's video) whose links go to evil.example. Only use
an og:url on the same provider as the posted link, including the
provider's own short-link domains (youtu.be, spotify.link, redd.it, …).

Fixes #326

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared merged commit 099ada5d18 into lotus 2026-10-18 05:54:22 -04:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#403