61 lines
2.0 KiB
JavaScript
61 lines
2.0 KiB
JavaScript
const path = require("node:path");
|
|
const fs = require("node:fs/promises");
|
|
const mime = {
|
|
".html": "text/html; charset=utf-8",
|
|
".js": "text/javascript; charset=utf-8",
|
|
".css": "text/css; charset=utf-8",
|
|
".json": "application/json",
|
|
".wasm": "application/wasm",
|
|
".glb": "model/gltf-binary",
|
|
".gltf": "model/gltf+json",
|
|
".png": "image/png",
|
|
".jpg": "image/jpeg",
|
|
".svg": "image/svg+xml",
|
|
};
|
|
const csp =
|
|
"default-src 'none'; script-src 'self' 'unsafe-eval'; worker-src 'self' blob:; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' data: blob:; font-src 'self' data:; media-src 'self' blob:; object-src 'none'; base-uri 'none'; frame-src 'none'";
|
|
|
|
async function readGame(root, url, method = "GET") {
|
|
if (!["GET", "HEAD"].includes(method))
|
|
return { status: 405, body: "Method not allowed" };
|
|
let u, name;
|
|
try {
|
|
u = new URL(url);
|
|
name = decodeURIComponent(u.pathname);
|
|
} catch {
|
|
return { status: 400, body: "Bad URL" };
|
|
}
|
|
if (
|
|
u.protocol !== "forma:" ||
|
|
u.hostname !== "game" ||
|
|
u.port ||
|
|
u.username ||
|
|
u.password ||
|
|
name.includes("\\") ||
|
|
name.includes("\0")
|
|
)
|
|
return { status: 403, body: "Forbidden" };
|
|
const file = path.resolve(root, "." + (name === "/" ? "/index.html" : name));
|
|
if (!file.startsWith(path.resolve(root) + path.sep))
|
|
return { status: 403, body: "Forbidden" };
|
|
try {
|
|
const real = await fs.realpath(file);
|
|
if (!real.startsWith((await fs.realpath(root)) + path.sep))
|
|
return { status: 403, body: "Forbidden" };
|
|
const bytes = await fs.readFile(real);
|
|
return {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": mime[path.extname(file)] || "application/octet-stream",
|
|
"Content-Security-Policy": csp,
|
|
"X-Content-Type-Options": "nosniff",
|
|
"Cache-Control": "no-store",
|
|
},
|
|
body: method === "HEAD" ? null : bytes,
|
|
};
|
|
} catch {
|
|
return { status: 404, body: "Not found" };
|
|
}
|
|
}
|
|
module.exports = { readGame, csp };
|