Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899import { test } from 'node:test'import { deepStrictEqual, equal, match, rejects } from 'node:assert/strict'import http from 'node:http'import { createUserinputBoardProxyHandler } from '../../src/server/userinput-board-proxy.mjs'
const board = { spaceDid: 'did:plc:reams-test-board', spaceRkey: 'board-key' }
async function withServers(upstreamHandler, run) { const upstream = http.createServer(upstreamHandler) await new Promise(resolve => upstream.listen(0, '127.0.0.1', resolve)) const origin = `http://127.0.0.1:${upstream.address().port}` const handler = createUserinputBoardProxyHandler({ ...board, apiBaseUrl: origin, allowLoopbackTest: true }) const app = http.createServer(async (req, res) => { if (!await handler(req, res)) { res.writeHead(404); res.end() } }) await new Promise(resolve => app.listen(0, '127.0.0.1', resolve)) try { await run(`http://127.0.0.1:${app.address().port}`, origin) } finally { await Promise.all([upstream, app].map(server => new Promise(resolve => server.close(resolve)))) }}
test('proxies only the configured public board without forwarding caller credentials', async () => { await withServers((req, res) => { equal(req.url, `/api/board/${board.spaceDid}/${board.spaceRkey}`) equal(req.method, 'GET') equal(req.headers.authorization, undefined) equal(req.headers.cookie, undefined) equal(req.headers.accept, 'application/json') res.writeHead(200, { 'content-type': 'application/json' }) res.end(JSON.stringify({ posts: [{ title: 'Public' }] })) }, async origin => { const response = await fetch(`${origin}/api/feedback/board?url=http://127.0.0.1/secret`, { headers: { authorization: 'Bearer caller-secret', cookie: 'session=caller-secret' }, }) equal(response.status, 200) match(response.headers.get('content-type'), /^application\/json; charset=utf-8$/) equal(response.headers.get('cache-control'), 'no-store') deepStrictEqual(await response.json(), { posts: [{ title: 'Public' }], spaceUrl: 'https://userinput.app/s/did:plc:reams-test-board/board-key' }) })})
test('maps upstream 503 to actionable service-unavailable JSON', async () => { await withServers((_req, res) => { res.writeHead(503); res.end('edge unavailable') }, async origin => { const response = await fetch(`${origin}/api/feedback/board`) equal(response.status, 503) match((await response.json()).message, /temporarily unavailable.*Try again/) })})
test('fails closed on invalid configuration and disallows arbitrary non-HTTPS origins', async () => { for (const options of [ { ...board, spaceDid: 'not-a-did' }, { ...board, spaceRkey: '../secret' }, { ...board, apiBaseUrl: 'http://127.0.0.1:1234' }, { ...board, apiBaseUrl: 'https://user:pass@userinput.app' }, ]) rejects(Promise.resolve().then(() => createUserinputBoardProxyHandler(options)))})
test('does not match unrelated paths and rejects non-GET methods', async () => { const handler = createUserinputBoardProxyHandler({ ...board }) const response = { writeHead(status, headers) { this.status = status; this.headers = headers }, end(body) { this.body = body } } equal(await handler({ method: 'GET', url: '/elsewhere' }, response), false) equal(await handler({ method: 'POST', url: '/api/feedback/board' }, response), true) equal(response.status, 405) equal(response.headers['cache-control'], 'no-store') deepStrictEqual(JSON.parse(response.body), { error: 'method_not_allowed', message: 'Use GET to read the public feedback board.' })})
test('bounds upstream bodies', async () => { const originServer = http.createServer((_req, res) => { res.writeHead(200, { 'content-type': 'application/json' }) res.end('"' + 'x'.repeat(128) + '"') }) await new Promise(resolve => originServer.listen(0, '127.0.0.1', resolve)) const handler = createUserinputBoardProxyHandler({ ...board, apiBaseUrl: `http://127.0.0.1:${originServer.address().port}`, allowLoopbackTest: true, maxResponseBytes: 32 }) const response = { writeHead(status) { this.status = status }, end(body) { this.body = body } } try { await handler({ method: 'GET', url: '/api/feedback/board' }, response) equal(response.status, 502) match(JSON.parse(response.body).message, /configuration/) } finally { await new Promise(resolve => originServer.close(resolve)) }})
test('times out a slow upstream response', async () => { const upstream = http.createServer(() => {}) await new Promise(resolve => upstream.listen(0, '127.0.0.1', resolve)) const handler = createUserinputBoardProxyHandler({ ...board, apiBaseUrl: `http://127.0.0.1:${upstream.address().port}`, allowLoopbackTest: true, timeoutMs: 20 }) const response = { writeHead(status) { this.status = status }, end(body) { this.body = body } } try { await handler({ method: 'GET', url: '/api/feedback/board' }, response) equal(response.status, 504) match(JSON.parse(response.body).message, /too long to respond/) } finally { upstream.closeAllConnections() await new Promise(resolve => upstream.close(resolve)) }})