Skip to content

Commit 87fbaeb

Browse files
committed
Initial Commit
0 parents  commit 87fbaeb

File tree

5 files changed

+199
-0
lines changed

5 files changed

+199
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules/
2+
dist/

package.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "masqr-express",
3+
"version": "1.0.0",
4+
"main": "index.js",
5+
"scripts": {
6+
"build": "tsc"
7+
},
8+
"keywords": [],
9+
"author": "",
10+
"license": "ISC",
11+
"description": "",
12+
"dependencies": {
13+
"@types/express": "^4.17.21",
14+
"@types/node": "^20.14.12"
15+
}
16+
}

pnpm-lock.yaml

+104
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/masqr.ts

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import path from "path";
2+
import fs from "fs";
3+
import type { Request, Response, NextFunction } from "express";
4+
5+
type MasqrConfig = {
6+
licenseServer: string;
7+
whitelist: string[];
8+
}
9+
10+
export async function masqrCheck(config: MasqrConfig, htmlFile: string): Promise<Function> {
11+
let loadedHTMLFile = fs.readFileSync(htmlFile, "utf8");
12+
return async (req: Request, res: Response, next: NextFunction) => {
13+
if (req.headers.host && config.whitelist.includes(req.headers.host)) {
14+
next();
15+
return;
16+
}
17+
const authheader = req.headers.authorization;
18+
if (req.cookies["authcheck"]) {
19+
next();
20+
return;
21+
}
22+
if (!authheader) {
23+
res.setHeader("WWW-Authenticate", "Basic");
24+
res.status(401);
25+
MasqFail(req, res, loadedHTMLFile);
26+
return;
27+
}
28+
// If we are at this point, then the request should be a valid masqr request, and we are going to check the license server
29+
const auth = Buffer.from(authheader.split(" ")[1], "base64").toString().split(":");
30+
const pass = auth[1];
31+
32+
const licenseCheck = (await (await fetch(config.licenseServer + pass + "&host=" + req.headers.host)).json())["status"];
33+
34+
if (licenseCheck === "License valid") {
35+
// Authenticated, set cookie for a year
36+
res.cookie("authcheck", "true", {
37+
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000),
38+
});
39+
res.send(`<script>window.location.href = window.location.href</script>`); // fun hack to make the browser refresh and remove the auth params from the URL
40+
return;
41+
}
42+
}
43+
}
44+
45+
async function MasqFail(req: Request, res: Response, failureFile: string) {
46+
if (!req.headers.host) {
47+
return;
48+
}
49+
const unsafeSuffix = req.headers.host + ".html";
50+
let safeSuffix = path.normalize(unsafeSuffix).replace(/^(\.\.(\/|\\|$))+/, "");
51+
let safeJoin = path.join(process.cwd() + "/Masqrd", safeSuffix);
52+
try {
53+
await fs.promises.access(safeJoin); // man do I wish this was an if-then instead of a "exception on fail"
54+
const failureFileLocal = await fs.promises.readFile(safeJoin, "utf8");
55+
res.setHeader("Content-Type", "text/html");
56+
res.send(failureFileLocal);
57+
return;
58+
} catch (e) {
59+
res.setHeader("Content-Type", "text/html");
60+
res.send(failureFile);
61+
return;
62+
}
63+
}

tsconfig.json

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"module": "ES2020",
5+
"esModuleInterop": true,
6+
"forceConsistentCasingInFileNames": true,
7+
"strict": true,
8+
"skipLibCheck": true,
9+
"rootDir": "src",
10+
"outDir": "dist",
11+
"moduleResolution": "Node",
12+
"declaration": true,
13+
}
14+
}

0 commit comments

Comments
 (0)