Summary
I found a confirmed rate-limit bypass on the live lookup API of bug-bounties.as93.net. Read-only testing, no data modified.
Endpoint
https://bug-bounties.as93.net/api/lookup/website — same pattern applies to /api/lookup/{github|package|forge|app}.
Root cause
The in-memory rate limiter keys every request off the client-supplied X-Forwarded-For / X-Real-IP header, with no trusted-proxy validation.
web/src/lib/lookup/api-helpers.ts:
export function getClientIp(request: Request): string {
return (
request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ||
request.headers.get("x-real-ip") ||
"unknown"
);
}
// enforceRateLimit(getClientIp(request)) → checkRateLimit(ip) [Map keyed by ip]
An attacker who sets a fresh X-Forwarded-For value per request lands in a different IP bucket every time, so the configured caps (8/min, 100/hour, 300/day — rate-limit.ts) are never reached.
Live confirmation
- Baseline (no spoof) — 9th request is blocked with
429:
400 400 400 400 400 400 400 400 429 429 429 429
- Spoofed
X-Forwarded-For (unique value per request) — all 12 pass, 0 × 429:
400 400 400 400 400 400 400 400 400 400 400 400
(The 400s are fabricated NXDOMAIN hosts — the limiter runs before domain resolution, so each still increments the counter.)
Impact
- Unbounded scraping of the full program database.
- Because every lookup fans out into several external fetches (homepage, DNS, security.txt, TLS, package registries), the endpoint also doubles as an open amplification/abuse vector against third-party looked-up targets.
Suggested fix
- Use the real peer IP (Netlify exposes
X-Nf-Client-Connection-IP; Cloudflare CF-Connecting-IP) instead of trusting the client header, and configure trusted-proxy so only the platform-appended forward header is honored.
- Drop the client-controlled
X-Real-IP fallback.
- Note: the
Map is single-instance and resets on redeploy — a shared store (e.g. Cloudflare KV) would also be more robust across edge instances.
Lower severity
web/src/pages/api/submit-anonymous.json.ts forwards with a hardcoded X-Gitgost-Key: gitgost-anonymous. I assume this is an intentional public anonymizer key; flagging in case you want the anonymous-submit path hardened.
Happy to provide a PoC script on request. Thanks for the clean codebase — the SSRF protections in resolve-domain.ts (DNS resolution + private-range blocking, including octal/hex/IPv4-mapped forms) are notably solid.
Summary
I found a confirmed rate-limit bypass on the live lookup API of bug-bounties.as93.net. Read-only testing, no data modified.
Endpoint
https://bug-bounties.as93.net/api/lookup/website— same pattern applies to/api/lookup/{github|package|forge|app}.Root cause
The in-memory rate limiter keys every request off the client-supplied
X-Forwarded-For/X-Real-IPheader, with no trusted-proxy validation.web/src/lib/lookup/api-helpers.ts:An attacker who sets a fresh
X-Forwarded-Forvalue per request lands in a different IP bucket every time, so the configured caps (8/min, 100/hour, 300/day —rate-limit.ts) are never reached.Live confirmation
429:X-Forwarded-For(unique value per request) — all 12 pass, 0 × 429:(The 400s are fabricated NXDOMAIN hosts — the limiter runs before domain resolution, so each still increments the counter.)
Impact
Suggested fix
X-Nf-Client-Connection-IP; CloudflareCF-Connecting-IP) instead of trusting the client header, and configure trusted-proxy so only the platform-appended forward header is honored.X-Real-IPfallback.Mapis single-instance and resets on redeploy — a shared store (e.g. Cloudflare KV) would also be more robust across edge instances.Lower severity
web/src/pages/api/submit-anonymous.json.tsforwards with a hardcodedX-Gitgost-Key: gitgost-anonymous. I assume this is an intentional public anonymizer key; flagging in case you want the anonymous-submit path hardened.Happy to provide a PoC script on request. Thanks for the clean codebase — the SSRF protections in
resolve-domain.ts(DNS resolution + private-range blocking, including octal/hex/IPv4-mapped forms) are notably solid.