import { createServer } from 'node:http'; import { readFileSync, statSync, realpathSync } from 'node:fs'; import { extname, resolve, sep } from 'node:path'; import { fileURLToPath } from 'node:url'; import { estimateEntropy, generateMemorizable, generateRandom, } from './src/generator.js'; import { listMeta } from './src/words.js'; const PUBLIC_DIR = resolve(fileURLToPath(new URL('./public/', import.meta.url))); const NOT_FOUND_PAGE = resolve(PUBLIC_DIR, '404.html'); const MAX_BODY_BYTES = 64 * 1024; const PORT = Number.parseInt(process.env.PORT || '3000', 10); const HOST = '0.0.0.0'; const SECURITY_HEADERS = { 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'no-referrer', 'X-Frame-Options': 'DENY', 'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'self'", }; const CONTENT_TYPES = { '.html': 'text/html; charset=utf-8', '.css': 'text/css; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.txt': 'text/plain; charset=utf-8', '.json': 'application/json; charset=utf-8', '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon', }; function sendBuffer(req, res, status, body, headers) { res.writeHead(status, { ...SECURITY_HEADERS, 'Content-Length': body.length, ...headers, }); if (req.method === 'HEAD') { res.end(); return; } res.end(body); } function sendJson(req, res, status, payload) { const body = Buffer.from(JSON.stringify(payload)); sendBuffer(req, res, status, body, { 'Content-Type': 'application/json; charset=utf-8', 'Cache-Control': 'no-store', }); } function sendError(req, res, status, message) { sendJson(req, res, status, { error: message }); } function sendNotFoundPage(req, res) { let body; try { body = readFileSync(NOT_FOUND_PAGE); } catch { body = Buffer.from('Not found

Not found

'); } sendBuffer(req, res, 404, body, { 'Content-Type': 'text/html; charset=utf-8', 'Cache-Control': 'no-store', }); } function contentTypeFor(filePath) { return CONTENT_TYPES[extname(filePath).toLowerCase()] || 'application/octet-stream'; } function resolveStaticPath(pathname) { let decoded; try { decoded = decodeURIComponent(pathname); } catch { return null; } if (decoded.includes('\0')) return null; const relative = decoded === '/' ? 'index.html' : decoded.replace(/^\/+/, ''); const target = resolve(PUBLIC_DIR, relative); if (target !== PUBLIC_DIR && !target.startsWith(PUBLIC_DIR + sep)) { return null; } return target; } function serveStatic(req, res, pathname) { const target = resolveStaticPath(pathname); if (!target) { sendNotFoundPage(req, res); return; } let stats; let real; try { stats = statSync(target); real = realpathSync(target); } catch { sendNotFoundPage(req, res); return; } if (!stats.isFile() || (real !== PUBLIC_DIR && !real.startsWith(PUBLIC_DIR + sep))) { sendNotFoundPage(req, res); return; } let body; try { body = readFileSync(real); } catch { sendNotFoundPage(req, res); return; } sendBuffer(req, res, 200, body, { 'Content-Type': contentTypeFor(real), 'Cache-Control': 'no-cache', }); } function readJsonBody(req) { return new Promise((resolvePromise, rejectPromise) => { const chunks = []; let size = 0; let tooLarge = false; req.on('data', (chunk) => { size += chunk.length; if (size > MAX_BODY_BYTES) { tooLarge = true; chunks.length = 0; if (size > MAX_BODY_BYTES * 8) { req.destroy(); } return; } chunks.push(chunk); }); req.on('error', (err) => { rejectPromise(Object.assign(err, { statusCode: 400 })); }); req.on('end', () => { if (tooLarge) { rejectPromise(Object.assign(new Error('request body too large (limit 64 KB)'), { statusCode: 413 })); return; } const raw = Buffer.concat(chunks).toString('utf8'); if (raw.trim() === '') { resolvePromise({}); return; } try { const parsed = JSON.parse(raw); if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { rejectPromise(Object.assign(new Error('request body must be a JSON object'), { statusCode: 400 })); return; } resolvePromise(parsed); } catch { rejectPromise(Object.assign(new Error('malformed JSON body'), { statusCode: 400 })); } }); }); } function countOption(body) { const raw = body.count === undefined || body.count === null ? 1 : Number(body.count); if (!Number.isInteger(raw) || raw < 1 || raw > 10) { throw Object.assign(new Error('count must be an integer between 1 and 10'), { statusCode: 400 }); } return raw; } async function handleGenerate(req, res, body) { const mode = body.mode === undefined || body.mode === null ? 'memorable' : String(body.mode); if (mode !== 'memorable' && mode !== 'random') { sendError(req, res, 400, `unknown mode "${mode}"`); return; } let count; try { count = countOption(body); } catch (err) { sendError(req, res, err.statusCode || 400, err.message); return; } let entropy; try { entropy = estimateEntropy(mode, body); } catch (err) { sendError(req, res, 400, err.message); return; } const passwords = []; for (let i = 0; i < count; i += 1) { let value; try { value = mode === 'memorable' ? generateMemorizable(body) : generateRandom(body); } catch (err) { sendError(req, res, 400, err.message); return; } passwords.push({ value, mode, entropy }); } sendJson(req, res, 200, { mode, count, passwords, entropy }); } async function handleApi(req, res, pathname) { if (pathname === '/api/meta') { if (req.method !== 'GET' && req.method !== 'HEAD') { sendError(req, res, 405, 'method not allowed'); return; } sendJson(req, res, 200, { lists: listMeta() }); return; } if (pathname === '/api/generate') { if (req.method !== 'POST') { sendError(req, res, 405, 'method not allowed'); return; } let body; try { body = await readJsonBody(req); } catch (err) { sendError(req, res, err.statusCode || 400, err.message); return; } await handleGenerate(req, res, body); return; } sendError(req, res, 404, 'not found'); } const server = createServer((req, res) => { let pathname = '/'; try { pathname = new URL(req.url || '/', 'http://localhost').pathname; } catch { sendError(req, res, 400, 'bad request'); return; } if (pathname === '/healthz') { if (req.method !== 'GET' && req.method !== 'HEAD') { sendError(req, res, 405, 'method not allowed'); return; } sendJson(req, res, 200, { status: 'ok' }); return; } if (pathname.startsWith('/api/')) { handleApi(req, res, pathname).catch((err) => { sendError(req, res, 500, err && err.message ? err.message : 'internal error'); }); return; } if (req.method !== 'GET' && req.method !== 'HEAD') { sendError(req, res, 405, 'method not allowed'); return; } serveStatic(req, res, pathname); }); server.listen(PORT, HOST, () => { console.log(`reze-test listening on http://${HOST}:${PORT}`); });