Replace Web Basic Auth With Cookie Sessions

Add bounded login challenges, CSRF/origin enforcement, logout, and
session-bound WebSocket admission. Isolate private HTTPD access behind a
version-guarded adapter and add focused host coverage. Also let empty
admin
SSH input reach the normal console handler.
This commit is contained in:
2026-09-05 23:55:05 +02:00
parent 4435a7fddd
commit 5a609fa40b
36 changed files with 1940 additions and 360 deletions
+5 -1
View File
@@ -23,6 +23,10 @@ function browser(queue = []) {
setTimeout: (fn, ms) => { timers.set(++timerId, {fn, ms}); return timerId; },
clearTimeout: id => timers.delete(id),
fetch: async (url, options) => {
// Guard the no-referrer/Origin:null regression; this VM does not synthesize browser headers.
assert.ok(['/api/login-challenge', '/api/login'].includes(url));
if (options.method === 'POST') assert.equal(options.mode, 'cors');
assert.equal(options.headers?.Origin, undefined);
calls.push({url, ...options});
assert.ok(queue.length, 'unexpected/automatic fetch');
const next = queue.shift();
@@ -57,7 +61,7 @@ async function test(name, fn) { await fn(); ++passed; console.log('PASS JS:', na
assert.equal(post.headers['X-CSRF-Token'], token); assert.equal(post.headers['Content-Type'], 'application/json');
assert.deepEqual(JSON.parse(post.body), {username: 'alice', password: 'password'});
for (const call of b.calls) {
for (const [k, v] of Object.entries({credentials: 'same-origin', mode: 'same-origin', cache: 'no-store', redirect: 'error'})) assert.equal(call[k], v);
for (const [k, v] of Object.entries({credentials: 'same-origin', mode: 'cors', cache: 'no-store', redirect: 'error'})) assert.equal(call[k], v);
assert.ok(call.signal instanceof AbortSignal);
}
});