fix(previews): no genuine preview card for a site a deceptive link only pretends to be (#286) #404

Merged
jared merged 1 commits from fix/preview-deceptive-links into lotus 2026-10-18 06:15:47 -04:00
Owner

Fixes #286.

URL previews are built from the plain body, but clicks follow formatted_body's hrefs, and a sender controls both. A link whose text reads https://paypal.com but points to https://evil.example/login was caught by the click confirmation (#122), yet the message still showed a genuine PayPal preview card.

Change: new previewUrlsFor(urls, formattedBody). It runs every <a> in formatted_body through analyzeLink, and drops any body URL whose site is only claimed by a mismatched link's text. Honest links and other URLs in the same message keep their previews. It's used by all three message renderers (text, notice, emote).

Verified (Chromium, preview fixture for PayPal; Bob sends pay here <a href="https://evil.example/login">https://paypal.com</a>):

  • Before: the PayPal card shows.
  • After: no card.

Unit tests: deceptive → no preview; honest link (www.paypal.com) → preview; no HTML → unchanged; mixed message → only the deceptive one dropped.

tsc clean · unit tests pass · ESLint clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5

Fixes #286. URL previews are built from the plain `body`, but clicks follow `formatted_body`'s `href`s, and a sender controls both. A link whose text reads `https://paypal.com` but points to `https://evil.example/login` was caught by the click confirmation (#122), yet the message still showed a **genuine PayPal preview card**. **Change:** new `previewUrlsFor(urls, formattedBody)`. It runs every `<a>` in `formatted_body` through `analyzeLink`, and drops any body URL whose site is only **claimed** by a mismatched link's text. Honest links and other URLs in the same message keep their previews. It's used by all three message renderers (text, notice, emote). **Verified** (Chromium, preview fixture for PayPal; Bob sends `pay here <a href="https://evil.example/login">https://paypal.com</a>`): - **Before:** the PayPal card shows. - **After:** no card. Unit tests: deceptive → no preview; honest link (www.paypal.com) → preview; no HTML → unchanged; mixed message → only the deceptive one dropped. `tsc` clean · unit tests pass · ESLint clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
jared added 1 commit 2026-10-18 00:05:10 -04:00
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
7b3d1bad8f
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
jared merged commit 8a3b2eeb61 into lotus 2026-10-18 06:15:47 -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#404