import { describe, expect, it, vi } from "vitest"; import { PRESIGNED_TRANSFER_PROTOCOL } from "../../src/application/ports/browser-transfer/presigned-transfer.ts"; import { createPresignedCapabilityVault } from "../../src/adapters/browser-transfer/presigned/presigned-capability-vault.ts"; import { sha256Hex } from "../../src/adapters/browser-transfer/presigned/incremental-sha256.ts"; import { CHECKSUM_HEADER, CONTROL_ENDPOINT, DATA_ORIGIN, DIGEST_HEADER, DOWNLOAD_HREF, DOWNLOAD_PATH, NOW, POLICY_HEADER, REQUEST_BINDING_SHA256, UPLOAD_SESSION_ID, collect, createHarness, downloadCapabilityPayload, downloadResponse, jsonResponse, responseWithUrl, uploadCapabilityPayload, } from "./presigned-transfer-fixture.ts"; describe("presigned download stream lifecycle", () => { describe("TR-01 the stored capability is the one that was validated", () => { const baseRegistration = () => ({ protocol: PRESIGNED_TRANSFER_PROTOCOL, capabilityReceipt: "capability-snapshot-1", method: "GET" as const, binding: { kind: "DOWNLOAD" as const, resourceId: "resource-1" }, href: `${DATA_ORIGIN}${DOWNLOAD_PATH}`, origin: DATA_ORIGIN, path: DOWNLOAD_PATH, allowedQueryParameters: [], requestHeaders: [{ name: "x-safe", value: "1" }], requiredResponseHeaders: [], digestRequestHeader: null, digestResponseHeader: null, receiptResponseHeader: null, expectedStatus: 200, expectedResponseByteLength: 3, mediaType: "application/octet-stream", byteLength: 3, maxBytes: 3, expectedSha256: "a".repeat(64), expiresAtEpochMs: NOW + 60_000, }); const freshVault = () => createPresignedCapabilityVault({ now: () => NOW, maxActiveCapabilities: 4, }); it("refuses a header row that answers differently on a second read", () => { const vault = freshVault(); let nameReads = 0; const header = new Proxy( { name: "x-safe", value: "1" }, { getOwnPropertyDescriptor(target, key) { if (key === "name") { nameReads += 1; return { configurable: true, enumerable: true, value: nameReads > 1 ? "authorization" : "x-safe", }; } return Reflect.getOwnPropertyDescriptor(target, key); }, }, ); const registered = vault.register({ ...baseRegistration(), requestHeaders: [header], } as never); if (registered.ok) { // A single read means the value that was checked is the value stored. const resolved = vault.resolve(registered.value); expect(resolved.ok).toBe(true); if (resolved.ok) { expect(resolved.value.requestHeaders.map((row) => row.name)).toEqual([ "x-safe", ]); } } vault.dispose(); }); const hostileRegistrations: readonly (readonly [string, () => unknown])[] = [ [ "an accessor field", () => Object.defineProperty(baseRegistration(), "href", { enumerable: true, get: () => `${DATA_ORIGIN}${DOWNLOAD_PATH}`, }), ], [ "an inherited field", () => Object.assign(Object.create({ injected: true }), baseRegistration()), ], [ "a symbol field", () => ({ ...baseRegistration(), [Symbol.for("injected")]: true }), ], [ "a non-enumerable own field", () => Object.defineProperty(baseRegistration(), "injected", { enumerable: false, value: true, }), ], [ "a throwing ownKeys trap", () => new Proxy(baseRegistration(), { ownKeys() { throw new TypeError("hostile ownKeys trap"); }, }), ], [ "a null header array", () => ({ ...baseRegistration(), requestHeaders: null }), ], [ "a non-iterable header array", () => ({ ...baseRegistration(), requestHeaders: { length: 1 } }), ], [ "a header row with an extra field", () => ({ ...baseRegistration(), requestHeaders: [{ name: "x-safe", value: "1", injected: true }], }), ], [ "an accessor header name", () => ({ ...baseRegistration(), requestHeaders: [ Object.defineProperty({ value: "1" }, "name", { enumerable: true, get: () => "x-safe", }), ], }), ], [ "a binding with an extra field", () => ({ ...baseRegistration(), binding: { kind: "DOWNLOAD", resourceId: "r", injected: true }, }), ], [ "a null binding", () => ({ ...baseRegistration(), binding: null }), ], ]; for (const [label, build] of hostileRegistrations) { it(`rejects ${label} as POLICY_REJECTED`, () => { const vault = freshVault(); expect(vault.register(build() as never)).toMatchObject({ ok: false, error: { code: "POLICY_REJECTED" }, }); vault.dispose(); }); } it("does not observe a mutation of the issuer's object after registration", () => { const vault = freshVault(); const registration = baseRegistration(); const registered = vault.register(registration as never); expect(registered.ok).toBe(true); if (!registered.ok) return; registration.requestHeaders[0]!.name = "authorization"; registration.expiresAtEpochMs = NOW + 999_999; const resolved = vault.resolve(registered.value); expect(resolved.ok).toBe(true); if (!resolved.ok) return; expect(resolved.value.requestHeaders.map((row) => row.name)).toEqual([ "x-safe", ]); expect(resolved.value.expiresAtEpochMs).toBe(NOW + 60_000); vault.dispose(); }); }); it("does not fetch a presigned download until stream consumption", async () => { const bytes = new Uint8Array([1, 2, 3]); const responsePayload = downloadCapabilityPayload(bytes); let downloadFetches = 0; const fetcher = vi.fn(async (input: RequestInfo | URL) => { if (String(input) === CONTROL_ENDPOINT) { return jsonResponse(responsePayload); } downloadFetches += 1; return downloadResponse(bytes.slice().buffer, responsePayload); }) as unknown as typeof fetch; const { provider, executor } = createHarness({ fetcher }); const signal = new AbortController().signal; const issued = await provider.issueDownload({ resourceId: "resource-1", signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; const opened = await executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; // BT-PRE-01. open() performs no network I/O. expect(downloadFetches).toBe(0); for await (const chunk of opened.value.stream(signal)) { expect(chunk.ok).toBe(true); } expect(downloadFetches).toBe(1); opened.value.close(); }); it("closes an unused download source without network I/O", async () => { const bytes = new Uint8Array([1, 2, 3]); const responsePayload = downloadCapabilityPayload(bytes); let downloadFetches = 0; const fetcher = vi.fn(async (input: RequestInfo | URL) => { if (String(input) === CONTROL_ENDPOINT) { return jsonResponse(responsePayload); } downloadFetches += 1; return downloadResponse(bytes.slice().buffer, responsePayload); }) as unknown as typeof fetch; const { provider, executor } = createHarness({ fetcher }); const signal = new AbortController().signal; const issued = await provider.issueDownload({ resourceId: "resource-1", signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; const opened = await executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; opened.value.close(); // close() is idempotent and never starts the transfer. opened.value.close(); expect(downloadFetches).toBe(0); // A stream after close is one terminal conflict, still without fetching. const results = []; for await (const chunk of opened.value.stream(signal)) { results.push(chunk); } expect(results).toMatchObject([ { ok: false, error: { code: "CONFLICT" } }, ]); expect(downloadFetches).toBe(0); }); it.each([ { name: "truncation", body: new Uint8Array([1, 2]), expectedCode: "INTEGRITY_FAILED", }, { name: "overrun", body: new Uint8Array([1, 2, 3, 4]), expectedCode: "INTEGRITY_FAILED", }, ])("closes $name as a terminal stream failure", async ({ body, expectedCode }) => { const declared = new Uint8Array([1, 2, 3]); const payload = downloadCapabilityPayload(declared); const fetcher = vi.fn(async (input: RequestInfo | URL) => String(input) === CONTROL_ENDPOINT ? jsonResponse(payload) : downloadResponse(body.slice().buffer, payload), ) as unknown as typeof fetch; const { provider, executor } = createHarness({ fetcher }); const issued = await provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; const opened = await executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal: new AbortController().signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; const results = await collect(opened.value); expect(results.at(-1)).toMatchObject({ ok: false, error: { code: expectedCode }, }); const firstFailure = results.findIndex((result) => !result.ok); expect(results.slice(firstFailure + 1)).toEqual([]); }); it("closes native body errors without throwing across the port", async () => { const bytes = new Uint8Array([1, 2, 3]); const payload = downloadCapabilityPayload(bytes); const failingBody = new ReadableStream({ pull(controller) { controller.error(new DOMException("secret native detail", "NetworkError")); }, }); const fetcher = vi.fn(async (input: RequestInfo | URL) => String(input) === CONTROL_ENDPOINT ? jsonResponse(payload) : downloadResponse(failingBody, payload), ) as unknown as typeof fetch; const { provider, executor } = createHarness({ fetcher }); const issued = await provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; const opened = await executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal: new AbortController().signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; await expect(collect(opened.value)).resolves.toMatchObject([ { ok: false, error: { code: "NOT_READABLE", recovery: "REISSUE_CAPABILITY", }, }, ]); }); it("closes active abort and timeout without leaking native rejection", async () => { const bytes = new Uint8Array([1]); const payload = downloadCapabilityPayload(bytes); const neverBody = () => new ReadableStream({ pull() {} }); const fetcher = vi.fn(async (input: RequestInfo | URL) => String(input) === CONTROL_ENDPOINT ? jsonResponse(payload) : downloadResponse(neverBody(), payload), ) as unknown as typeof fetch; const controller = new AbortController(); let harness = createHarness({ fetcher }); let issued = await harness.provider.issueDownload({ resourceId: "resource-1", signal: controller.signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; let opened = await harness.executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal: controller.signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; const aborted = collect(opened.value, controller.signal); controller.abort("user"); expect(await aborted).toMatchObject([ { ok: false, error: { code: "ABORTED" } }, ]); let timeoutCallback: (() => void) | undefined; const scheduler = { setTimeout(callback: () => void) { timeoutCallback = callback; return 1; }, clearTimeout() {}, }; harness = createHarness({ fetcher, scheduler }); issued = await harness.provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; opened = await harness.executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal: new AbortController().signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; const timedOut = collect(opened.value); timeoutCallback?.(); expect(await timedOut).toMatchObject([ { ok: false, error: { code: "UNAVAILABLE", recovery: "REISSUE_CAPABILITY", }, }, ]); }); it("rejects an expired capability before data-plane fetch", async () => { const bytes = new Uint8Array([1]); const payload = downloadCapabilityPayload(bytes); let current = NOW; const fetcher = vi.fn(async (input: RequestInfo | URL) => String(input) === CONTROL_ENDPOINT ? jsonResponse(payload) : downloadResponse(bytes.slice().buffer, payload), ) as unknown as typeof fetch; const { provider, executor } = createHarness({ fetcher, now: () => current, }); const issued = await provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; current = Number(payload.expiresAtEpochMs) + 1; expect( await executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal: new AbortController().signal, }), ).toMatchObject({ ok: false, error: { code: "EXPIRED_RESOURCE", recovery: "REISSUE_CAPABILITY", }, }); expect(fetcher).toHaveBeenCalledTimes(1); }); it("rejects capabilities below the configured minimum remaining lifetime", async () => { const bytes = new Uint8Array([1]); const nearExpiryPayload = downloadCapabilityPayload(bytes, { expiresAtEpochMs: NOW + 999, }); let fetcher = vi.fn(async () => jsonResponse(nearExpiryPayload), ) as unknown as typeof fetch; let harness = createHarness({ fetcher }); expect( await harness.provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }), ).toMatchObject({ ok: false, error: { code: "EXPIRED_RESOURCE", recovery: "REISSUE_CAPABILITY", }, }); const acceptedPayload = downloadCapabilityPayload(bytes, { expiresAtEpochMs: NOW + 2_000, }); let current = NOW; fetcher = vi.fn(async (input: RequestInfo | URL) => String(input) === CONTROL_ENDPOINT ? jsonResponse(acceptedPayload) : downloadResponse(bytes.slice().buffer, acceptedPayload), ) as unknown as typeof fetch; harness = createHarness({ fetcher, now: () => current, }); const issued = await harness.provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }); expect(issued.ok).toBe(true); if (!issued.ok) return; current = NOW + 1_001; expect( await harness.executor.downloadSources.open({ resourceId: "resource-1", capability: issued.value, signal: new AbortController().signal, }), ).toMatchObject({ ok: false, error: { code: "EXPIRED_RESOURCE", recovery: "REISSUE_CAPABILITY", }, }); expect(fetcher).toHaveBeenCalledTimes(1); }); it("closes malformed AbortSignal inputs at every public boundary", async () => { const bytes = new Uint8Array([1, 2]); const payload = downloadCapabilityPayload(bytes); const uploadChecksum = sha256Hex(bytes); const uploadPayload = uploadCapabilityPayload({ bytes, checksum: uploadChecksum, }); const fetcher = vi.fn( async (input: RequestInfo | URL, init?: RequestInit) => { if (String(input) === CONTROL_ENDPOINT) { const request = JSON.parse(String(init?.body)) as { method: string; }; return jsonResponse( request.method === "GET" ? payload : uploadPayload, ); } if (String(input) === DOWNLOAD_HREF) { return downloadResponse(bytes.slice().buffer, payload); } return responseWithUrl( new Response(null, { status: 200, headers: { [POLICY_HEADER]: "v1", "Content-Length": "0", ETag: "\"part-etag-1\"", }, }), String(uploadPayload.href), ); }, ) as unknown as typeof fetch; const { provider, executor } = createHarness({ fetcher }); const malformed = {} as AbortSignal; expect( await provider.issueDownload({ resourceId: "resource-1", signal: malformed, }), ).toMatchObject({ ok: false, error: { code: "INVALID_INPUT" }, }); expect( await provider.issueUploadPart({ sessionId: UPLOAD_SESSION_ID, requestBindingSha256: REQUEST_BINDING_SHA256, uploadBindingSha256: "b".repeat(64), partNumber: 1, offset: 0, byteLength: bytes.byteLength, checksumSha256: uploadChecksum, mediaType: "application/octet-stream", idempotencyKey: "part-attempt-1", signal: malformed, }), ).toMatchObject({ ok: false, error: { code: "INVALID_INPUT" }, }); const issuedDownload = await provider.issueDownload({ resourceId: "resource-1", signal: new AbortController().signal, }); expect(issuedDownload.ok).toBe(true); if (!issuedDownload.ok) return; expect( await executor.downloadSources.open({ resourceId: "resource-1", capability: issuedDownload.value, signal: malformed, }), ).toMatchObject({ ok: false, error: { code: "INVALID_INPUT" }, }); const opened = await executor.downloadSources.open({ resourceId: "resource-1", capability: issuedDownload.value, signal: new AbortController().signal, }); expect(opened.ok).toBe(true); if (!opened.ok) return; expect(await collect(opened.value, malformed)).toMatchObject([ { ok: false, error: { code: "INVALID_INPUT" } }, ]); expect(await collect(opened.value)).toMatchObject([ { ok: false, error: { code: "CONFLICT" } }, ]); const issuedUpload = await provider.issueUploadPart({ sessionId: UPLOAD_SESSION_ID, requestBindingSha256: REQUEST_BINDING_SHA256, uploadBindingSha256: "b".repeat(64), partNumber: 1, offset: 0, byteLength: bytes.byteLength, checksumSha256: uploadChecksum, mediaType: "application/octet-stream", idempotencyKey: "part-attempt-1", signal: new AbortController().signal, }); expect(issuedUpload.ok).toBe(true); if (!issuedUpload.ok) return; expect( await executor.uploadParts.put({ capability: issuedUpload.value, sessionId: UPLOAD_SESSION_ID, requestBindingSha256: REQUEST_BINDING_SHA256, uploadBindingSha256: "b".repeat(64), partNumber: 1, offset: 0, byteLength: bytes.byteLength, checksumSha256: uploadChecksum, idempotencyKey: "part-attempt-1", bytes, signal: malformed, }), ).toMatchObject({ ok: false, error: { code: "INVALID_INPUT" }, }); }); });