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

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
This commit is contained in:
Lotus CI
2026-10-17 23:54:50 -04:00
co-authored by Claude Opus 5.5
parent 906a034e26
commit 2c11bd3b8e
3 changed files with 63 additions and 1 deletions
@@ -54,6 +54,7 @@ import {
GIF_PREVIEW_MAX_WIDTH,
gifPreviewBox,
} from '../../utils/gifPreviewSize';
import { sameProvider } from './embedProvider';
const linkStyles = { color: color.Success.Main };
@@ -2497,7 +2498,10 @@ export const UrlPreviewCard = as<'div', { url: string; ts: number }>(
const resolveEmbed = (prev: IPreviewUrlResponse): MediaEmbed | null => {
if (embed) return embed;
const ogUrl = prev['og:url'];
return typeof ogUrl === 'string' && ogUrl !== url
// [Gitea #326] …but only from an og:url on the posted link's own
// provider: the page sets og:url, so any site could otherwise show up
// as a playable YouTube (etc.) card that links to itself.
return typeof ogUrl === 'string' && ogUrl !== url && sameProvider(url, ogUrl)
? parseMediaEmbed(ogUrl, window.location.hostname)
: null;
};