require("ts-node/register/transpile-only"); const { expect } = require("chai"); const express = require("express"); const session = require("express-session"); const http = require("http"); const { setImmediate } = require("timers"); const { randomBytes } = require("crypto"); const { createReviewConsentRouter, } = require("../src/server/service/review-consent-http"); const { createReviewCallbacks } = require("../src/server/service/review-callbacks"); const callbackURL = "https://review.example.test/api/v1/artifacts/callback"; const callbacks = createReviewCallbacks(JSON.stringify({version:1,callbacks:[{clientId:"1".repeat(32),callbackId:"6".repeat(32),url:callbackURL}]})); const origin = "https://anonymous.example.test"; const owner = "a".repeat(24); const intent = { contract: "4open.artifacts/1", clientId: "1".repeat(32), intentId: "2".repeat(32), token: "3".repeat(64), requestId: "4".repeat(32), }; const previewInput = () => ({ repositoryId: "synthetic-repository", intent }); const confirmInput = () => ({ ticket: "synthetic-signed-ticket", requestId: "5".repeat(32), acceptAccess: true, acceptRetention: true, }); const policy = { version: 1, access: "restricted-review", retainUntil: "2099-01-01T00:00:00Z", }; describe("review consent browser HTTP transport", function () { this.timeout(20000); let server, store, cookie, sid, csrf, backend, calls; function request(path, options = {}) { const body = options.raw === undefined ? options.body === undefined ? undefined : JSON.stringify(options.body) : options.raw; return new Promise((resolve, reject) => { const req = http.request( { host: "127.0.0.1", port: server.address().port, path, method: options.method || (body === undefined ? "GET" : "POST"), headers: { "X-Forwarded-Proto": "https", ...(cookie ? { Cookie: cookie } : {}), ...(body === undefined ? {} : { Origin: origin, "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body), "X-Review-CSRF": csrf || "", }), ...options.headers, }, }, (res) => { const chunks = []; res.on("data", (chunk) => chunks.push(chunk)); res.on("end", () => { const raw = Buffer.concat(chunks).toString(); resolve({ status: res.statusCode, headers: res.headers, body: raw ? JSON.parse(raw) : null, }); }); }, ); req.on("error", reject); if (body !== undefined) req.write(body); req.end(); }); } beforeEach(async () => { cookie = ""; sid = ""; csrf = ""; calls = []; store = new session.MemoryStore(); backend = { preview: async (actor, repository, input) => { calls.push({ actor, repository, input }); return { ticket: "synthetic-signed-ticket", repositoryName: repository, policy, expiresAt: "2098-01-01T00:00:00Z", }; }, confirm: async (actor, ticket, input) => { calls.push({ actor, ticket, input }); return { clientId: intent.clientId, intentId: intent.intentId, accountId: actor.accountId, repositoryId: "b".repeat(24), role: "owner", requestId: input.requestId, policy, confirmedAt: "2026-01-01T00:00:00Z", }; }, }; backend.handoff = async (actor, ticket, resolveCallback) => { calls.push({actor,ticket,handoff:true}); return { callbackUrl: resolveCallback(intent.clientId,"6".repeat(32)), completion: { contract: intent.contract, clientId: intent.clientId, intentId: intent.intentId, code: "9".repeat(64), expiresAt: new Date(Date.now()+60000).toISOString().replace(/\.\d{3}Z$/, "Z"), privateField: "private-field" }, privateField: "private-field" }; }; const app = express(); // Model a TLS-terminating trusted loopback proxy, never trust arbitrary peers. app.set("trust proxy", "loopback"); app.use( session({ secret: "synthetic-local-session-secret", resave: false, saveUninitialized: false, store, }), ); // Test-only login stands in for Passport's existing authenticated session. app.get("/test-login", (req, res) => { req.session.passport = { user: owner }; req.session.save((error) => error ? res.sendStatus(500) : res.json({ sid: req.sessionID }), ); }); app.use((req, _res, next) => { const id = req.session.passport?.user; if (id) req.user = { user: { _id: id } }; req.isAuthenticated = () => !!id; next(); }); app.use( "/api/review-consent", createReviewConsentRouter(origin, backend, randomBytes(32), callbacks), ); app.use("/api/review-consent-disabled", createReviewConsentRouter(origin, backend, randomBytes(32))); server = http.createServer(app); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const login = await request("/test-login"); cookie = login.headers["set-cookie"][0].split(";")[0]; sid = login.body.sid; const issued = await request("/api/review-consent/csrf"); expect(issued.status).to.equal(200); csrf = issued.body.csrf; }); afterEach(async () => { server.closeAllConnections(); await new Promise((resolve) => server.close(resolve)); store.clear(); }); it("binds CSRF to the current Passport session and omits owner identifiers from confirmation responses", async () => { const preview = await request("/api/review-consent/preview", { body: previewInput(), }); expect(preview.status).to.equal(200); expect(calls[0].actor).to.deep.equal({ accountId: owner, sessionId: sid }); expect(calls[0].input).to.deep.equal(intent); expect(preview.headers["cache-control"]).to.equal("no-store"); expect(preview.headers["referrer-policy"]).to.equal("no-referrer"); expect(preview.headers["access-control-allow-origin"]).to.equal(undefined); const result = await request("/api/review-consent/confirm", { body: confirmInput(), }); expect(result.status).to.equal(200); expect(result.body).to.deep.equal({ requestId: "5".repeat(32), policy, confirmedAt: "2026-01-01T00:00:00Z", }); expect(JSON.stringify(result.body)).not.to.include(owner); }); it("rejects insecure, cross-origin, bearer and missing-CSRF requests before backend work", async () => { for (const headers of [ { "X-Forwarded-Proto": "http" }, { Origin: "https://attacker.example" }, { Origin: "null" }, { Authorization: "Bearer synthetic" }, { "X-Review-CSRF": "0".repeat(64) }, { "X-Review-CSRF": "" }, { "Sec-Fetch-Site": "same-site" }, { "Content-Encoding": "gzip" }, ]) expect( ( await request("/api/review-consent/preview", { body: previewInput(), headers, }) ).status, ).to.equal(403); expect( ( await request("/api/review-consent/preview", { body: previewInput(), headers: { Cookie: "" }, }) ).status, ).to.equal(401); expect(calls).to.have.length(0); }); it("accepts only bounded JSON with the exact operation fields and explicit acceptance", async () => { for (const input of [ { ...previewInput(), accountId: owner }, { ...previewInput(), policy }, { ...previewInput(), repositoryId: "../other" }, { ...previewInput(), intent: { ...intent, requestId: "bad" } }, ]) expect( (await request("/api/review-consent/preview", { body: input })).status, ).to.equal(422); for (const input of [ { ...confirmInput(), acceptAccess: false }, { ...confirmInput(), acceptRetention: "true" }, { ...confirmInput(), policy }, ]) expect( (await request("/api/review-consent/confirm", { body: input })).status, ).to.equal(422); expect( (await request("/api/review-consent/preview", { raw: "{" })).status, ).to.equal(400); expect( (await request("/api/review-consent/preview", { raw: " ".repeat(13000) })) .status, ).to.equal(413); expect( ( await request("/api/review-consent/preview", { body: previewInput(), headers: { "Content-Type": "text/plain" }, }) ).status, ).to.equal(415); for (const path of ["/csrf?x=1", "/csrf/", "/CSRF", "/%63srf", "/other"]) expect((await request("/api/review-consent" + path)).status).to.equal( 404, ); expect((await request("/api/review-consent/preview")).status).to.equal(405); expect( (await request("/api/review-consent/csrf", { method: "GET", body: {} })) .status, ).to.equal(400); expect(calls).to.have.length(0); }); it("rejects a token copied from another session of the same account", async () => { const old = csrf; cookie = ""; const login = await request("/test-login"); cookie = login.headers["set-cookie"][0].split(";")[0]; expect( ( await request("/api/review-consent/preview", { body: previewInput(), headers: { "X-Review-CSRF": old }, }) ).status, ).to.equal(403); expect(calls).to.have.length(0); }); for (const operation of ["preview", "confirm", "handoff"]) { for (const change of ["logout", "account replacement"]) { it(`rejects ${operation} before backend work after ${change} during upload`, async () => { const original = store.get.bind(store); let reads = 0, ready; const authenticated = new Promise((resolve) => { ready = resolve; }); store.get = (id, callback) => original(id, (error, value) => { callback(error, value); // The first read restores the session; the second authenticates // the request before the JSON parser waits for the remaining body. if (++reads === 2) setImmediate(ready); }); const body = JSON.stringify(operation === "preview" ? previewInput() : operation === "handoff" ? { ticket: "synthetic-signed-ticket" } : confirmInput()); let upload; const response = new Promise((resolve, reject) => { upload = http.request({ host: "127.0.0.1", port: server.address().port, path: "/api/review-consent/" + operation, method: "POST", headers: { "X-Forwarded-Proto": "https", Cookie: cookie, Origin: origin, "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body), "X-Review-CSRF": csrf, }, }, (res) => { res.resume(); res.on("end", () => resolve(res.statusCode)); }); upload.on("error", reject); upload.write(body.slice(0, 1)); }); try { await authenticated; if (change === "logout") { await new Promise((resolve) => store.destroy(sid, resolve)); } else { const saved = await new Promise((resolve) => original(sid, (_error, value) => resolve(value))); saved.passport.user = "b".repeat(24); await new Promise((resolve) => store.set(sid, saved, resolve)); } upload.end(body.slice(1)); expect(await response).to.equal(401); expect(calls).to.have.length(0); } finally { upload.destroy(); store.get = original; } }); } } it("reloads session authority before returning a pending policy preview", async () => { let arrived, release; const ready = new Promise((resolve) => { arrived = resolve; }); const gate = new Promise((resolve) => { release = resolve; }); backend.preview = async () => { arrived(); await gate; return { ticket: "private-policy-preview", policy, repositoryName: "synthetic-repository", expiresAt: "2098-01-01T00:00:00Z", }; }; const pending = request("/api/review-consent/preview", { body: previewInput(), }); await ready; await new Promise((resolve) => store.destroy(sid, resolve)); release(); const result = await pending; expect(result.status).to.equal(401); expect(JSON.stringify(result.body)).not.to.include( "private-policy-preview", ); }); it("never rewrites or resurrects a session when a CSRF read races with logout", async () => { const original = store.get.bind(store); let reads = 0, arrived, release; const ready = new Promise((resolve) => { arrived = resolve; }); const gate = new Promise((resolve) => { release = resolve; }); store.get = (id, callback) => original(id, (error, value) => { if (++reads === 3) { arrived(); gate.then(() => callback(error, value)); } else callback(error, value); }); let writes = 0; const set = store.set.bind(store); store.set = (...args) => { writes++; return set(...args); }; const pending = request("/api/review-consent/csrf"); await ready; await new Promise((resolve) => store.destroy(sid, resolve)); release(); await pending; expect(writes).to.equal(0); expect( await new Promise((resolve) => original(sid, (_error, value) => resolve(value)), ), ).to.equal(undefined); expect( (await request("/api/review-consent/preview", { body: previewInput() })) .status, ).to.equal(401); expect(calls).to.have.length(0); }); it("returns fixed failure codes without exposing provider or database messages", async () => { for (const [kind, status] of [ ["forbidden", 403], ["conflict", 409], ["expired", 410], ["invalid", 422], ["unavailable", 503], [undefined, 503], ]) { backend.preview = async () => { const error = new Error("private credential and database details"); error.kind = kind; throw error; }; const result = await request("/api/review-consent/preview", { body: previewInput(), }); expect(result.status).to.equal(status); expect(JSON.stringify(result.body)).not.to.include("private"); } }); it("bounds authenticated request rates without accepting a different client identity", async () => { for (let n = 0; n < 59; n++) expect((await request("/api/review-consent/csrf")).status).to.equal(200); const response = await request("/api/review-consent/csrf"); expect(response.status).to.equal(429); expect(response.headers["retry-after"]).to.equal("60"); expect(calls).to.have.length(0); }); it("closes an unauthorized incomplete upload without waiting for its declared body", async () => { const started = Date.now(); let pending; try { const result = await new Promise((resolve, reject) => { pending = http.request( { host: "127.0.0.1", port: server.address().port, path: "/api/review-consent/preview", method: "POST", headers: { "X-Forwarded-Proto": "https", Origin: origin, "Content-Type": "application/json", "Content-Length": 10000000, }, }, (response) => { response.resume(); response.on("end", () => resolve({ status: response.statusCode, connection: response.headers.connection, }), ); }, ); pending.on("error", reject); pending.write("{"); }); expect(result).to.deep.equal({ status: 401, connection: "close" }); expect(Date.now() - started).to.be.lessThan(2000); expect(calls).to.have.length(0); } finally { pending?.destroy(); } }); it("cancels a pending preview when its browser connection closes", async () => { let entered, aborted; const ready = new Promise((resolve) => { entered = resolve; }); const cancelled = new Promise((resolve) => { aborted = resolve; }); backend.preview = async (_actor, _repo, _input, signal) => { entered(); await new Promise((resolve) => signal.addEventListener("abort", resolve, { once: true }), ); aborted(); throw new Error("private pending request cancelled"); }; const body = JSON.stringify(previewInput()); const pending = http.request({ host: "127.0.0.1", port: server.address().port, path: "/api/review-consent/preview", method: "POST", headers: { Cookie: cookie, "X-Forwarded-Proto": "https", Origin: origin, "Content-Type": "application/json", "X-Review-CSRF": csrf, "Content-Length": Buffer.byteLength(body), }, }); pending.on("error", () => undefined); pending.end(body); await ready; pending.destroy(); await cancelled; }); it("expires an authenticated incomplete upload at the total request deadline", async () => { const started = Date.now(); let pending; try { const status = await new Promise((resolve) => { pending = http.request( { host: "127.0.0.1", port: server.address().port, path: "/api/review-consent/preview", method: "POST", headers: { Cookie: cookie, "X-Forwarded-Proto": "https", Origin: origin, "Content-Type": "application/json", "Content-Length": 1000, "X-Review-CSRF": csrf, }, }, (response) => { response.resume(); response.on("end", () => resolve(response.statusCode)); }, ); pending.on("error", () => resolve("closed")); pending.write("{"); }); expect([408, "closed"]).to.include(status); expect(Date.now() - started).to.be.within(14000, 19000); expect(calls).to.have.length(0); } finally { pending?.destroy(); } }); it("measures authenticated HTTP preview overhead when explicitly requested", async function () { const process = require("process"); if (!process.env.TEST_REVIEW_CONSENT_HTTP_PERF) this.skip(); const times = []; for (let n = 0; n < 50; n++) { const start = process.hrtime.bigint(); expect( (await request("/api/review-consent/preview", { body: previewInput() })) .status, ).to.equal(200); times.push(Number(process.hrtime.bigint() - start) / 1e6); } times.sort((a, b) => a - b); expect(calls).to.have.length(50); require("fs").writeFileSync( process.env.TEST_REVIEW_CONSENT_HTTP_PERF, JSON.stringify( { calls: times.length, transport: "loopback HTTP with synthetic trusted-proxy TLS metadata", sessionStore: "in-memory test store", backend: "synthetic response, no MongoDB or provider timing", meanMs: times.reduce((a, b) => a + b, 0) / times.length, medianMs: times[25], p95Ms: times[47], }, null, 2, ) + "\n", ); }); it('offers only a configured, current-session completion POST handoff', async function () { expect((await request('/api/review-consent/csrf')).body.handoffEnabled).equal(true); const response=await request('/api/review-consent/handoff',{body:{ticket:'synthetic-signed-ticket'}}); expect(response.status).equal(200);expect(response.body.callbackUrl).equal(callbackURL); expect(response.body.completion.code).equal('9'.repeat(64)); expect(Object.keys(response.body.completion).sort()).deep.equal(['clientId','code','contract','expiresAt','intentId']); expect(JSON.stringify(response.body)).not.include('private-field'); expect(response.headers['cache-control']).equal('no-store'); expect((await request('/api/review-consent/handoff',{body:{ticket:'synthetic-signed-ticket',callbackUrl:'https://other.example.test/callback'}})).status).equal(400); expect((await request('/api/review-consent-disabled/csrf')).body.handoffEnabled).equal(false); expect((await request('/api/review-consent-disabled/handoff',{body:{ticket:'synthetic-signed-ticket'}})).status).equal(404); }); it('does not disclose a completion code after concurrent logout', async function () { let entered, release; const pending=new Promise(resolve=>{entered=resolve;}); const wait=new Promise(resolve=>{release=resolve;}); const original=backend.handoff; backend.handoff=async(...args)=>{entered();await wait;return original(...args);}; const response=request('/api/review-consent/handoff',{body:{ticket:'synthetic-signed-ticket'}}); await pending;await new Promise(resolve=>store.destroy(sid,resolve));release(); const result=await response;expect(result.status).equal(401);expect(JSON.stringify(result.body)).not.include('9'.repeat(64)); }); });