Conversation
The worker short-circuited any path ending in `.something` to the asset handler, skipping the security/CSP middlewares and the edge cache. Plugin element pages end with a fully qualified class name (`/plugins/core/flow/io.kestra.plugin.core.flow.subflow`) and release pages with a version (`/docs/changelog/v1.3.39`), so 1 947 plugin pages and 38 docs pages were served without Content-Security-Policy / X-Frame-Options and fully re-rendered on every request (TTFB 0.8–10 s cold vs 40–90 ms for the cached plugin group pages). Static assets are now recognised on an allowlist of the extensions the site actually ships; `.md` variants keep bypassing the cache as before. Also adds `ke` (emailing token) to the tracking params stripped from the cache key.
☁️ Cloudflare Worker Preview Deployed!🔗 https://ks-seo-worker-static-asset-allowl-docs.kestra-io.workers.dev 🔦 Lighthouse Benchmark
Scores (0–100, higher is better)
Core Web Vitals (lower is better)
Legend🟢 improved · 🔻 regressed · (blank) no significant change Median of repeated runs: Home x5, About Us x3, Docs Landing x5, Flow (full featured docs) x3, Plugins Landing x3, Blueprints Landing x3. A single run of these swings 20+ points between runners. View full Lighthouse HTML report for a pageFull per-page Lighthouse Results (LHR) are attached as the |
iitzIrFan
left a comment
There was a problem hiding this comment.
Structurally this fits the codebase: the predicate lives next to the other worker helpers in src/middlewares/ with a colocated vitest file, the same way contentSecurityPolicy.ts / contentSecurityPolicy.test.ts do; no new deps, and the comment density matches the rest of worker.ts. Tests pass (28) and lint/typecheck are clean on the branch. The diagnosis is right too — I reproduced it live: plugin element pages and /docs/changelog/v* come back with no content-security-policy today.
One gap before this lands, in the same class of bug the PR is fixing.
Two live, indexed element pages end with a segment that is an allowlisted extension, so they stay on the bypass path after this change:
/plugins/plugin-transform-records/io.kestra.plugin.transform.map→map/plugins/plugin-transform-records/io.kestra.plugin.transform.zip→zip
Both return 200 text/html with no CSP and no cf-cache-status, and both are in sitemap/plugins.xml. I checked the full sitemap (2 613 URLs) and the plugin API (2 169 classes, 583 packages) — these two are the only collisions today, but the shape is open-ended: any future class named Csv, Json, Xml, Pdf, Wav… lands in the same hole silently.
zip can't just be dropped from the list (public/kestra-logo-kit.zip is real). map can — there's no sourcemap setting in astro.config, and no .map files are emitted — so removing it costs nothing and fixes half the case. For the rest, a narrow guard in isStaticAssetPath keeps the allowlist honest:
// Plugin element pages end with a fully qualified class name, so their last
// segment can itself end in a real extension
// (io.kestra.plugin.transform.map, …transform.zip). Only the `.md` variant
// of those pages is an asset.
const PLUGIN_ELEMENT_SEGMENT = /(^|\/)io\.kestra\.[^/]*$/
export function isStaticAssetPath(pathname: string): boolean {
if (PLUGIN_ELEMENT_SEGMENT.test(pathname) && !pathname.endsWith(".md")) {
return false
}
// … existing extension check
}Worth two more rows in the "treats %s as a page" table so it can't regress.
Smaller notes, non-blocking:
- The
ketracking param is unrelated to the allowlist. Fine by me to keep it here given the size, but it deserves a line in the PR title/description so it's findable later. - The
STATIC_ASSET_EXTENSIONScomment says "extensions of the files this site actually ships", but roughly a third of the entries (mjs,cjs,wasm,avif,jpeg,ttf,otf,eot,webm,mp3,ogg,wav,csv) aren't shipped today. Not harmful for assets, but each one is a fresh collision surface for the page paths above — either trim to what's real or reword the comment to "extensions we're willing to treat as assets".
The worker treated any path ending in
.somethingas a static asset and skipped the security/CSP middlewares and the edge cache for it. Plugin element pages end with a fully qualified class name (/plugins/core/flow/io.kestra.plugin.core.flow.subflow) and release pages with a version (/docs/changelog/v1.3.39), so they took that shortcut too.Observed on the 16 Sept crawl and confirmed live today:
content-security-policy,x-frame-optionsand the other headers every other page gets.cf-cache-status: HITon the plugin group pages next to them. Screaming Frog flagged 510 pages above 1.5 s, all in this set.Static assets are now matched on an allowlist of the extensions the site actually ships (
src/middlewares/staticAssets.ts, unit-tested);.mdvariants keep bypassing the cache as before. Also addske(emailing token) to the tracking params stripped from the cache key.