@wrongstack/webui 0.7.5 → 0.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/entry.js +22 -4
- package/dist/server/entry.js.map +1 -1
- package/dist/server/index.js +22 -4
- package/dist/server/index.js.map +1 -1
- package/package.json +5 -5
package/dist/server/entry.js
CHANGED
|
@@ -367,7 +367,7 @@ async function startWebUI(opts = {}) {
|
|
|
367
367
|
};
|
|
368
368
|
}
|
|
369
369
|
const wsToken = randomBytes(16).toString("hex");
|
|
370
|
-
console.log(`[WebUI] WS auth token: ${wsToken}`);
|
|
370
|
+
console.log(`[WebUI] WS auth token: ${wsToken.slice(0, 4)}\u2026${wsToken.slice(-4)} (masked)`);
|
|
371
371
|
const isLoopback = (hostname) => hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1" || hostname === "[::1]";
|
|
372
372
|
const verifyClient = (info) => {
|
|
373
373
|
const origin = info.origin;
|
|
@@ -1523,20 +1523,38 @@ async function startWebUI(opts = {}) {
|
|
|
1523
1523
|
} else {
|
|
1524
1524
|
filePath = path.join(DIST_DIR, "index.html");
|
|
1525
1525
|
}
|
|
1526
|
-
const
|
|
1526
|
+
const resolvedPath = path.resolve(filePath);
|
|
1527
|
+
const resolvedRoot = path.resolve(DIST_DIR);
|
|
1528
|
+
if (!resolvedPath.startsWith(resolvedRoot + path.sep) && resolvedPath !== resolvedRoot) {
|
|
1529
|
+
res.writeHead(403, { "Content-Type": "text/plain" });
|
|
1530
|
+
res.end("Forbidden");
|
|
1531
|
+
return;
|
|
1532
|
+
}
|
|
1533
|
+
const ext = path.extname(resolvedPath);
|
|
1527
1534
|
const contentType = mimeTypes[ext] ?? "application/octet-stream";
|
|
1528
1535
|
res.setHeader("Content-Type", contentType);
|
|
1536
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
1537
|
+
res.setHeader("X-Frame-Options", "DENY");
|
|
1538
|
+
res.setHeader("Referrer-Policy", "strict-origin-when-cross-origin");
|
|
1529
1539
|
if (ext === ".html") {
|
|
1530
1540
|
res.setHeader("Cache-Control", "no-cache");
|
|
1541
|
+
res.setHeader(
|
|
1542
|
+
"Content-Security-Policy",
|
|
1543
|
+
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' ws: wss:; img-src 'self' data:; font-src 'self' data:"
|
|
1544
|
+
);
|
|
1531
1545
|
}
|
|
1532
|
-
const fileContent = await fs2.readFile(
|
|
1546
|
+
const fileContent = await fs2.readFile(resolvedPath);
|
|
1533
1547
|
res.writeHead(200);
|
|
1534
1548
|
res.end(fileContent);
|
|
1535
1549
|
} catch (err) {
|
|
1536
1550
|
if (err.code === "ENOENT") {
|
|
1537
1551
|
try {
|
|
1538
1552
|
const fileContent = await fs2.readFile(path.join(DIST_DIR, "index.html"));
|
|
1539
|
-
res.writeHead(200, {
|
|
1553
|
+
res.writeHead(200, {
|
|
1554
|
+
"Content-Type": "text/html",
|
|
1555
|
+
"X-Content-Type-Options": "nosniff",
|
|
1556
|
+
"X-Frame-Options": "DENY"
|
|
1557
|
+
});
|
|
1540
1558
|
res.end(fileContent);
|
|
1541
1559
|
} catch {
|
|
1542
1560
|
res.writeHead(404);
|