From b59e99151c5de0bcdf5bcbc9fc0c842052f724e6 Mon Sep 17 00:00:00 2001 From: Twink Sanderson Date: Tue, 3 Mar 2026 04:10:28 +0100 Subject: [PATCH] Add rate limiter and input validation to /norad/:id route MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NORAD route was missing the rate limiter middleware that all other routes (tle, json, csv) use. This allowed unlimited requests that could abuse Celestrak upstream. Also adds validation for NORAD catalog IDs — rejects NaN, negative, and out-of-range values (must be 1-999999) with a 400 response instead of forwarding garbage to Celestrak. --- src/routes/norad.ts | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/routes/norad.ts b/src/routes/norad.ts index 47a701e..4cd8ee5 100644 --- a/src/routes/norad.ts +++ b/src/routes/norad.ts @@ -1,11 +1,22 @@ import { Elysia } from "elysia"; +import limiter from "../utils/ratelimiter"; import tleGetter from "../utils/tleGetter"; -const noradRoute = new Elysia({ prefix: "/norad" }).get("/:id", async ({ params }) => { - const noradId = parseInt(params.id, 10); - const tleData = await tleGetter(noradId); - return new Response(tleData, { headers: { "Content-Type": "text/plain", "Cache-Control": "max-age=3600" } }); -}); +const noradRoute = new Elysia({ prefix: "/norad" }) + .use(limiter) + .get("/:id", async ({ params }) => { + const noradId = parseInt(params.id, 10); + + if (isNaN(noradId) || noradId < 1 || noradId > 999999) { + return new Response("Invalid NORAD ID. Must be a positive integer between 1 and 999999.", { + status: 400, + headers: { "Content-Type": "text/plain" }, + }); + } + + const tleData = await tleGetter(noradId); + return new Response(tleData, { headers: { "Content-Type": "text/plain", "Cache-Control": "max-age=3600" } }); + }); export default noradRoute;