← back to AbramsOS

tests/csrf.test.js

65 lines

// Verifies CSRF middleware rejects POSTs without a matching token.

const test = require('node:test');
const assert = require('node:assert');
const http = require('node:http');

require('dotenv').config();
const app = require('../server');
const { pool } = require('../lib/db');

let server;
test.before(() => new Promise((r) => { server = app.listen(0, r); }));
test.after(async () => {
  await new Promise((r) => server.close(r));
  await pool.end();
});

function post(path, formObj = {}, headers = {}) {
  return new Promise((resolve, reject) => {
    const port = server.address().port;
    const body = new URLSearchParams(formObj).toString();
    const req = http.request({
      method: 'POST', hostname: '127.0.0.1', port, path,
      headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(body), ...headers },
    }, (res) => {
      let data = ''; res.on('data', (c) => (data += c));
      res.on('end', () => resolve({ status: res.statusCode, body: data, headers: res.headers }));
    });
    req.on('error', reject);
    req.write(body);
    req.end();
  });
}

test('POST /signup without CSRF token → 403', async () => {
  const r = await post('/signup', { email: 'attacker@example.com', password: 'verystrongpw1234' });
  assert.strictEqual(r.status, 403);
  assert.match(r.body, /CSRF/i);
});

test('POST /signin without CSRF token → 403', async () => {
  const r = await post('/signin', { stage: 'password', email: 'a@b.com', password: 'x' });
  assert.strictEqual(r.status, 403);
});

test('POST /signup with WRONG CSRF token → 403', async () => {
  // First request to mint a real cookie, then POST with a forged body token
  const get = await new Promise((resolve, reject) => {
    const port = server.address().port;
    http.get(`http://127.0.0.1:${port}/signup`, (res) => { res.resume(); res.on('end', () => resolve(res)); }).on('error', reject);
  });
  const cookieHeader = (get.headers['set-cookie'] || []).map(c => c.split(';')[0]).join('; ');
  const r = await post('/signup', { _csrf: 'forged-token-xxx', email: 'a@b.com', password: 'verystrongpw1234' }, { cookie: cookieHeader });
  assert.strictEqual(r.status, 403);
});

test('GET routes mint a CSRF cookie', async () => {
  const port = server.address().port;
  const got = await new Promise((resolve, reject) => {
    http.get(`http://127.0.0.1:${port}/signup`, (res) => { res.resume(); res.on('end', () => resolve(res)); }).on('error', reject);
  });
  const setCookie = got.headers['set-cookie'] || [];
  assert.ok(setCookie.some(c => c.startsWith('aos.csrf=')), 'aos.csrf cookie should be set');
});