fix(previews): no genuine preview card for a site a deceptive link only pretends to be (#286)
CI / Build & Quality Checks (pull_request) Successful in 3m6s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 9s
CI / Playwright smoke (e2e) (pull_request) Successful in 21m16s
CI / Build & Quality Checks (pull_request) Successful in 3m6s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 9s
CI / Playwright smoke (e2e) (pull_request) Successful in 21m16s
Previews are built from the plain body, clicks follow formatted_body's hrefs, and a sender controls both: text "https://paypal.com" linked to evil.example showed a real PayPal card. Drop body URLs whose site is only claimed by the visible text of a mismatched link (linkSafety). Fixes #286 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
906a034e26
commit
7b3d1bad8f
@@ -221,3 +221,37 @@ export const analyzeLink = (text: string, href: string): LinkSafety | null => {
|
||||
const other = shown.find((h) => registrableDomain(h) !== realSite);
|
||||
return { mismatch: other !== undefined, shownHost: other ?? shown[0], realHost, punycode };
|
||||
};
|
||||
|
||||
const ANCHOR_REG = /<a\s[^>]*href\s*=\s*(["'])(.*?)\1[^>]*>([\s\S]*?)<\/a>/gi;
|
||||
const decodeEntities = (s: string): string =>
|
||||
s
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, "'")
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/g, '&');
|
||||
|
||||
/**
|
||||
* [Gitea #286] The URLs to build link previews for. Previews come from the
|
||||
* plain `body`, but clicks follow `formatted_body`'s hrefs, and a sender
|
||||
* controls both: text reading "https://paypal.com" linked to evil.example got
|
||||
* a genuine PayPal card. Drop any body URL whose site is only *claimed* by a
|
||||
* deceptive link's text.
|
||||
*/
|
||||
export const previewUrlsFor = (urls: string[], formattedBody: string | undefined): string[] => {
|
||||
if (!formattedBody || urls.length === 0) return urls;
|
||||
const claimed = new Set<string>();
|
||||
Array.from(formattedBody.matchAll(ANCHOR_REG)).forEach((m) => {
|
||||
const safety = analyzeLink(decodeEntities(m[3]), decodeEntities(m[2]));
|
||||
if (safety?.mismatch && safety.shownHost) claimed.add(registrableDomain(safety.shownHost));
|
||||
});
|
||||
if (claimed.size === 0) return urls;
|
||||
return urls.filter((u) => {
|
||||
try {
|
||||
return !claimed.has(registrableDomain(new URL(u).hostname));
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user