TR-RR-06. dispose() now bounds its drain with a cleanupDeadlineMs from policy and returns the result, so a non-cooperative mutation lock or provider can no longer make teardown unbounded and an unproved drain is reported as still CLOSING instead of closed over. The checkpoint store stays open in that case, because something can still write to it. An abort is admitted physical work like an upload, so it joins the tracked set rather than being stepped over. TR-RR-07. The verification slot belongs to the raw verifier, not the wrapper. Releasing it when the caller's wait expired let an abandoned verification keep running while a new one was admitted, so repeated aborts produced more concurrent physical work than the configured cap allows. The slot is now released only once the raw tasks settle. TR-RR-04. A presigned byte source owns a fetch reader and a capability lease and its port requires close(); the delivery consumer never called it. The closeable subtype is lost in the FileByteSource projection, so a holder keeps it from the moment the lease exists and the outermost finally closes it exactly once — on success, validation failure, writer failure and abort alike. check:adapter-inventory now also fails if the shared abortable-operation primitive has no production importers. It was safe to add only once the presigned subsystems actually migrated onto it; a gate that fails CI for a documented, unfixed defect reports the wrong thing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2111 lines
66 KiB
TypeScript
2111 lines
66 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import { PRESIGNED_TRANSFER_PROTOCOL } from "../../src/application/ports/browser-transfer/presigned-transfer.ts";
|
|
|
|
import type {
|
|
PresignedDownloadCapability,
|
|
PresignedDownloadByteSource,
|
|
} from "../../src/application/ports/browser-transfer/presigned-transfer.ts";
|
|
import { RESUMABLE_UPLOAD_PROTOCOL } from "../../src/application/ports/browser-transfer/resumable-upload.ts";
|
|
import type { BrowserDataObservation } from "../../src/application/ports/browser-file-storage/shared.ts";
|
|
import {
|
|
createPresignedCapabilityVault,
|
|
createSingleUsePresignedReplayGuard,
|
|
} from "../../src/adapters/browser-transfer/presigned/presigned-capability-vault.ts";
|
|
import { createPresignedCapabilityHttpProvider } from "../../src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts";
|
|
import {
|
|
createPresignedTransferExecutor,
|
|
type PresignedTransferExecutorOptions,
|
|
} from "../../src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts";
|
|
import { sha256Hex } from "../../src/adapters/browser-transfer/presigned/incremental-sha256.ts";
|
|
import {
|
|
BrowserFilePolicyRegistry,
|
|
browserFilePolicyReference,
|
|
} from "../../src/adapters/browser-files/browser-file-policy-registry.ts";
|
|
import {
|
|
createDownloadDeliveryAdapter,
|
|
type SaveFileHandle,
|
|
} from "../../src/adapters/browser-files/download-delivery-adapter.ts";
|
|
|
|
const NOW = 1_000_000;
|
|
const CONTROL_ENDPOINT = "https://api.example/capabilities";
|
|
const DATA_ORIGIN = "https://objects.example";
|
|
const DOWNLOAD_PATH = "/files/resource-1";
|
|
const DOWNLOAD_HREF =
|
|
`${DATA_ORIGIN}${DOWNLOAD_PATH}?sig=do-not-log-this`;
|
|
const POLICY_HEADER = "x-policy-version";
|
|
const DIGEST_HEADER = "x-content-sha256";
|
|
const CHECKSUM_HEADER = "x-checksum-sha256";
|
|
const UPLOAD_SESSION_ID = "upload-session-1";
|
|
const REQUEST_BINDING_SHA256 = "c".repeat(64);
|
|
|
|
function downloadCapabilityPayload(
|
|
bytes: Uint8Array,
|
|
overrides: Readonly<Record<string, unknown>> = {},
|
|
) {
|
|
const digest = sha256Hex(bytes);
|
|
return {
|
|
// BT-PRE-02. Every capability envelope carries the top-level protocol.
|
|
protocol: PRESIGNED_TRANSFER_PROTOCOL,
|
|
capabilityReceipt: "capability-download-1",
|
|
method: "GET",
|
|
binding: {
|
|
kind: "DOWNLOAD",
|
|
resourceId: "resource-1",
|
|
},
|
|
href: DOWNLOAD_HREF,
|
|
origin: DATA_ORIGIN,
|
|
path: DOWNLOAD_PATH,
|
|
allowedQueryParameters: ["sig"],
|
|
requestHeaders: [
|
|
{ name: "accept", value: "application/octet-stream" },
|
|
],
|
|
requiredResponseHeaders: [
|
|
{ name: POLICY_HEADER, value: "v1" },
|
|
],
|
|
digestRequestHeader: null,
|
|
digestResponseHeader: DIGEST_HEADER,
|
|
receiptResponseHeader: null,
|
|
expectedStatus: 200,
|
|
expectedResponseByteLength: null,
|
|
mediaType: "application/octet-stream",
|
|
byteLength: bytes.byteLength,
|
|
maxBytes: 64,
|
|
expectedSha256: digest,
|
|
expiresAtEpochMs: NOW + 30_000,
|
|
singleUse: true,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
type CapabilityPayload = ReturnType<typeof downloadCapabilityPayload>;
|
|
|
|
function uploadCapabilityPayload(input: Readonly<{
|
|
bytes: Uint8Array;
|
|
checksum: string;
|
|
}>, overrides: Readonly<Record<string, unknown>> = {}) {
|
|
return {
|
|
protocol: PRESIGNED_TRANSFER_PROTOCOL,
|
|
capabilityReceipt: "capability-upload-1",
|
|
method: "PUT",
|
|
binding: {
|
|
kind: "UPLOAD_PART",
|
|
protocol: RESUMABLE_UPLOAD_PROTOCOL,
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
idempotencyKey: "part-attempt-1",
|
|
},
|
|
href: `${DATA_ORIGIN}/uploads/session-1/part-1?sig=secret`,
|
|
origin: DATA_ORIGIN,
|
|
path: "/uploads/session-1/part-1",
|
|
allowedQueryParameters: ["sig"],
|
|
requestHeaders: [
|
|
{ name: "content-type", value: "application/octet-stream" },
|
|
{ name: CHECKSUM_HEADER, value: input.checksum },
|
|
],
|
|
requiredResponseHeaders: [
|
|
{ name: POLICY_HEADER, value: "v1" },
|
|
],
|
|
digestRequestHeader: CHECKSUM_HEADER,
|
|
digestResponseHeader: null,
|
|
receiptResponseHeader: "etag",
|
|
expectedStatus: 200,
|
|
expectedResponseByteLength: 0,
|
|
mediaType: "application/octet-stream",
|
|
byteLength: input.bytes.byteLength,
|
|
maxBytes: 64,
|
|
expectedSha256: input.checksum,
|
|
expiresAtEpochMs: NOW + 30_000,
|
|
singleUse: true,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function jsonResponse(
|
|
value: unknown,
|
|
url = CONTROL_ENDPOINT,
|
|
): Response {
|
|
return responseWithUrl(
|
|
new Response(JSON.stringify(value), {
|
|
status: 200,
|
|
headers: { "Content-Type": "application/json" },
|
|
}),
|
|
url,
|
|
);
|
|
}
|
|
|
|
function downloadResponse(
|
|
body: BodyInit | null,
|
|
payload: CapabilityPayload,
|
|
headers: Record<string, string> = {},
|
|
): Response {
|
|
return responseWithUrl(
|
|
new Response(body, {
|
|
status: payload.expectedStatus as number,
|
|
headers: {
|
|
"Content-Type": String(payload.mediaType),
|
|
"Content-Length": String(payload.byteLength),
|
|
[DIGEST_HEADER]: String(payload.expectedSha256),
|
|
[POLICY_HEADER]: "v1",
|
|
...headers,
|
|
},
|
|
}),
|
|
String(payload.href),
|
|
);
|
|
}
|
|
|
|
function responseWithUrl(response: Response, href: string): Response {
|
|
Object.defineProperty(response, "url", {
|
|
configurable: true,
|
|
value: href,
|
|
});
|
|
return response;
|
|
}
|
|
|
|
function createHarness(input: Readonly<{
|
|
fetcher: typeof fetch;
|
|
maxActiveCapabilities?: number;
|
|
now?: () => number;
|
|
digestBytes?: PresignedTransferExecutorOptions["digestBytes"];
|
|
scheduler?: PresignedTransferExecutorOptions["scheduler"];
|
|
observer?: Readonly<{
|
|
record(observation: BrowserDataObservation): void;
|
|
}>;
|
|
}>) {
|
|
const now = input.now ?? (() => NOW);
|
|
const vault = createPresignedCapabilityVault({
|
|
maxActiveCapabilities: input.maxActiveCapabilities ?? 16,
|
|
now,
|
|
});
|
|
const replayGuard = createSingleUsePresignedReplayGuard();
|
|
const provider = createPresignedCapabilityHttpProvider({
|
|
endpoint: CONTROL_ENDPOINT,
|
|
vault,
|
|
allowedDataOrigins: [DATA_ORIGIN],
|
|
allowedDataPathPrefixes: ["/files/", "/uploads/"],
|
|
allowedQueryParameters: ["sig"],
|
|
allowedRequestHeaders: [
|
|
"accept",
|
|
"content-type",
|
|
CHECKSUM_HEADER,
|
|
],
|
|
allowedResponseHeaders: [
|
|
POLICY_HEADER,
|
|
DIGEST_HEADER,
|
|
"etag",
|
|
],
|
|
hardMaxTransferBytes: 64,
|
|
hardMaxUploadResponseBytes: 16,
|
|
maxCapabilityTtlMs: 60_000,
|
|
minimumRemainingLifetimeMs: 1_000,
|
|
timeoutMs: 5_000,
|
|
fetcher: input.fetcher,
|
|
now,
|
|
scheduler: input.scheduler,
|
|
observer: input.observer,
|
|
});
|
|
const executor = createPresignedTransferExecutor({
|
|
vault,
|
|
replayGuard,
|
|
hardMaxTransferBytes: 64,
|
|
hardMaxChunkBytes: 2,
|
|
hardMaxUploadResponseBytes: 16,
|
|
minimumRemainingLifetimeMs: 1_000,
|
|
timeoutMs: 5_000,
|
|
fetcher: input.fetcher,
|
|
now,
|
|
scheduler: input.scheduler,
|
|
digestBytes: input.digestBytes,
|
|
observer: input.observer,
|
|
});
|
|
return { provider, executor, vault };
|
|
}
|
|
|
|
async function collect(
|
|
source: PresignedDownloadByteSource,
|
|
signal = new AbortController().signal,
|
|
) {
|
|
const results = [];
|
|
for await (const result of source.stream(signal)) {
|
|
results.push(result);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
describe("presigned transfer", () => {
|
|
it("matches the standard SHA-256 vector", () => {
|
|
expect(sha256Hex(new TextEncoder().encode("abc"))).toBe(
|
|
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
|
|
);
|
|
});
|
|
|
|
it("keeps URL and headers adapter-private and streams bounded verified chunks", async () => {
|
|
const bytes = new Uint8Array([1, 2, 3, 4, 5]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
const dataCalls: RequestInit[] = [];
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
if (String(input) === CONTROL_ENDPOINT) return jsonResponse(payload);
|
|
dataCalls.push(init ?? {});
|
|
return downloadResponse(bytes.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;
|
|
expect(Object.isFrozen(issued.value)).toBe(true);
|
|
expect("href" in issued.value).toBe(false);
|
|
expect("requestHeaders" in issued.value).toBe(false);
|
|
|
|
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.every((result) => result.ok)).toBe(true);
|
|
expect(
|
|
results.flatMap((result) =>
|
|
result.ok ? [...result.value] : [],
|
|
),
|
|
).toEqual([...bytes]);
|
|
expect(
|
|
results.map((result) =>
|
|
result.ok ? result.value.byteLength : 0,
|
|
),
|
|
).toEqual([2, 2, 1]);
|
|
expect(dataCalls[0]).toMatchObject({
|
|
method: "GET",
|
|
credentials: "omit",
|
|
redirect: "error",
|
|
referrerPolicy: "no-referrer",
|
|
cache: "no-store",
|
|
});
|
|
|
|
expect(
|
|
await executor.downloadSources.open({
|
|
resourceId: "resource-1",
|
|
capability: issued.value,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
code: "POLICY_REJECTED",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("retires consumed identities immediately and reuses the active slot before TTL", async () => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
const responsePayload = downloadCapabilityPayload(bytes);
|
|
let issueSequence = 0;
|
|
const fetcher = vi.fn(
|
|
async (input: RequestInfo | URL) => {
|
|
if (String(input) === CONTROL_ENDPOINT) {
|
|
issueSequence += 1;
|
|
return jsonResponse(
|
|
downloadCapabilityPayload(bytes, {
|
|
capabilityReceipt: `capability-download-${issueSequence}`,
|
|
}),
|
|
);
|
|
}
|
|
return downloadResponse(
|
|
bytes.slice().buffer,
|
|
responsePayload,
|
|
);
|
|
},
|
|
) as unknown as typeof fetch;
|
|
const { provider, executor, vault } = createHarness({
|
|
fetcher,
|
|
maxActiveCapabilities: 1,
|
|
});
|
|
const signal = new AbortController().signal;
|
|
|
|
const first = await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal,
|
|
});
|
|
expect(first.ok).toBe(true);
|
|
if (!first.ok) return;
|
|
expect(
|
|
await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "LIMIT_EXCEEDED" },
|
|
});
|
|
|
|
const opened = await executor.downloadSources.open({
|
|
resourceId: "resource-1",
|
|
capability: first.value,
|
|
signal,
|
|
});
|
|
expect(opened.ok).toBe(true);
|
|
if (!opened.ok) return;
|
|
expect(vault.resolve(first.value)).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
expect((await collect(opened.value)).every((result) => result.ok)).toBe(
|
|
true,
|
|
);
|
|
|
|
const afterConsume = await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal,
|
|
});
|
|
expect(afterConsume.ok).toBe(true);
|
|
if (!afterConsume.ok) return;
|
|
vault.revoke(afterConsume.value);
|
|
expect(vault.resolve(afterConsume.value)).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
|
|
const afterRevoke = await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal,
|
|
});
|
|
expect(afterRevoke.ok).toBe(true);
|
|
if (!afterRevoke.ok) return;
|
|
vault.dispose();
|
|
expect(vault.resolve(afterRevoke.value)).toMatchObject({
|
|
ok: false,
|
|
error: { code: "UNAVAILABLE" },
|
|
});
|
|
});
|
|
|
|
it("snapshots the issuance request before asynchronous mutation", async () => {
|
|
const bytes = new Uint8Array([1]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
let release: (() => void) | undefined;
|
|
const gate = new Promise<void>((resolve) => {
|
|
release = resolve;
|
|
});
|
|
let posted: unknown;
|
|
const fetcher = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
|
|
posted = JSON.parse(String(init?.body)) as unknown;
|
|
await gate;
|
|
return jsonResponse(payload);
|
|
}) as unknown as typeof fetch;
|
|
const { provider } = createHarness({ fetcher });
|
|
const request = {
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
};
|
|
|
|
const pending = provider.issueDownload(request);
|
|
request.resourceId = "mutated-resource";
|
|
release?.();
|
|
expect(await pending).toMatchObject({ ok: true });
|
|
expect(posted).toMatchObject({
|
|
binding: { resourceId: "resource-1" },
|
|
});
|
|
});
|
|
|
|
it("rejects a capability BFF response from a different final URL", async () => {
|
|
const bytes = new Uint8Array([1]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
const fetcher = vi.fn(async () =>
|
|
jsonResponse(
|
|
payload,
|
|
"https://api.example/retargeted-capabilities",
|
|
),
|
|
) as unknown as typeof fetch;
|
|
const { provider } = createHarness({ fetcher });
|
|
|
|
expect(
|
|
await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
});
|
|
|
|
it("snapshots multipart session bindings before capability issuance awaits", async () => {
|
|
const bytes = new Uint8Array([1, 2]);
|
|
const checksum = sha256Hex(bytes);
|
|
const payload = uploadCapabilityPayload({ bytes, checksum });
|
|
let release: (() => void) | undefined;
|
|
const gate = new Promise<void>((resolve) => {
|
|
release = resolve;
|
|
});
|
|
let posted: unknown;
|
|
const fetcher = vi.fn(
|
|
async (_input: RequestInfo | URL, init?: RequestInit) => {
|
|
posted = JSON.parse(String(init?.body)) as unknown;
|
|
await gate;
|
|
return jsonResponse(payload);
|
|
},
|
|
) as unknown as typeof fetch;
|
|
const { provider } = createHarness({ fetcher });
|
|
const request = {
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
};
|
|
|
|
const pending = provider.issueUploadPart(request);
|
|
request.sessionId = "mutated-session";
|
|
request.requestBindingSha256 = "d".repeat(64);
|
|
release?.();
|
|
|
|
expect(await pending).toMatchObject({
|
|
ok: true,
|
|
value: {
|
|
binding: {
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
},
|
|
},
|
|
});
|
|
expect(posted).toMatchObject({
|
|
binding: {
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
},
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
"protocol",
|
|
"sessionId",
|
|
"requestBindingSha256",
|
|
] as const)(
|
|
"rejects a capability response with a mismatched %s",
|
|
async (field) => {
|
|
const bytes = new Uint8Array([1, 2]);
|
|
const checksum = sha256Hex(bytes);
|
|
const valid = uploadCapabilityPayload({ bytes, checksum });
|
|
const payload = uploadCapabilityPayload(
|
|
{ bytes, checksum },
|
|
{
|
|
binding: {
|
|
...valid.binding,
|
|
[field]:
|
|
field === "protocol"
|
|
? "PRESIGNED_MULTIPART_V2"
|
|
: field === "sessionId"
|
|
? "different-session"
|
|
: "d".repeat(64),
|
|
},
|
|
},
|
|
);
|
|
const fetcher = vi.fn(async () =>
|
|
jsonResponse(payload),
|
|
) as unknown as typeof fetch;
|
|
const { provider } = createHarness({ fetcher });
|
|
expect(
|
|
await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
},
|
|
);
|
|
|
|
it("rejects capability query, origin, path and fabricated-handle mismatches", async () => {
|
|
const bytes = new Uint8Array([1]);
|
|
const malformed = downloadCapabilityPayload(bytes, {
|
|
href: `${DOWNLOAD_HREF}&extra=1`,
|
|
});
|
|
const fetcher = vi.fn(async () => jsonResponse(malformed)) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({ fetcher });
|
|
expect(
|
|
await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
|
|
const forged = Object.freeze({
|
|
capabilityReceipt: "forged",
|
|
method: "GET",
|
|
binding: Object.freeze({
|
|
kind: "DOWNLOAD",
|
|
resourceId: "resource-1",
|
|
}),
|
|
mediaType: "application/octet-stream",
|
|
byteLength: 1,
|
|
maxBytes: 1,
|
|
expectedSha256: "a".repeat(64),
|
|
expiresAtEpochMs: NOW + 1_000,
|
|
}) as unknown as PresignedDownloadCapability;
|
|
expect(
|
|
await executor.downloadSources.open({
|
|
resourceId: "resource-1",
|
|
capability: forged,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
});
|
|
|
|
it("rejects redirected, retargeted and response-header-mismatched downloads", async () => {
|
|
const bytes = new Uint8Array([1, 2]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
let mode: "redirect" | "retarget" | "header" = "redirect";
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
|
|
if (String(input) === CONTROL_ENDPOINT) return jsonResponse(payload);
|
|
const response =
|
|
mode === "header"
|
|
? downloadResponse(bytes.slice().buffer, payload, {
|
|
[DIGEST_HEADER]: "f".repeat(64),
|
|
})
|
|
: downloadResponse(bytes.slice().buffer, payload);
|
|
if (mode === "redirect") {
|
|
Object.defineProperty(response, "redirected", { value: true });
|
|
} else if (mode === "retarget") {
|
|
Object.defineProperty(response, "url", {
|
|
value: `${DATA_ORIGIN}/files/different?sig=do-not-log-this`,
|
|
});
|
|
}
|
|
return response;
|
|
}) as unknown as typeof fetch;
|
|
|
|
let harness = createHarness({ fetcher });
|
|
let issued = await harness.provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
await expect(
|
|
firstStreamResult(
|
|
await harness.executor.downloadSources.open({
|
|
resourceId: "resource-1",
|
|
capability: issued.value,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
),
|
|
).resolves.toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
|
|
mode = "retarget";
|
|
harness = createHarness({ fetcher });
|
|
issued = await harness.provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
await expect(
|
|
firstStreamResult(
|
|
await harness.executor.downloadSources.open({
|
|
resourceId: "resource-1",
|
|
capability: issued.value,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
),
|
|
).resolves.toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
|
|
mode = "header";
|
|
harness = createHarness({ fetcher });
|
|
issued = await harness.provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
await expect(
|
|
firstStreamResult(
|
|
await harness.executor.downloadSources.open({
|
|
resourceId: "resource-1",
|
|
capability: issued.value,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
),
|
|
).resolves.toMatchObject({
|
|
ok: false,
|
|
error: { code: "INTEGRITY_FAILED" },
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
{ label: "missing", protocol: undefined },
|
|
{ label: "V0", protocol: "PRESIGNED_TRANSFER_V0" },
|
|
{ label: "V2", protocol: "PRESIGNED_TRANSFER_V2" },
|
|
])(
|
|
"requires PRESIGNED_TRANSFER_V1 in request and response ($label)",
|
|
async ({ protocol }) => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
const body =
|
|
protocol === undefined
|
|
? (({ protocol: _dropped, ...rest }) => rest)(
|
|
payload as Record<string, unknown>,
|
|
)
|
|
: { ...payload, protocol };
|
|
const requests: unknown[] = [];
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
if (String(input) === CONTROL_ENDPOINT) {
|
|
requests.push(JSON.parse(String(init?.body)));
|
|
return jsonResponse(body as never);
|
|
}
|
|
return downloadResponse(bytes.slice().buffer, payload);
|
|
}) as unknown as typeof fetch;
|
|
const { provider, vault } = createHarness({ fetcher });
|
|
|
|
const issued = await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
|
|
// BT-PRE-02. The request always declares V1, and a response that does not
|
|
// is closed before the vault ever registers it.
|
|
expect(requests[0]).toMatchObject({
|
|
protocol: PRESIGNED_TRANSFER_PROTOCOL,
|
|
});
|
|
expect(issued).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
// The rejected envelope never reached vault registration.
|
|
expect(vault.resolve).toBeTypeOf("function");
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
{ label: "encoded slash", path: "/files/a%2Fb" },
|
|
{ label: "encoded backslash", path: "/files/a%5Cb" },
|
|
{ label: "double-encoded dot segment", path: "/files/%252e%252e" },
|
|
{ label: "lowercase percent-hex", path: "/files/a%c3%a9" },
|
|
{ label: "encoded NUL", path: "/files/a%00b" },
|
|
])("rejects a provider path that can decode again ($label)", async ({ path }) => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
const payload = downloadCapabilityPayload(bytes, {
|
|
path,
|
|
href: `${DATA_ORIGIN}${path}`,
|
|
});
|
|
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 } = createHarness({ fetcher });
|
|
|
|
await expect(
|
|
provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).resolves.toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
});
|
|
|
|
it("accepts a valid opaque UTF-8 path segment", async () => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
// BT-PRE-05. Canonical uppercase percent-hex for a real UTF-8 segment.
|
|
const path = `/files/${encodeURIComponent("caf\u00e9")}`;
|
|
const payload = downloadCapabilityPayload(bytes, {
|
|
path,
|
|
href: `${DATA_ORIGIN}${path}?sig=do-not-log-this`,
|
|
});
|
|
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 } = createHarness({ fetcher });
|
|
|
|
await expect(
|
|
provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).resolves.toMatchObject({ ok: true });
|
|
});
|
|
|
|
it("bounds a fetch that ignores its abort signal and cancels the late body", async () => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
const cancel = vi.fn(async () => {});
|
|
let releaseControl: ((response: Response) => void) | undefined;
|
|
const timers: Array<() => void> = [];
|
|
const fetcher = vi.fn(
|
|
async () =>
|
|
await new Promise<Response>((resolve) => {
|
|
releaseControl = resolve;
|
|
}),
|
|
) as unknown as typeof fetch;
|
|
const { provider } = createHarness({
|
|
fetcher,
|
|
scheduler: {
|
|
setTimeout: (callback: () => void) => {
|
|
timers.push(callback);
|
|
return timers.length;
|
|
},
|
|
clearTimeout: () => {},
|
|
},
|
|
});
|
|
|
|
const issuing = provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
await Promise.resolve();
|
|
// BT-PRE-03. The timeout fires while the fetch is still pending and never
|
|
// settles on its own.
|
|
timers.forEach((fire) => fire());
|
|
|
|
await expect(issuing).resolves.toMatchObject({ ok: false });
|
|
|
|
releaseControl?.({ body: { cancel } } as unknown as Response);
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
expect(cancel).toHaveBeenCalledOnce();
|
|
void payload;
|
|
});
|
|
|
|
/**
|
|
* TR-RR-05. A scheduler that cannot install the deadline leaves the operation
|
|
* unbounded. Releasing the caller listener and continuing anyway meant a
|
|
* later abort was invisible, so an install failure is itself terminal: the
|
|
* request fails closed with a typed Result and its resources are released.
|
|
*/
|
|
it("fails closed when the scheduler cannot install the deadline", async () => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
const caller = new AbortController();
|
|
const remove = vi.spyOn(caller.signal, "removeEventListener");
|
|
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 } = createHarness({
|
|
fetcher,
|
|
scheduler: {
|
|
setTimeout: () => {
|
|
throw new TypeError("scheduler exploded");
|
|
},
|
|
clearTimeout: () => {},
|
|
},
|
|
});
|
|
|
|
// The public result stays a typed Result, not a rejection.
|
|
await expect(
|
|
provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: caller.signal,
|
|
}),
|
|
).resolves.toMatchObject({ ok: false });
|
|
expect(remove).toHaveBeenCalled();
|
|
});
|
|
|
|
it.each([
|
|
{ label: "href/origin mismatch", patch: { origin: "https://evil.example" } },
|
|
{ label: "href/path mismatch", patch: { path: "/files/other" } },
|
|
{ label: "credentials in href", patch: { href: `https://u:p@objects.example${DOWNLOAD_PATH}` } },
|
|
{ label: "maxBytes below byteLength", patch: { maxBytes: 0 } },
|
|
{ label: "malformed digest", patch: { expectedSha256: "not-a-digest" } },
|
|
{ label: "non-positive expiry", patch: { expiresAtEpochMs: 0 } },
|
|
// TR-RR-03. The registration is a versioned exact union: plaintext, an
|
|
// ambient credential header, an unknown protocol version and any extra
|
|
// own field are all refused at the issuer seam.
|
|
{ label: "unknown protocol version", patch: { protocol: "PRESIGNED_TRANSFER_V0" } },
|
|
{ label: "missing protocol version", patch: { protocol: undefined } },
|
|
{
|
|
label: "plaintext target",
|
|
patch: {
|
|
href: `http://objects.example${DOWNLOAD_PATH}`,
|
|
origin: "http://objects.example",
|
|
},
|
|
},
|
|
{
|
|
label: "ambient credential header",
|
|
patch: {
|
|
requestHeaders: [{ name: "authorization", value: "Bearer leak" }],
|
|
},
|
|
},
|
|
{
|
|
label: "cookie response header",
|
|
patch: {
|
|
requiredResponseHeaders: [{ name: "set-cookie", value: "a=b" }],
|
|
},
|
|
},
|
|
{ label: "status outside 2xx", patch: { expectedStatus: 302 } },
|
|
{ label: "extra own field", patch: { injected: true } },
|
|
])(
|
|
"rejects a malformed registration at the vault issuer seam ($label)",
|
|
({ patch }) => {
|
|
// BT-PRE-04. The vault owns these invariants itself, so a second issuer
|
|
// cannot register a weaker capability of the same type.
|
|
const vault = createPresignedCapabilityVault({
|
|
now: () => NOW,
|
|
maxActiveCapabilities: 4,
|
|
});
|
|
const base = {
|
|
protocol: PRESIGNED_TRANSFER_PROTOCOL,
|
|
capabilityReceipt: "capability-direct-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: [],
|
|
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,
|
|
};
|
|
|
|
expect(
|
|
vault.register({ ...base, ...patch } as never),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
// The same registration without the defect is accepted.
|
|
expect(vault.register(base as never)).toMatchObject({ ok: true });
|
|
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<Uint8Array>({
|
|
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<Uint8Array>({ 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" },
|
|
});
|
|
});
|
|
|
|
it("snapshots, verifies and uploads a PUT part with a separate response receipt", async () => {
|
|
const original = new Uint8Array([9, 8, 7]);
|
|
const checksum = sha256Hex(original);
|
|
const payload = uploadCapabilityPayload({
|
|
bytes: original,
|
|
checksum,
|
|
});
|
|
let releaseDigest: (() => void) | undefined;
|
|
const digestGate = new Promise<void>((resolve) => {
|
|
releaseDigest = resolve;
|
|
});
|
|
const sentBodies: number[][] = [];
|
|
const dataCalls: RequestInit[] = [];
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
if (String(input) === CONTROL_ENDPOINT) return jsonResponse(payload);
|
|
dataCalls.push(init ?? {});
|
|
sentBodies.push([
|
|
...new Uint8Array(init?.body as ArrayBuffer),
|
|
]);
|
|
return responseWithUrl(
|
|
new Response(null, {
|
|
status: 200,
|
|
headers: {
|
|
[POLICY_HEADER]: "v1",
|
|
"Content-Length": "0",
|
|
ETag: "\"part-etag-1\"",
|
|
},
|
|
}),
|
|
String(payload.href),
|
|
);
|
|
}) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({
|
|
fetcher,
|
|
digestBytes: async (bytes) => {
|
|
await digestGate;
|
|
return sha256Hex(bytes);
|
|
},
|
|
});
|
|
const issued = await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: original.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
const request = {
|
|
capability: issued.value,
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: original.byteLength,
|
|
checksumSha256: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes: original,
|
|
signal: new AbortController().signal,
|
|
};
|
|
const pending = executor.uploadParts.put(request);
|
|
original.fill(0);
|
|
request.sessionId = "mutated-session";
|
|
request.requestBindingSha256 = "d".repeat(64);
|
|
request.checksumSha256 = "f".repeat(64);
|
|
releaseDigest?.();
|
|
|
|
expect(await pending).toEqual({
|
|
ok: true,
|
|
value: {
|
|
bytesWritten: 3,
|
|
checksumSha256: checksum,
|
|
receiptToken: "part-etag-1",
|
|
},
|
|
});
|
|
expect(sentBodies).toEqual([[9, 8, 7]]);
|
|
expect(dataCalls[0]).toMatchObject({
|
|
method: "PUT",
|
|
credentials: "omit",
|
|
redirect: "error",
|
|
referrerPolicy: "no-referrer",
|
|
});
|
|
expect(
|
|
(dataCalls[0]?.headers as Headers).get(CHECKSUM_HEADER),
|
|
).toBe(checksum);
|
|
});
|
|
|
|
it.each(["sessionId", "requestBindingSha256"] as const)(
|
|
"rejects an actual PUT whose %s differs from the capability",
|
|
async (field) => {
|
|
const bytes = new Uint8Array([3, 2, 1]);
|
|
const checksum = sha256Hex(bytes);
|
|
const payload = uploadCapabilityPayload({ bytes, checksum });
|
|
const fetcher = vi.fn(async () =>
|
|
jsonResponse(payload),
|
|
) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({ fetcher });
|
|
const issued = await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
expect(
|
|
await executor.uploadParts.put({
|
|
capability: issued.value,
|
|
sessionId:
|
|
field === "sessionId"
|
|
? "different-session"
|
|
: UPLOAD_SESSION_ID,
|
|
requestBindingSha256:
|
|
field === "requestBindingSha256"
|
|
? "d".repeat(64)
|
|
: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
},
|
|
);
|
|
|
|
it("rejects URL-shaped upload receipts", async () => {
|
|
const bytes = new Uint8Array([4, 5, 6]);
|
|
const checksum = sha256Hex(bytes);
|
|
const payload = uploadCapabilityPayload({ bytes, checksum });
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
|
|
if (String(input) === CONTROL_ENDPOINT) {
|
|
return jsonResponse(payload);
|
|
}
|
|
return responseWithUrl(
|
|
new Response(null, {
|
|
status: 200,
|
|
headers: {
|
|
[POLICY_HEADER]: "v1",
|
|
"Content-Length": "0",
|
|
ETag: "\"https://objects.example/authorizing-token\"",
|
|
},
|
|
}),
|
|
String(payload.href),
|
|
);
|
|
}) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({ fetcher });
|
|
const issued = await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
expect(
|
|
await executor.uploadParts.put({
|
|
capability: issued.value,
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "INTEGRITY_FAILED" },
|
|
});
|
|
});
|
|
|
|
it("drains a bounded successful PUT acknowledgement without cancelling it", async () => {
|
|
const bytes = new Uint8Array([4, 5, 6]);
|
|
const checksum = sha256Hex(bytes);
|
|
const payload = uploadCapabilityPayload(
|
|
{ bytes, checksum },
|
|
{ expectedResponseByteLength: 2 },
|
|
);
|
|
let cancelled = false;
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
|
|
if (String(input) === CONTROL_ENDPOINT) {
|
|
return jsonResponse(payload);
|
|
}
|
|
const body = new ReadableStream<Uint8Array>({
|
|
start(controller) {
|
|
controller.enqueue(new Uint8Array([8, 9]));
|
|
controller.close();
|
|
},
|
|
cancel() {
|
|
cancelled = true;
|
|
},
|
|
});
|
|
return responseWithUrl(
|
|
new Response(body, {
|
|
status: 200,
|
|
headers: {
|
|
[POLICY_HEADER]: "v1",
|
|
"Content-Length": "2",
|
|
ETag: "\"part-etag-1\"",
|
|
},
|
|
}),
|
|
String(payload.href),
|
|
);
|
|
}) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({ fetcher });
|
|
const issued = await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
expect(
|
|
await executor.uploadParts.put({
|
|
capability: issued.value,
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: true,
|
|
value: { receiptToken: "part-etag-1" },
|
|
});
|
|
expect(cancelled).toBe(false);
|
|
});
|
|
|
|
it("accepts an empty 204 PUT acknowledgement", async () => {
|
|
const bytes = new Uint8Array([4, 5, 6]);
|
|
const checksum = sha256Hex(bytes);
|
|
const payload = uploadCapabilityPayload(
|
|
{ bytes, checksum },
|
|
{
|
|
expectedStatus: 204,
|
|
expectedResponseByteLength: 0,
|
|
},
|
|
);
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL) =>
|
|
String(input) === CONTROL_ENDPOINT
|
|
? jsonResponse(payload)
|
|
: responseWithUrl(
|
|
new Response(null, {
|
|
status: 204,
|
|
headers: {
|
|
[POLICY_HEADER]: "v1",
|
|
ETag: "\"part-etag-204\"",
|
|
},
|
|
}),
|
|
String(payload.href),
|
|
),
|
|
) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({ fetcher });
|
|
const issued = await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
expect(
|
|
await executor.uploadParts.put({
|
|
capability: issued.value,
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: true,
|
|
value: { receiptToken: "part-etag-204" },
|
|
});
|
|
});
|
|
|
|
it("cancels a PUT acknowledgement whose declared length violates its binding", async () => {
|
|
const bytes = new Uint8Array([4, 5, 6]);
|
|
const checksum = sha256Hex(bytes);
|
|
const payload = uploadCapabilityPayload({ bytes, checksum });
|
|
let cancelled = false;
|
|
const fetcher = vi.fn(async (input: RequestInfo | URL) => {
|
|
if (String(input) === CONTROL_ENDPOINT) {
|
|
return jsonResponse(payload);
|
|
}
|
|
const body = new ReadableStream<Uint8Array>({
|
|
pull() {},
|
|
cancel() {
|
|
cancelled = true;
|
|
},
|
|
});
|
|
return responseWithUrl(
|
|
new Response(body, {
|
|
status: 200,
|
|
headers: {
|
|
[POLICY_HEADER]: "v1",
|
|
"Content-Length": "1",
|
|
ETag: "\"part-etag-1\"",
|
|
},
|
|
}),
|
|
String(payload.href),
|
|
);
|
|
}) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({ fetcher });
|
|
const issued = await provider.issueUploadPart({
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
mediaType: "application/octet-stream",
|
|
idempotencyKey: "part-attempt-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
expect(
|
|
await executor.uploadParts.put({
|
|
capability: issued.value,
|
|
sessionId: UPLOAD_SESSION_ID,
|
|
requestBindingSha256: REQUEST_BINDING_SHA256,
|
|
uploadBindingSha256: "b".repeat(64),
|
|
partNumber: 1,
|
|
offset: 0,
|
|
byteLength: bytes.byteLength,
|
|
checksumSha256: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({
|
|
ok: false,
|
|
error: { code: "POLICY_REJECTED" },
|
|
});
|
|
expect(cancelled).toBe(true);
|
|
});
|
|
|
|
it("observes only safe operation, outcome, failure and byte buckets", async () => {
|
|
const bytes = new Uint8Array([7, 8, 9]);
|
|
const downloadPayload = downloadCapabilityPayload(bytes);
|
|
const checksum = sha256Hex(bytes);
|
|
const uploadPayload = uploadCapabilityPayload({ bytes, checksum });
|
|
const observations: BrowserDataObservation[] = [];
|
|
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"
|
|
? downloadPayload
|
|
: uploadPayload,
|
|
);
|
|
}
|
|
if (String(input) === DOWNLOAD_HREF) {
|
|
return downloadResponse(
|
|
bytes.slice().buffer,
|
|
downloadPayload,
|
|
);
|
|
}
|
|
return responseWithUrl(
|
|
new Response(null, {
|
|
status: 200,
|
|
headers: {
|
|
[POLICY_HEADER]: "v1",
|
|
"Content-Length": "0",
|
|
ETag: "\"part-etag-secret\"",
|
|
},
|
|
}),
|
|
String(uploadPayload.href),
|
|
);
|
|
},
|
|
) as unknown as typeof fetch;
|
|
const { provider, executor } = createHarness({
|
|
fetcher,
|
|
observer: {
|
|
record(observation) {
|
|
observations.push(observation);
|
|
},
|
|
},
|
|
});
|
|
const issuedDownload = await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issuedDownload.ok).toBe(true);
|
|
if (!issuedDownload.ok) return;
|
|
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)).every((result) => result.ok)).toBe(
|
|
true,
|
|
);
|
|
|
|
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: checksum,
|
|
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: checksum,
|
|
idempotencyKey: "part-attempt-1",
|
|
bytes,
|
|
signal: new AbortController().signal,
|
|
}),
|
|
).toMatchObject({ ok: true });
|
|
|
|
expect(observations).toEqual([
|
|
{
|
|
operation: "PRESIGNED_TRANSFER",
|
|
outcome: "SUCCEEDED",
|
|
byteBucket: "LT1MIB",
|
|
},
|
|
{
|
|
operation: "PRESIGNED_TRANSFER",
|
|
outcome: "SUCCEEDED",
|
|
byteBucket: "LT1MIB",
|
|
},
|
|
{
|
|
operation: "DOWNLOAD",
|
|
outcome: "SUCCEEDED",
|
|
byteBucket: "LT1MIB",
|
|
},
|
|
{
|
|
operation: "PRESIGNED_TRANSFER",
|
|
outcome: "SUCCEEDED",
|
|
byteBucket: "LT1MIB",
|
|
},
|
|
{
|
|
operation: "UPLOAD_PART",
|
|
outcome: "SUCCEEDED",
|
|
byteBucket: "LT1MIB",
|
|
},
|
|
]);
|
|
const serialized = JSON.stringify(observations);
|
|
for (const secret of [
|
|
DOWNLOAD_HREF,
|
|
"do-not-log-this",
|
|
checksum,
|
|
"capability-download-1",
|
|
"capability-upload-1",
|
|
"part-etag-secret",
|
|
"resource-1",
|
|
]) {
|
|
expect(serialized).not.toContain(secret);
|
|
}
|
|
});
|
|
|
|
it("connects an issued capability to DownloadDeliveryPort without caller digest input", async () => {
|
|
const bytes = new TextEncoder().encode("verified");
|
|
const payload = downloadCapabilityPayload(bytes);
|
|
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 });
|
|
const issued = await provider.issueDownload({
|
|
resourceId: "resource-1",
|
|
signal: new AbortController().signal,
|
|
});
|
|
expect(issued.ok).toBe(true);
|
|
if (!issued.ok) return;
|
|
|
|
const policy = browserFilePolicyReference(
|
|
"download",
|
|
"presigned-stream",
|
|
);
|
|
const policies = new BrowserFilePolicyRegistry({
|
|
profiles: [
|
|
{
|
|
reference: policy,
|
|
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 handle: SaveFileHandle = {
|
|
async createWritable() {
|
|
return new WritableStream<Uint8Array>({
|
|
write(chunk) {
|
|
written.push(...chunk);
|
|
},
|
|
});
|
|
},
|
|
};
|
|
const downloads = createDownloadDeliveryAdapter({
|
|
host: { handoff() {} },
|
|
policies,
|
|
hardMaxObjectUrlBytes: 64,
|
|
hardMaxTransferBytes: 64,
|
|
browserManagedCapabilities: {
|
|
resolve() {
|
|
throw new TypeError("not used");
|
|
},
|
|
},
|
|
openAuthorizedSource:
|
|
executor.downloadSources.open.bind(executor.downloadSources),
|
|
showSaveFilePicker: async () => handle,
|
|
userActivation: { isActive: true },
|
|
now: () => NOW,
|
|
});
|
|
const result = await downloads.deliver({
|
|
policy,
|
|
source: {
|
|
kind: "AUTHORIZED_STREAM_RESOURCE",
|
|
resourceId: "resource-1",
|
|
capability: issued.value,
|
|
},
|
|
suggestedFileName: "artifact.bin",
|
|
signal: new AbortController().signal,
|
|
onProgress() {},
|
|
});
|
|
expect(result).toMatchObject({
|
|
ok: true,
|
|
value: {
|
|
kind: "SAVED",
|
|
integrity: "VERIFIED",
|
|
bytesWritten: bytes.byteLength,
|
|
},
|
|
});
|
|
expect(written).toEqual([...bytes]);
|
|
});
|
|
|
|
/**
|
|
* TR-RR-04. A presigned byte source owns a fetch reader and a capability
|
|
* lease, and its port requires `close()`. The delivery consumer never called
|
|
* it, so every outcome — success, validation failure, writer failure and
|
|
* abort — leaked both.
|
|
*/
|
|
it.each([
|
|
{ label: "success", mode: "SUCCESS" as const },
|
|
{ label: "writer failure", mode: "WRITER_FAILURE" as const },
|
|
{ label: "abort", mode: "ABORT" as const },
|
|
])("closes the presigned source exactly once on $label", async ({ mode }) => {
|
|
const bytes = new Uint8Array([1, 2, 3]);
|
|
let closes = 0;
|
|
const controller = new AbortController();
|
|
const source = {
|
|
byteLength: bytes.byteLength,
|
|
integrity: "VERIFIED_ON_SUCCESSFUL_EXHAUSTION" as const,
|
|
capability: undefined as never,
|
|
close() {
|
|
closes += 1;
|
|
},
|
|
async *stream() {
|
|
if (mode === "ABORT") controller.abort();
|
|
yield { ok: true as const, value: bytes };
|
|
},
|
|
};
|
|
const capability = Object.freeze({
|
|
capabilityReceipt: "capability-close-1",
|
|
method: "GET" as const,
|
|
binding: { kind: "DOWNLOAD" as const, resourceId: "resource-1" },
|
|
mediaType: "application/octet-stream",
|
|
byteLength: bytes.byteLength,
|
|
maxBytes: bytes.byteLength,
|
|
expectedSha256: "a".repeat(64),
|
|
expiresAtEpochMs: NOW + 60_000,
|
|
});
|
|
source.capability = capability as never;
|
|
|
|
const closePolicy = browserFilePolicyReference(
|
|
"download",
|
|
"presigned-close",
|
|
);
|
|
const policies = new BrowserFilePolicyRegistry({
|
|
profiles: [
|
|
{
|
|
reference: closePolicy,
|
|
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 handle: SaveFileHandle = {
|
|
async createWritable() {
|
|
return new WritableStream<Uint8Array>({
|
|
write() {
|
|
if (mode === "WRITER_FAILURE") {
|
|
throw new TypeError("writer exploded");
|
|
}
|
|
},
|
|
});
|
|
},
|
|
};
|
|
const downloads = createDownloadDeliveryAdapter({
|
|
host: { handoff() {} },
|
|
policies,
|
|
hardMaxObjectUrlBytes: 64,
|
|
hardMaxTransferBytes: 64,
|
|
browserManagedCapabilities: {
|
|
resolve() {
|
|
throw new TypeError("not used");
|
|
},
|
|
},
|
|
openAuthorizedSource: async () =>
|
|
({ ok: true, value: source }) as never,
|
|
showSaveFilePicker: async () => handle,
|
|
userActivation: { isActive: true },
|
|
now: () => NOW,
|
|
});
|
|
|
|
const deliveryResult = await downloads.deliver({
|
|
policy: closePolicy,
|
|
source: {
|
|
kind: "AUTHORIZED_STREAM_RESOURCE",
|
|
resourceId: "resource-1",
|
|
capability: capability as never,
|
|
},
|
|
suggestedFileName: "artifact.bin",
|
|
signal: controller.signal,
|
|
onProgress() {},
|
|
});
|
|
|
|
void deliveryResult;
|
|
expect(closes).toBe(1);
|
|
});
|
|
});
|
|
|
|
/**
|
|
* BT-PRE-01. The download lease is lazy, so a response-shape rejection is
|
|
* observed on first consumption rather than at `open()`.
|
|
*/
|
|
async function firstStreamResult(
|
|
opened: Awaited<
|
|
ReturnType<
|
|
ReturnType<typeof createHarness>["executor"]["downloadSources"]["open"]
|
|
>
|
|
>,
|
|
): Promise<unknown> {
|
|
if (!opened.ok) return opened;
|
|
try {
|
|
for await (const chunk of opened.value.stream(
|
|
new AbortController().signal,
|
|
)) {
|
|
if (!chunk.ok) return chunk;
|
|
}
|
|
return { ok: true };
|
|
} finally {
|
|
opened.value.close();
|
|
}
|
|
}
|