111 lines
3.7 KiB
JavaScript
111 lines
3.7 KiB
JavaScript
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);
|
|
assert.equal(response.status < 500, true, `${path} returned ${response.status}`);
|
|
}
|
|
});
|
|
|
|
test("admin redirects to setup or login", async () => {
|
|
const response = await request(app).get("/admin");
|
|
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 = csrfFrom(page.text);
|
|
const response = await agent
|
|
.post("/contact")
|
|
.send(`_csrf=${encodeURIComponent(token || "")}&name=&email=bad&subject=&message=`);
|
|
assert.equal(response.status, 400);
|
|
});
|