From 07fb985a3ad8be120ee0922701d9d6731e9fe6c1 Mon Sep 17 00:00:00 2001 From: yourfriendoss Date: Fri, 31 Oct 2025 19:55:07 +0200 Subject: [PATCH] tight --- src/server.ts | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/server.ts b/src/server.ts index 3d7490c..240a38b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,19 +8,53 @@ import { readFileSync } from "node:fs"; import { minify } from "uglify-js"; const app = express(); +const allowedSites = new Set( + (process.env.ALLOWED_SITES || "") + .split(",") + .map(s => s.trim().toLowerCase()) + .filter(Boolean) +); + const clientJS = minify(readFileSync("src/clientAnalytics.js", "utf8")).code; app.use(bodyParser.json({ limit: "512kb" })); app.use( - cors() + cors({ + origin: (origin, callback) => { + if (!origin) return callback(null, true); + try { + const host = new URL(origin).hostname.toLowerCase(); + if (allowedSites.size === 0 || allowedSites.has(host)) { + return callback(null, true); + } + return callback(new Error("Not allowed by CORS")); + } catch { + return callback(new Error("Invalid Origin")); + } + }, + methods: ["GET", "POST", "OPTIONS"], + allowedHeaders: ["Content-Type"], + }) ); if (process.env.TRUST_PROXY) { app.set("trust proxy", true); } app.options("*path", (req, res) => { - res.set("Access-Control-Allow-Origin", "*"); + const origin = req.get("Origin") || ""; + let ok = true; + if (allowedSites.size > 0) { + try { + const host = new URL(origin).hostname.toLowerCase(); + ok = allowedSites.has(host); + } catch { + ok = false; + } + } + if (!ok) return res.sendStatus(403); + res.set("Vary", "Origin"); + res.set("Access-Control-Allow-Origin", origin || "*"); res.set("Access-Control-Allow-Methods", "GET,POST,OPTIONS"); res.set("Access-Control-Allow-Headers", "Content-Type"); res.sendStatus(204); @@ -108,6 +142,10 @@ app.post("/some-cool-endpoint", (req, res) => { } catch (err) { } } + site = (site || "").toLowerCase(); + if (allowedSites.size > 0 && !allowedSites.has(site)) { + return res.sendStatus(403); + } eventCounter.labels(site, e.type, country, device, browser, os).inc(); @@ -136,9 +174,14 @@ app.get("/js", async (req, res) => { const partial_url = req.protocol + "://" + req.get("host"); const url = new URL(partial_url + req.url); - const id = url.searchParams.get("site") || url.hostname; + const id = (url.searchParams.get("site") || url.hostname || "").toLowerCase(); + if (allowedSites.size > 0 && !allowedSites.has(id)) { + res.status(403).send("site not allowed"); + return; + } res.set('Content-Type', 'text/javascript') + res.set('Vary', 'Origin') res.end(clientJS.replace( "some_misc_string", partial_url ).replace("some_site_string", id)