From a819ea37c3562d70618bc340a5b9d4cad87b658d Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Wed, 30 Sep 2026 14:48:40 +0200 Subject: [PATCH] the two blob proxies are ungated on purpose, and nothing said so An audit found that /api/aggregate and /api/sitemap-data answer 200 on production carrying every dev-only bench, bench 208 included, with its full editorial fields and an seo_title naming a leader for a page that 404s. Eleven gated slugs in the aggregate, nine in the sitemap blob. The obvious fix is wrong and would cause an outage. Both libraries point every Vercel deployment at these routes ON THE PRODUCTION DOMAIN (aggregate-blob.ts:66, sitemap-blob.ts:4), because the alternative is a 12 s transatlantic fetch of a 7.5 MB blob from the Paris VPS. Staging is the only place the dev-only benches render, and it reads its data through production's proxy, so filtering on VERCEL_ENV in either route would strip all nine benches from staging. Verified: chain-bridged-tvl and perp-liq-rate both answer 200 on openchainbench-staging.vercel.app today. So the routes stay verbatim and now say why, at the top of each, addressed to whoever finds this next. The gate that matters is the loader (materialize/load.ts) and sitemap-builder.ts, and production's own surfaces are correct: sitemap.xml, llms.txt, RSS, /api/citable, /api/stat, MCP, the Markdown views and every page all drop bench 208, each checked live. What bounds the exposure, recorded with the warning: both routes are noindex via the /api/:path* X-Robots-Tag, neither is linked from any page, neither appears in llms.txt or the OpenAPI document, and the payload holds no secret. Scanned for keyed URL shapes (quiknode, alchemy, chainstack, getblock, api-key, token): none. While in that file, the leading docblock was wrong in three more ways. It described the three sets as one, so it gave 410 as the answer for a dev-only slug when the middleware only handles REMOVED_BENCH_SLUGS and a dev-only page 404s. It credited src/app/sitemap.ts, which does not exist since the sitemap became a Route Handler. And its list of protected surfaces was the list that omitted these two routes. It is now a map of which set does what, with the status code each produces and why a 404 is right for a URL that never published. Documentation only. typecheck, lint and the suite (368 tests) clean. --- src/app/api/aggregate/route.ts | 20 ++++++++++++ src/app/api/sitemap-data/route.ts | 11 +++++++ src/lib/removed-benches.ts | 52 +++++++++++++++++++++++-------- 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/app/api/aggregate/route.ts b/src/app/api/aggregate/route.ts index 6500c8b28..24c873439 100644 --- a/src/app/api/aggregate/route.ts +++ b/src/app/api/aggregate/route.ts @@ -10,6 +10,26 @@ // aggregate-blob.ts can point AGGREGATE_BLOB_URL at this route instead // of kv.openchainbench.com to get near-zero latency from any Vercel // function in the same region. +// +// UNFILTERED BY DESIGN. This route serves the worker's blob verbatim, so on +// production it carries every dev-only bench (the DEV_ONLY_BENCH_SLUGS set in +// src/lib/removed-benches.ts) with its full editorial fields, including a +// seo_title that names a leader for a bench whose page 404s. An SEO audit on +// 2026-09-30 flagged that as a leak. Do not "fix" it by filtering on +// VERCEL_ENV here. +// +// The reason is aggregate-blob.ts:66: on ANY Vercel deployment, staging and +// previews included, the site fetches this route on the production domain. +// Staging is the only place the dev-only benches render, so filtering them out +// here would empty the staging pipeline of all nine of them. The gate belongs +// where it is, at the loader (src/lib/materialize/load.ts), which drops them +// per deployment; this route is transport underneath that gate, not a public +// surface. +// +// What bounds the exposure instead: the route is noindex (the /api/:path* +// X-Robots-Tag in next.config.ts), it is linked from no page and listed in +// neither llms.txt nor the OpenAPI document, and it carries no secret. Checked +// 2026-09-30: no keyed URL shape appears anywhere in the payload. export const runtime = "nodejs"; // force-dynamic + explicit Cache-Control: CDN caches via s-maxage without // generating ISR write entries. revalidateTag("bench-aggregate") from the diff --git a/src/app/api/sitemap-data/route.ts b/src/app/api/sitemap-data/route.ts index 04a6a6553..45964eb3f 100644 --- a/src/app/api/sitemap-data/route.ts +++ b/src/app/api/sitemap-data/route.ts @@ -1,3 +1,14 @@ +// Vercel-edge proxy for the worker's slim sitemap blob, the same shape of +// transport as /api/aggregate. +// +// UNFILTERED BY DESIGN, for the same reason and with the same warning: it +// serves the blob verbatim, so on production it lists every dev-only bench. +// sitemap-blob.ts:4 points every Vercel deployment at this route on the +// production domain, staging included, and staging is where those benches +// render. Filtering on VERCEL_ENV here would strip them from the staging +// sitemap. The gate that matters is in src/lib/sitemap-builder.ts, which drops +// them per deployment; sitemap.xml on production is correct and carries none +// of them. export const runtime = "nodejs"; export const revalidate = 3600; diff --git a/src/lib/removed-benches.ts b/src/lib/removed-benches.ts index feaad9193..ae6b70d44 100644 --- a/src/lib/removed-benches.ts +++ b/src/lib/removed-benches.ts @@ -1,19 +1,45 @@ /** - * Bench slugs that must not exist on production. They render normally - * on dev / staging / preview (VERCEL_ENV !== "production") and are the - * staging pipeline: benches still being validated, or held back for a - * partnership announcement. + * Which bench slugs production does not serve, and what a hit on one gets. * - * Enforced in three places, all driven by this single set: - * 1. src/middleware.ts returns 410 Gone for direct URL hits on prod - * (SEO-correct signal for previously indexed URLs). - * 2. src/lib/materialize/load.ts drops the specs at loader level on - * prod, so the catalog index, category pages, /rpc hub, compare - * pairs, /api/citable, llms.txt and RSS never link or cite them. - * 3. src/app/sitemap.ts excludes the routes on prod. + * This file holds three sets, and they are NOT interchangeable. Each has its + * own docblock below; this is the map, because an earlier version of this + * comment described all of them as one and got the status code wrong. * - * Moving a bench to production = remove its slug here, bump the - * bench-set cache keys in src/lib/spec.ts, ship dev to main. + * REMOVED_BENCH_SLUGS retired for good, plus a few held for review. + * src/middleware.ts answers 410 Gone on prod, which is + * the SEO-correct signal for a URL that was indexed. + * RENAMED_BENCH_SLUGS split or renamed; a 308 to the successor, issued from + * next.config.ts redirects() so it costs no invocation. + * Checked before the 410 so a rename always wins. + * DEV_ONLY_BENCH_SLUGS the staging pipeline: still being validated, or held + * back on purpose. Dropped at the loader, so the page + * 404s rather than 410s. Nothing was ever published at + * those URLs, so there is no signal to preserve. + * + * The loader gate (src/lib/materialize/load.ts) is what keeps every rendered + * surface consistent: catalog index, category pages, /rpc hub, compare pairs, + * /api/citable, /api/stat, llms.txt, RSS, MCP and the Markdown views never + * link or cite a slug this deployment does not serve. The sitemap is dropped + * separately in src/lib/sitemap-builder.ts, through the isDevOnlyBench and + * isDevOnlyRoute helpers below rather than the sets directly, and is served by + * src/app/sitemap.xml/route.ts (there is no src/app/sitemap.ts; it became a + * Route Handler so it could set its own Cache-Control). + * + * Moving a bench to production = remove its slug from DEV_ONLY_BENCH_SLUGS, + * bump the bench-set cache keys in src/lib/spec.ts, ship dev to main. + * + * TWO PRODUCTION ENDPOINTS DELIBERATELY DO NOT ENFORCE ANY OF THIS, and an + * audit will find them again: /api/aggregate and /api/sitemap-data serve the + * worker's blobs verbatim, so on production both carry every dev-only slug + * with its editorial fields, including an seo_title naming a leader for a + * bench whose page 404s. Flagged 2026-09-30. They are transport, not a public + * surface: every Vercel deployment including staging fetches those two routes + * on the production domain (src/lib/aggregate-blob.ts, src/lib/sitemap-blob.ts), + * and staging is the only place these benches render, so filtering them there + * would empty the staging pipeline of all of them. Both routes carry that + * warning at the top. What bounds the exposure: noindex via the /api/:path* + * X-Robots-Tag, linked from no page, absent from llms.txt and the OpenAPI + * document, and no secret in the payload (no keyed URL shape, checked). */ /** * Bench slugs renamed / split, mapped to their canonical successor.