Skip to content

fix(preview): serve Open Graph to iMessage, Reddit and Bluesky scrapers (SIT-359) - #42

Merged
onamfc merged 1 commit into
mainfrom
brandon/sit-359-core-scraper-allowlist-stale
Sep 4, 2026
Merged

fix(preview): serve Open Graph to iMessage, Reddit and Bluesky scrapers (SIT-359)#42
onamfc merged 1 commit into
mainfrom
brandon/sit-359-core-scraper-allowlist-stale

Conversation

@onamfc

@onamfc onamfc commented Aug 25, 2026

Copy link
Copy Markdown
Member

The gap

isSocialScraper (src/routes/preview.ts) has held the same 13 user-agents since the beginning. Anything unlisted falls through to the redirect handler, so the share card renders the destination site's metadata instead of the link's.

Three real scrapers were affected:

User-agent Before
Applebot/0.1 (iMessage, Notes, Spotlight) 302 → destination
redditbot/1.0 302 → destination
Bluesky Cardyb/1.1 302 → destination

Why Bluesky is the urgent one

CardyB bakes the fetched card into the post record permanently. Unlike Facebook or LinkedIn, there is no force-re-scrape — a post that captured a wrong card keeps it forever. Its user-agent masquerades as Chrome, so Cardyb is the only usable token:

Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible;
Bluesky Cardyb/1.1; +mailto:support@bsky.app) Chrome/W.X.Y.Z Safari/537.36

What is added

Applebot, redditbot, Cardyb, Mastodon, vkShare, Embedly, Iframely, plus the common validators (opengraph, MetaInspector, PreviewBot, LinkPreview). Every entry is a token only a server-side fetcher sends.

What is deliberately NOT added

Instagram, Threads, Snapchat, Tumblr. Each of those strings appears in that app's in-app browser user-agent, so matching the bare name would serve a real person the meta-refresh interstitial instead of their redirect — worse than a missing preview card.

Instagram and Threads previews are generated by Meta's facebookexternalhit, already in the list, so adding them buys nothing and only creates the regression. A test asserts all four stay absent, with the reason attached.

WhatsApp has the same defect and predates the rule. Left alone deliberately — narrowing it risks breaking previews that currently work. Documented in a comment.

Structural change

scraperPatterns moves from a function-local array to a module-level SCRAPER_PATTERNS, exported alongside isSocialScraper so both are testable. Behaviour and signature are unchanged.

Verification

  • npm run build clean, tsc --noEmit clean.
  • npx vitest run232 tests across 15 files (was 198 across 14). The 34 new cases cover every scraper pattern, every human UA including the four in-app browsers, and the absent-pattern guard.
  • Diff is 2 files.

…rs (SIT-359)

Core's scraper allowlist had drifted behind Cloud's. It held the same 13
user-agents it has since the beginning, so self-hosted deployments still
redirect Applebot, redditbot and Bluesky's Cardyb to the destination
instead of serving them the preview page — the share card then shows the
destination site's metadata rather than the link's.

Cloud verified all three against production and fixed its own copy in
cloud#217. Cloud registers its hook ahead of these routes, so this list
governs self-hosted deployments only.

Bluesky is the one worth prioritising: CardyB bakes the fetched card into
the post record permanently, so a post that captured a wrong card can
never be re-scraped the way a Facebook or LinkedIn share can.

Adds Applebot, redditbot, Cardyb, Mastodon, vkShare, the Embedly and
Iframely aggregators, and the common preview validators. Every entry is a
token only a server-side fetcher sends.

Deliberately NOT added: Instagram, Threads, Snapchat, Tumblr. Each name
appears in that app's in-app browser user-agent, so matching it would
serve a real person the meta-refresh interstitial instead of their
redirect. Instagram and Threads previews come from facebookexternalhit,
already covered. A test pins all four as non-matches.

Lifts the list to a module-level SCRAPER_PATTERNS and exports it with
isSocialScraper so both are testable. First test coverage for this gate:
232 tests pass, tsc clean.
@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@onamfc
onamfc merged commit 0897627 into main Sep 4, 2026
13 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant