From 5832c9d8516a671b7fe243c6c226eef2154d9fa4 Mon Sep 17 00:00:00 2001 From: yetval Date: Sun, 29 Mar 2026 18:27:49 -0400 Subject: [PATCH 1/4] fixes index.js and Masqr.js --- Masqr.js | 14 ++++++++------ index.js | 17 ++++++++++------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Masqr.js b/Masqr.js index 348d7b16a9..5893a83fce 100644 --- a/Masqr.js +++ b/Masqr.js @@ -21,14 +21,14 @@ export function setupMasqr(app) { if (req.cookies["refreshcheck"] !== "true") { res.cookie("refreshcheck", "true", { maxAge: 10000 }) - MasqFail(req, res) + await MasqFail(req, res) return } if (!authheader) { res.setHeader("WWW-Authenticate", "Basic") res.status(401) - MasqFail(req, res) + await MasqFail(req, res) return } @@ -51,10 +51,10 @@ export function setupMasqr(app) { return } - MasqFail(req, res) + await MasqFail(req, res) } catch (error) { console.error(error) - MasqFail(req, res) + await MasqFail(req, res) } }) } @@ -63,9 +63,11 @@ async function MasqFail(req, res) { if (!req.headers.host) { return } + if (!/^[a-zA-Z0-9.\-:]+$/.test(req.headers.host)) { + return + } const unsafeSuffix = req.headers.host + ".html" - const safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.(\/|\\|$))+/, "") - const safeJoin = path.join(process.cwd() + "/Masqrd", safeSuffix) + const safeJoin = path.join(process.cwd(), "Masqrd", path.basename(unsafeSuffix)) try { await fs.promises.access(safeJoin) const FailLocal = await fs.promises.readFile(safeJoin, "utf8") diff --git a/index.js b/index.js index 896be226fc..4ffe373ae2 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ import fs from "node:fs"; import http from "node:http"; import path from "node:path"; +import { fileURLToPath } from "node:url"; import { createBareServer } from "@nebula-services/bare-server-node"; import chalk from "chalk"; import cookieParser from "cookie-parser"; @@ -14,7 +15,7 @@ import config from "./config.js"; console.log(chalk.yellow("🚀 Starting server...")); -const __dirname = process.cwd(); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); const server = http.createServer(); const app = express(); const bareServer = createBareServer("/ca/"); @@ -69,15 +70,17 @@ app.get("/e/*", async (req, res, next) => { const data = Buffer.from(await asset.arrayBuffer()); const ext = path.extname(reqTarget); const no = [".unityweb"]; - const contentType = no.includes(ext) ? "application/octet-stream" : mime.getType(ext); + const contentType = no.includes(ext) ? "application/octet-stream" : (mime.getType(ext) ?? "application/octet-stream"); cache.set(req.path, { data, contentType, timestamp: Date.now() }); res.writeHead(200, { "Content-Type": contentType }); - res.end(data); + return res.end(data); } catch (error) { console.error("Error fetching asset:", error); - res.setHeader("Content-Type", "text/html"); - res.status(500).send("Error fetching the asset"); + if (!res.headersSent) { + res.setHeader("Content-Type", "text/html"); + res.status(500).send("Error fetching the asset"); + } } }); @@ -109,11 +112,11 @@ routes.forEach(route => { }); }); -app.use((req, res, next) => { +app.use((_req, res, _next) => { res.status(404).sendFile(path.join(__dirname, "static", "404.html")); }); -app.use((err, req, res, next) => { +app.use((err, _req, res, _next) => { console.error(err.stack); res.status(500).sendFile(path.join(__dirname, "static", "404.html")); }); From dfef14b5eb1f4e86d6853936e0b77bcb825cfc0f Mon Sep 17 00:00:00 2001 From: yetval Date: Sat, 4 Apr 2026 00:41:03 -0400 Subject: [PATCH 2/4] re-enable Masqr so fixes in Masqr.js actually run --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 4ffe373ae2..10a554c6e1 100644 --- a/index.js +++ b/index.js @@ -10,7 +10,7 @@ import express from "express"; import basicAuth from "express-basic-auth"; import mime from "mime"; import fetch from "node-fetch"; -// import { setupMasqr } from "./Masqr.js"; +import { setupMasqr } from "./Masqr.js"; import config from "./config.js"; console.log(chalk.yellow("🚀 Starting server...")); @@ -88,10 +88,10 @@ app.use(cookieParser()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); -/* if (process.env.MASQR === "true") { +if (process.env.MASQR === "true") { console.log(chalk.green("Masqr is enabled")); setupMasqr(app); -} */ +} app.use(express.static(path.join(__dirname, "static"))); app.use("/ca", cors({ origin: true })); From ec9d4eac8fa6ebe70d82a266623af91708772701 Mon Sep 17 00:00:00 2001 From: yetval Date: Sun, 12 Apr 2026 15:25:24 -0400 Subject: [PATCH 3/4] =?UTF-8?q?keep=20Masqr=20commented=20out=20=E2=80=94?= =?UTF-8?q?=20it=20is=20opt-in=20via=20MASQR=20env=20var?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 10a554c6e1..4ffe373ae2 100644 --- a/index.js +++ b/index.js @@ -10,7 +10,7 @@ import express from "express"; import basicAuth from "express-basic-auth"; import mime from "mime"; import fetch from "node-fetch"; -import { setupMasqr } from "./Masqr.js"; +// import { setupMasqr } from "./Masqr.js"; import config from "./config.js"; console.log(chalk.yellow("🚀 Starting server...")); @@ -88,10 +88,10 @@ app.use(cookieParser()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); -if (process.env.MASQR === "true") { +/* if (process.env.MASQR === "true") { console.log(chalk.green("Masqr is enabled")); setupMasqr(app); -} +} */ app.use(express.static(path.join(__dirname, "static"))); app.use("/ca", cors({ origin: true })); From e794e492d454c449e895d40cca86c913184de655 Mon Sep 17 00:00:00 2001 From: yetval Date: Sun, 12 Apr 2026 15:38:28 -0400 Subject: [PATCH 4/4] fix remaining audit findings: 500 error page, Failed.html placeholder, keep Masqr commented --- Failed.html | 62 +++++++++++++++++++++++++---------------------- index.js | 64 +++++++++++++++++++++++++++++++++++++------------ static/500.html | 39 ++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 44 deletions(-) create mode 100644 static/500.html diff --git a/Failed.html b/Failed.html index b2a511a512..d9830bb798 100644 --- a/Failed.html +++ b/Failed.html @@ -1,32 +1,36 @@ - - - - Welcome to nginx! - - - -

Welcome to nginx!

-

- If you see this page, the nginx web server is successfully installed and working. Further - configuration is required. -

+ + -

- For online documentation and support please refer to - nginx.org.
- Commercial support is available at - nginx.com. -

+ + + + + + + License Required + + + + + + + +
+
+

401

+

License validation failed.

+

A valid Interstellar license is required to access this instance.

+ + +
+ + + + + -

Thank you for using nginx.

- diff --git a/index.js b/index.js index 4ffe373ae2..eb4542a5b9 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,6 @@ import { fileURLToPath } from "node:url"; import { createBareServer } from "@nebula-services/bare-server-node"; import chalk from "chalk"; import cookieParser from "cookie-parser"; -import cors from "cors"; import express from "express"; import basicAuth from "express-basic-auth"; import mime from "mime"; @@ -22,16 +21,37 @@ const bareServer = createBareServer("/ca/"); const PORT = process.env.PORT || 8080; const cache = new Map(); const CACHE_TTL = 30 * 24 * 60 * 60 * 1000; // Cache for 30 Days +const CACHE_MAX_ENTRIES = Number.parseInt(process.env.CACHE_MAX_ENTRIES ?? "1000", 10); +const COOKIE_SECRET = process.env.COOKIE_SECRET; + +if (process.env.MASQR === "true" && !COOKIE_SECRET) { + throw new Error("COOKIE_SECRET must be set when MASQR=true"); +} + +if (!Number.isFinite(CACHE_MAX_ENTRIES) || CACHE_MAX_ENTRIES <= 0) { + throw new Error("CACHE_MAX_ENTRIES must be a positive integer"); +} + +app.use(cookieParser(COOKIE_SECRET)); +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); if (config.challenge !== false) { + if (Object.keys(config.users).length === 0) { + throw new Error("CHALLENGE=true requires BASIC_AUTH_USERS to be set"); + } console.log(chalk.green("🔒 Password protection is enabled! Listing logins below")); - // biome-ignore lint: idk - Object.entries(config.users).forEach(([username, password]) => { - console.log(chalk.blue(`Username: ${username}, Password: ${password}`)); + Object.keys(config.users).forEach(username => { + console.log(chalk.blue(`Username: ${username}`)); }); app.use(basicAuth({ users: config.users, challenge: true })); } +/* if (process.env.MASQR === "true") { + console.log(chalk.green("Masqr is enabled")); + setupMasqr(app); +} */ + app.get("/e/*", async (req, res, next) => { try { if (cache.has(req.path)) { @@ -39,6 +59,9 @@ app.get("/e/*", async (req, res, next) => { if (Date.now() - timestamp > CACHE_TTL) { cache.delete(req.path); } else { + // Refresh insertion order to approximate LRU behavior. + cache.delete(req.path); + cache.set(req.path, { data, contentType, timestamp }); res.writeHead(200, { "Content-Type": contentType }); return res.end(data); } @@ -72,6 +95,13 @@ app.get("/e/*", async (req, res, next) => { const no = [".unityweb"]; const contentType = no.includes(ext) ? "application/octet-stream" : (mime.getType(ext) ?? "application/octet-stream"); + while (cache.size >= CACHE_MAX_ENTRIES) { + const oldestKey = cache.keys().next().value; + if (oldestKey === undefined) { + break; + } + cache.delete(oldestKey); + } cache.set(req.path, { data, contentType, timestamp: Date.now() }); res.writeHead(200, { "Content-Type": contentType }); return res.end(data); @@ -84,17 +114,7 @@ app.get("/e/*", async (req, res, next) => { } }); -app.use(cookieParser()); -app.use(express.json()); -app.use(express.urlencoded({ extended: true })); - -/* if (process.env.MASQR === "true") { - console.log(chalk.green("Masqr is enabled")); - setupMasqr(app); -} */ - app.use(express.static(path.join(__dirname, "static"))); -app.use("/ca", cors({ origin: true })); const routes = [ { path: "/b", file: "apps.html" }, @@ -118,11 +138,25 @@ app.use((_req, res, _next) => { app.use((err, _req, res, _next) => { console.error(err.stack); - res.status(500).sendFile(path.join(__dirname, "static", "404.html")); + res.status(500).sendFile(path.join(__dirname, "static", "500.html")); }); server.on("request", (req, res) => { if (bareServer.shouldRoute(req)) { + const origin = req.headers.origin; + if (typeof origin === "string" && origin.length > 0) { + res.setHeader("Access-Control-Allow-Origin", origin); + res.setHeader("Vary", "Origin"); + } else { + res.setHeader("Access-Control-Allow-Origin", "*"); + } + res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization"); + if (req.method === "OPTIONS") { + res.statusCode = 204; + res.end(); + return; + } bareServer.routeRequest(req, res); } else { app(req, res); diff --git a/static/500.html b/static/500.html new file mode 100644 index 0000000000..e900fe0c0a --- /dev/null +++ b/static/500.html @@ -0,0 +1,39 @@ + + + + + + + + + + Home + + + + + + + +
+
+

500

+

Internal server error.

+

Something went wrong on our end. Try again in a moment.

+ + + +
+ + + + + + +