Harden deployment security
This commit is contained in:
@@ -1,8 +1,23 @@
|
||||
const test = require("node:test");
|
||||
const assert = require("node:assert/strict");
|
||||
const fs = require("fs");
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const request = require("supertest");
|
||||
|
||||
const testRoot = fs.mkdtempSync(path.join(os.tmpdir(), "dws-test-"));
|
||||
process.env.DATABASE_PATH = path.join(testRoot, "site.sqlite");
|
||||
process.env.UPLOAD_DIR = path.join(testRoot, "uploads");
|
||||
process.env.BACKUP_DIR = path.join(testRoot, "backups");
|
||||
process.env.SESSION_SECRET = "test-session-secret-with-more-than-thirty-two-characters";
|
||||
process.env.SETUP_TOKEN = "test-setup-token-with-enough-length";
|
||||
|
||||
const { app } = require("../src/server");
|
||||
|
||||
function csrfFrom(html) {
|
||||
return /name="_csrf" value="([^"]+)"/.exec(html)?.[1] || "";
|
||||
}
|
||||
|
||||
test("public pages load", async () => {
|
||||
for (const path of ["/", "/items", "/services", "/about-contact", "/healthz"]) {
|
||||
const response = await request(app).get(path);
|
||||
@@ -15,10 +30,79 @@ test("admin redirects to setup or login", async () => {
|
||||
assert.equal([302, 303].includes(response.status), true);
|
||||
});
|
||||
|
||||
test("owner setup requires the private setup token", async () => {
|
||||
const agent = request.agent(app);
|
||||
|
||||
const locked = await agent.get("/admin/setup");
|
||||
assert.equal(locked.status, 403);
|
||||
|
||||
const setupPage = await agent.get("/admin/setup?token=test-setup-token-with-enough-length");
|
||||
assert.equal(setupPage.status, 200);
|
||||
const token = csrfFrom(setupPage.text);
|
||||
|
||||
const takeover = await agent
|
||||
.post("/admin/setup")
|
||||
.type("form")
|
||||
.send({
|
||||
_csrf: token,
|
||||
name: "Attacker",
|
||||
email: "attacker@example.com",
|
||||
password: "attacker-password"
|
||||
});
|
||||
assert.equal(takeover.status, 403);
|
||||
|
||||
const created = await agent
|
||||
.post("/admin/setup")
|
||||
.type("form")
|
||||
.send({
|
||||
_csrf: token,
|
||||
setup_token: "test-setup-token-with-enough-length",
|
||||
name: "Owner",
|
||||
email: "owner@example.com",
|
||||
password: "owner-password-123"
|
||||
});
|
||||
assert.equal(created.status, 302);
|
||||
assert.equal(created.headers.location, "/admin/dashboard");
|
||||
|
||||
const dashboard = await agent.get("/admin/dashboard");
|
||||
assert.equal(dashboard.status, 200);
|
||||
|
||||
const setupAfterClaim = await agent.get("/admin/setup?token=test-setup-token-with-enough-length");
|
||||
assert.equal(setupAfterClaim.status, 302);
|
||||
assert.equal(setupAfterClaim.headers.location, "/admin/login");
|
||||
});
|
||||
|
||||
test("multipart admin uploads pass CSRF after form parsing", async () => {
|
||||
const agent = request.agent(app);
|
||||
const loginPage = await agent.get("/admin/login");
|
||||
const loginToken = csrfFrom(loginPage.text);
|
||||
const login = await agent
|
||||
.post("/admin/login")
|
||||
.type("form")
|
||||
.send({
|
||||
_csrf: loginToken,
|
||||
email: "owner@example.com",
|
||||
password: "owner-password-123"
|
||||
});
|
||||
assert.equal(login.status, 302);
|
||||
|
||||
const mediaPage = await agent.get("/admin/media");
|
||||
const uploadToken = csrfFrom(mediaPage.text);
|
||||
const sampleImage = path.join(__dirname, "..", "public", "uploads", "sample-hero.png");
|
||||
const upload = await agent
|
||||
.post("/admin/media")
|
||||
.field("_csrf", uploadToken)
|
||||
.field("title", "Test upload")
|
||||
.field("alt", "Test upload")
|
||||
.attach("images", sampleImage);
|
||||
assert.equal(upload.status, 302);
|
||||
assert.equal(upload.headers.location, "/admin/media");
|
||||
});
|
||||
|
||||
test("contact form requires complete fields", async () => {
|
||||
const agent = request.agent(app);
|
||||
const page = await agent.get("/about-contact");
|
||||
const token = /name="_csrf" value="([^"]+)"/.exec(page.text)?.[1];
|
||||
const token = csrfFrom(page.text);
|
||||
const response = await agent
|
||||
.post("/contact")
|
||||
.send(`_csrf=${encodeURIComponent(token || "")}&name=&email=bad&subject=&message=`);
|
||||
|
||||
Reference in New Issue
Block a user