DEV Community

Cover image for `status === 200` let 7,382 non-pages into our iframes — the four checks that replaced it
Kynth
Kynth

Posted on

`status === 200` let 7,382 non-pages into our iframes — the four checks that replaced it

BlockDex indexes shadcn-style component registries, and each item page mounts an iframe of the registry's own demo page with a caption underneath: "this is the component running, not a screenshot of it."

On 2026-08-06 I fetched all 22,313 preview URLs the index had stored. 7,382 of them were not component pages.

Two mistakes stacked

The crawler learned a URL shape per registry — try {base}/docs/{name}, {origin}/components/{name}, and last on the list {origin}/r/{name} — against one sample item name. Whichever shape answered got filled in for every item in that registry and written to preview_url unchecked.

The acceptance test was one line:

const res = await get(url, { accept: 'text/html,*/*' });
if (res.status === 200 && (res.text ?? '').length > 500) { /* learned */ }
Enter fullscreen mode Exit fullscreen mode

{origin}/r/{name} is, on a large class of registries, the item JSON route — the same URL the install command reads. A 4KB JSON body clears length > 500 comfortably. The */* in the accept header forces nothing. And then the second mistake compounded it: an API route sends no X-Frame-Options and no CSP, so our framable() helper —

if (xfo.includes('deny') || xfo.includes('sameorigin')) return false;
const m = csp.match(/frame-ancestors([^;]*)/);
if (!m) return true;
Enter fullscreen mode Exit fullscreen mode

— returned true. The JSON endpoint was marked the most embeddable kind of URL there is. Chrome duly framed it and rendered its own JSON viewer inside the panel, under a caption asserting the component was running.

None of this is detectable at render time. The client watches the iframe's onLoad, and a 404 page, a 500 page and a JSON document all fire onLoad perfectly normally. The only failure a browser surfaces is a frame that never loads at all — which is the one that doesn't happen.

Blockdex search interface returning 57,672 matching component and block results in a table — Search displays components indexed by registry and library with quick-copy command buttons for reuse.

What the sweep actually found

Failure class Count of 22,313 Why the old test missed it What rejects it now
Hard 404 4,704 Never tested per item — the one sample name had a page, the other 7,794 items didn't res.status !== 200
JSON body 1,588 4KB body passed length > 500; */* accepted any content type content-type must contain text/html
Soft 404 691 200 + HTML + long body — indistinguishable by status. Next.js and Astro answer a missing route with 200 far more often than with 404 <title> announces a missing page
Server error 399 Same as hard 404: nothing per item ever ran res.status !== 200

The guard

export async function verifyPreview(url, { timeout = 15000 } = {}) {
  const no = (reason) => ({ ok: false, embeddable: false, reason });

  // `text/html` alone, no `*/*` escape hatch. A registry that answers JSON
  // to this is answering JSON to everybody.
  const res = await get(url, { accept: 'text/html', timeout });
  if (res.error) return no(`transport: ${res.error.slice(0, 60)}`);
  if (res.status !== 200) return no(`http ${res.status}`);

  const type = (res.headers?.get('content-type') ?? '').toLowerCase();
  if (!type.includes('text/html')) return no(`serves ${type.split(';')[0]}`);

  const body = res.text ?? '';
  if (body.length < 500) return no(`body is ${body.length} bytes`);

  const title = body.match(/<title[^>]*>([\s\S]{0,200}?)<\/title>/i)?.[1] ?? '';
  if (isNotFoundTitle(title)) return no(`not-found page (title: ${title.trim()})`);

  return { ok: true, embeddable: framable(res.headers) };
}
Enter fullscreen mode Exit fullscreen mode

The soft-404 check reads the title and nothing else, which was not the first version. Scanning the first 4KB of body condemned two pages that render fine — Apple gallery | 100xUI and Pixel Scroll Transition – unlumen UI — because each carried the string 404 in a nav or an inlined script. Deleting a working preview is worse than letting a broken one through, because the second is visible and the first isn't. So the regex has word boundaries and lives behind unit tests built from real titles: it must catch Component not found - HextaUI and 404: This page could not be found, and must leave Error404 Illustration | someui alone.

Two deliberate asymmetries

docs_url still takes the unverified pattern; preview_url does not. A "Docs on X" chip is a link somebody chooses to follow. The frame is a claim the page makes on their behalf, at 460px, above the fold.

And verification is capped at PREVIEW_CAP=2000 items per registry. Past the cap an item keeps its page, its install command and its access verdict — it just doesn't get a frame nothing checked. After the sweep the index publishes 12,561 verified live previews, 2,858 verified links, and 43,795 items with no preview at all, and the item page's comment block now carries those counted numbers instead of the estimates it used to.

This is how we built BlockDex, a search index for shadcn component registries: https://blockdex.kynth.studio

Top comments (0)