From 2941397180ca170451f12359720c43dd7c323a5b Mon Sep 17 00:00:00 2001 From: multipleof4 Date: Sun, 27 Sep 2026 11:37:51 -0700 Subject: [PATCH] Fix: Serve bad.webp for unknown query params Scanner probes like /userfiles?path=../../.env were treated as real searches since extra params were ignored, wasting searches and rate limit. Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- functions/[[path]].js | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75f353d..b8c1c74 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Literal slashes (`/`) and dots (`.`) in the URL path are **rejected** to prevent ### Things to know -- **Query parameters (`?...`)** other than `i` (and `src` on [free.direct-img.link](#free-images)) are ignored — `/orange+cat?size=large` → `orange cat` +- **Query parameters (`?...`)** other than `i` (and `src` on [free.direct-img.link](#free-images)) serve `bad.webp` — `/orange+cat?size=large` ❌ - **Fragments (`#...`)** are never sent to the server by browsers - **Double-encoded values** are decoded once — `%2520` becomes `%20` (literal), not a space - Two queries that normalize to the same string share the same cached image diff --git a/functions/[[path]].js b/functions/[[path]].js index 15209fd..6abfe6d 100644 --- a/functions/[[path]].js +++ b/functions/[[path]].js @@ -26,11 +26,13 @@ export async function onRequest(context) { if (!query) return jsonResponse(400, { error: "Empty query" }); if (query.length > 200) return jsonResponse(400, { error: "Query too long (max 200 characters)" }); - // ?i= picks which result to serve; free.direct-img.link also takes ?src= and serves only unrestricted images + // ?i= picks which result to serve; free.direct-img.link also takes ?src= and serves only unrestricted images. + // Any other param (e.g. scanner probes like ?path=../../.env) is rejected before it can cost a search. const free = url.hostname.startsWith("free."); const i = parseIndex(url.searchParams); const src = free ? parseFreeSource(url.searchParams) : null; - if (!i || (free && !src)) return asset("bad.webp"); + const extra = [...url.searchParams.keys()].some(k => k !== "i" && !(free && k === "src")); + if (!i || (free && !src) || extra) return asset("bad.webp"); context.waitUntil(countHit(env, request, query, free));