import { createHash } from "node:crypto"; import { expect, test } from "../support/browser/strict-browser-test.ts"; const RESOURCE_ID = "browser-artifact-1"; const OBJECT_ORIGIN = "https://objects.example.test"; const OBJECT_PATH = "/files/browser-artifact-1"; const OBJECT_HREF = `${OBJECT_ORIGIN}${OBJECT_PATH}?sig=opaque-browser-signature`; const DOWNLOAD_BYTES = Buffer.from([ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, ]); const DOWNLOAD_SHA256 = createHash("sha256") .update(DOWNLOAD_BYTES) .digest("hex"); test("issues an opaque capability and streams a verified object into the writable save path", async ({ page, }) => { const bffRequests: unknown[] = []; const objectRequests: Readonly<{ url: string; method: string; accept: string | undefined; cookie: string | undefined; }>[] = []; await page.route("**/api/download-capability", async (route) => { bffRequests.push(route.request().postDataJSON()); await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ capabilityReceipt: "browser-download-capability-1", method: "GET", binding: { kind: "DOWNLOAD", resourceId: RESOURCE_ID, }, href: OBJECT_HREF, origin: OBJECT_ORIGIN, path: OBJECT_PATH, allowedQueryParameters: ["sig"], requestHeaders: [ { name: "accept", value: "application/octet-stream", }, ], requiredResponseHeaders: [ { name: "x-policy-version", value: "v1", }, ], digestRequestHeader: null, digestResponseHeader: "x-content-sha256", receiptResponseHeader: null, expectedStatus: 200, expectedResponseByteLength: null, mediaType: "application/octet-stream", byteLength: DOWNLOAD_BYTES.byteLength, maxBytes: 64, expectedSha256: DOWNLOAD_SHA256, expiresAtEpochMs: Date.now() + 5 * 60_000, singleUse: true, }), }); }); await page.route(`${OBJECT_ORIGIN}/**`, async (route) => { const headers = route.request().headers(); objectRequests.push({ url: route.request().url(), method: route.request().method(), accept: headers.accept, cookie: headers.cookie, }); await route.fulfill({ status: 200, body: DOWNLOAD_BYTES, headers: { "access-control-allow-origin": "*", "access-control-expose-headers": "content-length, x-content-sha256, x-policy-version", "content-length": String(DOWNLOAD_BYTES.byteLength), "content-type": "application/octet-stream", "x-content-sha256": DOWNLOAD_SHA256, "x-policy-version": "v1", }, }); }); await page.goto("/"); const result = await page.evaluate( async ({ resourceId }) => { const vaultModulePath = "/src/adapters/browser-transfer/presigned/presigned-capability-vault.ts"; const providerModulePath = "/src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts"; const executorModulePath = "/src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts"; const policyModulePath = "/src/adapters/browser-files/browser-file-policy-registry.ts"; const deliveryModulePath = "/src/adapters/browser-files/download-delivery-adapter.ts"; const [ { createPresignedCapabilityVault, createSingleUsePresignedReplayGuard, }, { createPresignedCapabilityHttpProvider }, { createPresignedTransferExecutor }, { BrowserFilePolicyRegistry, browserFilePolicyReference, }, { createDownloadDeliveryAdapter }, ] = await Promise.all([ import( /* @vite-ignore */ vaultModulePath ) as Promise< typeof import("../../src/adapters/browser-transfer/presigned/presigned-capability-vault.ts") >, import( /* @vite-ignore */ providerModulePath ) as Promise< typeof import("../../src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts") >, import( /* @vite-ignore */ executorModulePath ) as Promise< typeof import("../../src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts") >, import( /* @vite-ignore */ policyModulePath ) as Promise< typeof import("../../src/adapters/browser-files/browser-file-policy-registry.ts") >, import( /* @vite-ignore */ deliveryModulePath ) as Promise< typeof import("../../src/adapters/browser-files/download-delivery-adapter.ts") >, ]); const vault = createPresignedCapabilityVault({ maxActiveCapabilities: 4, }); const provider = createPresignedCapabilityHttpProvider({ endpoint: `${location.origin}/api/download-capability`, vault, allowedDataOrigins: ["https://objects.example.test"], allowedDataPathPrefixes: ["/files/"], allowedQueryParameters: ["sig"], allowedRequestHeaders: ["accept"], allowedResponseHeaders: [ "x-content-sha256", "x-policy-version", ], hardMaxTransferBytes: 64, hardMaxUploadResponseBytes: 1_024, maxCapabilityTtlMs: 10 * 60_000, minimumRemainingLifetimeMs: 30_000, timeoutMs: 5_000, allowInsecureLocalhost: true, }); const executor = createPresignedTransferExecutor({ vault, replayGuard: createSingleUsePresignedReplayGuard(), hardMaxTransferBytes: 64, hardMaxChunkBytes: 3, hardMaxUploadResponseBytes: 1_024, minimumRemainingLifetimeMs: 30_000, timeoutMs: 5_000, }); const signal = new AbortController().signal; const issued = await provider.issueDownload({ resourceId, signal, }); if (!issued.ok) { vault.dispose(); return { issued, delivery: null, capabilityKeys: [], capabilityBindingKeys: [], written: [], writeChunkSizes: [], writableClosed: false, writableAborted: false, progressPhases: [], }; } const downloadPolicy = browserFilePolicyReference( "browser-presigned-download", "save-verified-artifact", ); const policies = new BrowserFilePolicyRegistry({ profiles: [ { reference: downloadPolicy, download: { strategy: "PROMPT_AND_STREAM", mediaType: "application/octet-stream", safeExtension: ".bin", maxTransferBytes: 64, maxBufferedBytes: 8, integrity: "REQUIRED", }, }, ], hardLimits: { maxInspectionBytes: 64, maxRetainedFileBytes: 64, maxPreviewBytes: 64, maxObjectUrlBytes: 64, maxTransferBytes: 64, }, }); const written: number[] = []; const writeChunkSizes: number[] = []; let writableClosed = false; let writableAborted = false; const downloads = createDownloadDeliveryAdapter({ host: { handoff() { throw new TypeError( "Writable delivery must not use browser handoff.", ); }, }, policies, hardMaxObjectUrlBytes: 64, hardMaxTransferBytes: 64, browserManagedCapabilities: { resolve() { throw new TypeError( "Browser-managed capability is not used.", ); }, }, openAuthorizedSource: executor.downloadSources.open.bind( executor.downloadSources, ), showSaveFilePicker: async () => ({ async createWritable() { return new WritableStream({ write(chunk) { writeChunkSizes.push(chunk.byteLength); written.push(...chunk); }, close() { writableClosed = true; }, abort() { writableAborted = true; }, }); }, }), createTransferId: () => "transfer:browser-presigned", progressMinIntervalMs: 0, userActivation: { isActive: true }, }); const progressPhases: string[] = []; const capabilityKeys = Object.keys(issued.value).sort(); const capabilityBindingKeys = Object.keys( issued.value.binding, ).sort(); const delivery = await downloads.deliver({ policy: downloadPolicy, source: { kind: "AUTHORIZED_STREAM_RESOURCE", resourceId, capability: issued.value, }, suggestedFileName: "browser-artifact.bin", signal, onProgress(progress) { progressPhases.push(progress.phase); }, }); downloads.dispose(); vault.dispose(); return { issued: { ok: true as const, exposedHref: "href" in issued.value, exposedRequestHeaders: "requestHeaders" in issued.value, }, delivery, capabilityKeys, capabilityBindingKeys, written, writeChunkSizes, writableClosed, writableAborted, progressPhases, }; }, { resourceId: RESOURCE_ID }, ); expect(bffRequests).toEqual([ { method: "GET", binding: { kind: "DOWNLOAD", resourceId: RESOURCE_ID, }, }, ]); expect(objectRequests).toEqual([ { url: OBJECT_HREF, method: "GET", accept: "application/octet-stream", cookie: undefined, }, ]); expect(result.issued).toEqual({ ok: true, exposedHref: false, exposedRequestHeaders: false, }); expect(result.capabilityKeys).toEqual([ "binding", "byteLength", "capabilityReceipt", "expectedSha256", "expiresAtEpochMs", "maxBytes", "mediaType", "method", ]); expect(result.capabilityBindingKeys).toEqual([ "kind", "resourceId", ]); expect(result.delivery).toEqual({ ok: true, value: { kind: "SAVED", transferId: "transfer:browser-presigned", bytesWritten: DOWNLOAD_BYTES.byteLength, integrity: "VERIFIED", }, }); expect(result.written).toEqual([...DOWNLOAD_BYTES]); expect(result.writeChunkSizes).toEqual([3, 3, 3, 1]); expect(result.writableClosed).toBe(true); expect(result.writableAborted).toBe(false); expect(result.progressPhases).toEqual([ "PREPARING", "TRANSFERRING", "TRANSFERRING", "TRANSFERRING", "TRANSFERRING", "VERIFYING", "FINALIZING", ]); });