Files
clean-architecture-frontend…/tests/unit/image-cdn-runtime.test.ts
T
DongHyeonkaandClaude Opus 5 5a76f95291 fix: bound resumable teardown, image concurrency and delivery leases
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>
2026-08-14 17:22:45 +09:00

2322 lines
65 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import type {
BackendIssuedImageAsset,
ImageCapabilityVerifier,
ImagePresetReference,
ImageProbeRequest,
PublicImmutableImageAsset,
} from "../../src/application/ports/browser-transfer/image-cdn.ts";
import {
createBrowserImageProbe,
type ImageProbeScheduler,
} from "../../src/adapters/browser-transfer/image-cdn/browser-image-probe.ts";
import {
IMAGE_CDN_IMPLEMENTATION_CEILINGS,
ImageCdnPolicyRegistry,
imageCdnPresetReference,
type ImageCdnHardLimits,
type ImageCdnPresetPolicy,
} from "../../src/adapters/browser-transfer/image-cdn/image-cdn-policy.ts";
import {
computeImageCapabilityBindingDigestHex,
createImageCdnRuntime,
DEFAULT_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS,
MAX_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS,
type ImageCapabilityVerificationScheduler,
} from "../../src/adapters/browser-transfer/image-cdn/image-cdn-runtime.ts";
import { createP256ImageCapabilityVerifier } from "../../src/adapters/browser-transfer/image-cdn/p256-image-capability-verifier.ts";
const hardLimits: ImageCdnHardLimits = Object.freeze({
maxIntrinsicWidth: 4_096,
maxIntrinsicHeight: 4_096,
maxSourcePixels: 16_777_216,
maxCssDimension: 2_048,
maxDpr: 2,
maxQuality: 90,
maxCandidateCount: 8,
maxTransformedPixels: 1_048_576,
maxDecodedBytes: 4_194_304,
maxEncodedBytes: 524_288,
maxUrlLength: 2_048,
maxCapabilityLifetimeMs: 3_600_000,
maxClockSkewMs: 60_000,
minCapabilityRemainingMs: 30_000,
maxPresetBindingsPerCapability: 8,
maxConcurrentCapabilityVerifications: 8,
allowedSourceMediaTypes: [
"image/avif",
"image/jpeg",
"image/png",
"image/webp",
] as const,
formatQualityCeilings: {
avif: 80,
jpeg: 85,
png: 90,
webp: 85,
},
});
function acceptsTestImageKey(keyId: string): boolean {
return keyId === "image-signing-2026-01";
}
function createPreset(
reference: ImagePresetReference,
overrides: Partial<ImageCdnPresetPolicy> = {},
): ImageCdnPresetPolicy {
return {
reference,
bindingId: "card-landscape-v1",
width: 640,
height: 360,
fit: "cover",
dprs: [2, 1],
responsiveWidths: [640, 320],
quality: 80,
formats: ["avif", "webp", "jpeg"],
sizes: "(max-width: 640px) 100vw, 640px",
loading: "eager",
decoding: "async",
fetchPriority: "high",
referrerPolicy: "strict-origin-when-cross-origin",
probeMode: "NONE",
allowUpscale: false,
maxTransformedPixels: 1_048_576,
maxDecodedBytes: 4_194_304,
maxEncodedBytes: 524_288,
...overrides,
};
}
function createPolicies(
presets: readonly ImageCdnPresetPolicy[],
overrides: Readonly<{
hardLimits?: ImageCdnHardLimits;
acceptedKeyIds?: readonly string[];
}> = {},
): ImageCdnPolicyRegistry {
return new ImageCdnPolicyRegistry({
applicationOrigin: "https://app.example.test",
origins: [
{
originKey: "product-images",
origin: "https://images.example.test",
assetPathPrefix: "/v1/assets/",
minimumPublicMaxAgeSeconds: 31_536_000,
},
],
presets,
hardLimits: overrides.hardLimits ?? hardLimits,
capability: {
issuer: "image-bff",
acceptedKeyIds: overrides.acceptedKeyIds ?? [
"image-signing-2026-01",
],
},
});
}
function publicAsset(
overrides: Partial<PublicImmutableImageAsset> = {},
): PublicImmutableImageAsset {
return {
kind: "ALLOWLISTED_PUBLIC",
originKey: "product-images",
assetId: "asset_Q3x8pL",
revision: "rev_a8N2kP4z",
mediaType: "image/jpeg",
contentKind: "RASTER_STATIC",
intrinsicWidth: 2_048,
intrinsicHeight: 1_152,
...overrides,
};
}
async function privateAsset(
overrides: Partial<BackendIssuedImageAsset> = {},
): Promise<BackendIssuedImageAsset> {
const base: BackendIssuedImageAsset = {
kind: "BACKEND_ISSUED_PRIVATE",
issuer: "image-bff",
originKey: "product-images",
assetId: "asset_Q3x8pL",
revision: "rev_a8N2kP4z",
mediaType: "image/jpeg",
contentKind: "RASTER_STATIC",
intrinsicWidth: 2_048,
intrinsicHeight: 1_152,
capabilityId: "cap_Z8m2Q7pR",
issuedAtEpochMs: 1_000_000,
expiresAtEpochMs: 1_300_000,
allowedPresetBindingIds: ["card-landscape-v1"],
signature: {
algorithm: "ECDSA_P256_SHA256",
keyId: "image-signing-2026-01",
capabilityBindingDigestHex: "0".repeat(64),
valueBase64Url: "A".repeat(86),
},
...overrides,
};
const descriptor = {
...base,
signature: {
...base.signature,
...(overrides.signature ?? {}),
},
};
const digest = await computeImageCapabilityBindingDigestHex(
globalThis.crypto.subtle,
descriptor,
);
return {
...descriptor,
signature: {
...descriptor.signature,
capabilityBindingDigestHex: digest,
},
};
}
describe("production image CDN runtime", () => {
it("builds sorted, duplicate-free responsive candidates from a named preset", async () => {
const preset = imageCdnPresetReference(
"card-landscape",
"render-public-product-image",
);
const runtime = createImageCdnRuntime({
policies: createPolicies([createPreset(preset)]),
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: vi.fn().mockResolvedValue(true),
},
});
const accepted = runtime.assets.acceptPublicImmutable(
publicAsset(),
);
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
if (!resolved.ok) return;
const widthDescriptors = resolved.value.srcSet
.split(", ")
.map((candidate) =>
Number(candidate.match(/ (\d+)w$/u)?.[1]),
);
expect(widthDescriptors).toEqual([320, 640, 1_280]);
expect(new Set(widthDescriptors).size).toBe(3);
expect(resolved.value.sources.map((source) => source.type)).toEqual(
["image/avif", "image/webp"],
);
expect(resolved.value.fallbackMediaType).toBe("image/jpeg");
expect(resolved.value).toMatchObject({
width: 640,
height: 360,
loading: "eager",
decoding: "async",
fetchPriority: "high",
crossOrigin: "anonymous",
delivery: {
class: "PUBLIC_IMMUTABLE",
browserCache: "PUBLIC_IMMUTABLE",
sharedCache: "PUBLIC_IMMUTABLE",
purge: "REVISION_ROLLOVER",
expiresAtEpochMs: null,
},
decodeBudget: {
maximumCandidatePixels: 921_600,
maximumDecodedBytes: 3_686_400,
},
});
const primary = new URL(resolved.value.src);
expect(primary.origin).toBe("https://images.example.test");
expect(primary.pathname).toBe(
"/v1/assets/asset_Q3x8pL/rev_a8N2kP4z",
);
expect([...primary.searchParams.keys()]).toEqual([
"dpr",
"fit",
"format",
"height",
"preset",
"quality",
"width",
]);
expect(Object.fromEntries(primary.searchParams)).toEqual({
dpr: "1",
fit: "cover",
format: "jpeg",
height: "360",
preset: "card-landscape-v1",
quality: "80",
width: "640",
});
});
it("rejects raw URL/query/transform input and forged preset or asset references", async () => {
const preset = imageCdnPresetReference(
"card-landscape",
"render-public-product-image",
);
const runtime = createImageCdnRuntime({
policies: createPolicies([createPreset(preset)]),
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: vi.fn().mockResolvedValue(true),
},
});
expect(
runtime.assets.acceptPublicImmutable({
...publicAsset(),
sourceUrl: `${"java"}script:alert(1)`,
} as PublicImmutableImageAsset),
).toMatchObject({
ok: false,
error: { code: "INVALID_INPUT" },
});
expect(
runtime.assets.acceptPublicImmutable(
publicAsset({
assetId: "../../admin?width=9999",
}),
),
).toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
expect(
runtime.assets.acceptPublicImmutable(
publicAsset({
mediaType: "image/svg+xml",
} as unknown as Partial<PublicImmutableImageAsset>),
),
).toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
const accepted = runtime.assets.acceptPublicImmutable(
publicAsset(),
);
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
width: 9_999,
query: "format=svg",
src: "data:text/html,active",
} as unknown as Parameters<typeof runtime.presentation.resolve>[0]),
).resolves.toMatchObject({
ok: false,
error: { code: "INVALID_INPUT" },
});
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset: imageCdnPresetReference(
"card-landscape",
"render-public-product-image",
),
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
await expect(
runtime.presentation.resolve({
asset: {} as typeof accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
});
it("deep-snapshots origin and preset policy against TOCTOU mutation", async () => {
const presetReference = imageCdnPresetReference(
"card-landscape",
"render-public-product-image",
);
const origin = {
originKey: "product-images",
origin: "https://images.example.test",
assetPathPrefix: "/v1/assets/",
minimumPublicMaxAgeSeconds: 31_536_000,
};
const preset = createPreset(presetReference);
const policies = new ImageCdnPolicyRegistry({
applicationOrigin: "https://app.example.test",
origins: [origin],
presets: [preset],
hardLimits,
capability: {
issuer: "image-bff",
acceptedKeyIds: ["image-signing-2026-01"],
},
});
origin.origin = "https://attacker.invalid";
origin.assetPathPrefix = "/stolen/";
(preset.dprs as number[])[0] = 4;
(preset.formats as string[])[0] = "svg";
(preset as { quality: number }).quality = 100;
const runtime = createImageCdnRuntime({ policies });
const accepted = runtime.assets.acceptPublicImmutable(
publicAsset(),
);
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset: presetReference,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
if (!resolved.ok) return;
const url = new URL(resolved.value.src);
expect(url.origin).toBe("https://images.example.test");
expect(url.pathname.startsWith("/v1/assets/")).toBe(true);
expect(url.searchParams.get("quality")).toBe("80");
expect(resolved.value.sources[0]?.type).toBe("image/avif");
});
it("rejects unsafe origins and preset budgets at composition time", () => {
const preset = imageCdnPresetReference(
"unsafe",
"reject-policy",
);
expect(
() =>
new ImageCdnPolicyRegistry({
applicationOrigin: "https://app.example.test",
origins: [
{
originKey: "bad",
origin: "http://user:password@example.test/?q=x#x",
assetPathPrefix: "/../assets/",
minimumPublicMaxAgeSeconds: 1,
},
],
presets: [createPreset(preset)],
hardLimits,
capability: {
issuer: "image-bff",
acceptedKeyIds: ["image-signing-2026-01"],
},
}),
).toThrow(/origin policy/u);
expect(
() =>
createPolicies([
createPreset(preset, {
maxTransformedPixels: 100,
maxDecodedBytes: 400,
}),
]),
).toThrow(/pixel budget/u);
const validRegistryOptions = {
origins: [
{
originKey: "product-images",
origin: "https://images.example.test",
assetPathPrefix: "/v1/assets/",
minimumPublicMaxAgeSeconds: 31_536_000,
},
],
presets: [createPreset(preset)],
hardLimits,
capability: {
issuer: "image-bff",
acceptedKeyIds: ["image-signing-2026-01"],
},
};
expect(
() =>
new ImageCdnPolicyRegistry({
...validRegistryOptions,
applicationOrigin: "https://images.example.test",
}),
).toThrow(/cross-origin/u);
for (const applicationOrigin of [
"http://app.example.test",
"https://app.example.test/",
]) {
expect(
() =>
new ImageCdnPolicyRegistry({
...validRegistryOptions,
applicationOrigin,
}),
).toThrow(/application origin/u);
}
});
it("enforces immutable implementation ceilings on every resource-bearing hard limit", () => {
const preset = imageCdnPresetReference(
"absolute-ceilings",
"bound-image-composition",
);
const ceiling = IMAGE_CDN_IMPLEMENTATION_CEILINGS;
const overCeiling: readonly Partial<ImageCdnHardLimits>[] = [
{ maxIntrinsicWidth: ceiling.maxIntrinsicWidth + 1 },
{ maxIntrinsicHeight: ceiling.maxIntrinsicHeight + 1 },
{ maxSourcePixels: ceiling.maxSourcePixels + 1 },
{ maxCssDimension: ceiling.maxCssDimension + 1 },
{ maxDpr: ceiling.maxDpr + 1 },
{ maxQuality: ceiling.maxQuality + 1 },
{ maxCandidateCount: ceiling.maxCandidateCount + 1 },
{
maxTransformedPixels:
ceiling.maxTransformedPixels + 1,
},
{ maxDecodedBytes: ceiling.maxDecodedBytes + 1 },
{ maxEncodedBytes: ceiling.maxEncodedBytes + 1 },
{ maxUrlLength: ceiling.maxUrlLength + 1 },
{
maxCapabilityLifetimeMs:
ceiling.maxCapabilityLifetimeMs + 1,
},
{ maxClockSkewMs: ceiling.maxClockSkewMs + 1 },
{
minCapabilityRemainingMs:
ceiling.maxMinimumCapabilityRemainingMs + 1,
},
{
maxPresetBindingsPerCapability:
ceiling.maxPresetBindingsPerCapability + 1,
},
{
maxConcurrentCapabilityVerifications:
ceiling.maxConcurrentCapabilityVerifications + 1,
},
];
for (const override of overCeiling) {
expect(() =>
createPolicies([createPreset(preset)], {
hardLimits: { ...hardLimits, ...override },
}),
).toThrow(/hard limits/u);
}
const boundaryLimits: ImageCdnHardLimits = {
...hardLimits,
maxIntrinsicWidth: ceiling.maxIntrinsicWidth,
maxIntrinsicHeight: ceiling.maxIntrinsicHeight,
maxSourcePixels: ceiling.maxSourcePixels,
maxCssDimension: ceiling.maxCssDimension,
maxDpr: ceiling.maxDpr,
maxQuality: ceiling.maxQuality,
maxCandidateCount: ceiling.maxCandidateCount,
maxTransformedPixels: ceiling.maxTransformedPixels,
maxDecodedBytes: ceiling.maxDecodedBytes,
maxEncodedBytes: ceiling.maxEncodedBytes,
maxUrlLength: ceiling.maxUrlLength,
maxCapabilityLifetimeMs:
ceiling.maxCapabilityLifetimeMs,
maxClockSkewMs: ceiling.maxClockSkewMs,
minCapabilityRemainingMs:
ceiling.maxMinimumCapabilityRemainingMs,
maxPresetBindingsPerCapability:
ceiling.maxPresetBindingsPerCapability,
maxConcurrentCapabilityVerifications:
ceiling.maxConcurrentCapabilityVerifications,
};
expect(() =>
createPolicies([createPreset(preset)], {
hardLimits: boundaryLimits,
}),
).not.toThrow();
});
it("snapshots a bounded unique capability-key overlap set", () => {
const preset = imageCdnPresetReference(
"key-policy",
"rotate-image-signing-key",
);
for (const acceptedKeyIds of [
[],
["image-key-current", "image-key-current"],
Array.from(
{
length:
IMAGE_CDN_IMPLEMENTATION_CEILINGS.maxAcceptedKeyIds +
1,
},
(_, index) => `image-key-${index}`,
),
["image-key-current", "invalid/key"],
]) {
expect(() =>
createPolicies([createPreset(preset)], {
acceptedKeyIds,
}),
).toThrow(/capability policy/u);
}
const mutableKeyIds = [
"image-signing-2026-02",
"image-signing-2026-01",
];
const policies = createPolicies([createPreset(preset)], {
acceptedKeyIds: mutableKeyIds,
});
mutableKeyIds[0] = "image-signing-attacker";
expect(policies.capabilityPolicy().acceptedKeyIds).toEqual([
"image-signing-2026-02",
"image-signing-2026-01",
]);
expect(
Object.isFrozen(
policies.capabilityPolicy().acceptedKeyIds,
),
).toBe(true);
});
it("accepts old and new overlap keys only when policy and verifier registries both cover them", async () => {
const preset = imageCdnPresetReference(
"key-overlap",
"verify-image-key-rotation",
);
const acceptedKeyIds = [
"image-signing-2026-02",
"image-signing-2026-01",
] as const;
const policies = createPolicies([createPreset(preset)], {
acceptedKeyIds,
});
const verifierKeys = new Set<string>(acceptedKeyIds);
const verify = vi.fn(
async (request: { keyId: string }) =>
verifierKeys.has(request.keyId),
);
const runtime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: (keyId) => verifierKeys.has(keyId),
verify,
},
});
const previous = await privateAsset();
const current = await privateAsset({
signature: {
algorithm: "ECDSA_P256_SHA256",
keyId: "image-signing-2026-02",
capabilityBindingDigestHex: "0".repeat(64),
valueBase64Url: "A".repeat(86),
},
});
await expect(
runtime.assets.acceptBackendIssued(previous),
).resolves.toMatchObject({ ok: true });
await expect(
runtime.assets.acceptBackendIssued(current),
).resolves.toMatchObject({ ok: true });
expect(verify.mock.calls.map(([request]) => request.keyId)).toEqual(
["image-signing-2026-01", "image-signing-2026-02"],
);
const unknown = await privateAsset({
signature: {
algorithm: "ECDSA_P256_SHA256",
keyId: "image-signing-2027-01",
capabilityBindingDigestHex: "0".repeat(64),
valueBase64Url: "A".repeat(86),
},
});
await expect(
runtime.assets.acceptBackendIssued(unknown),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
expect(verify).toHaveBeenCalledTimes(2);
expect(() =>
createImageCdnRuntime({
policies,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: (keyId) =>
keyId === "image-signing-2026-02",
verify: vi.fn().mockResolvedValue(true),
},
}),
).toThrow(/registry does not cover/u);
expect(() =>
createImageCdnRuntime({
policies,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey() {
throw new TypeError("registry unavailable");
},
verify: vi.fn().mockResolvedValue(true),
},
}),
).toThrow(/registry does not cover/u);
});
it("binds a private descriptor to its signed snapshot and exact preset", async () => {
const preset = imageCdnPresetReference(
"card-landscape",
"render-private-product-image",
);
const policies = createPolicies([
createPreset(preset, {
probeMode: "PRIMARY_REQUIRED",
}),
]);
let releaseVerification:
((value: boolean) => void) | undefined;
const verifierMethod = vi.fn(
() =>
new Promise<boolean>((resolve) => {
releaseVerification = resolve;
}),
);
const verifier: ImageCapabilityVerifier = {
acceptsKey: acceptsTestImageKey,
verify: verifierMethod,
};
let now = 1_000_000;
const runtime = createImageCdnRuntime({
policies,
now: () => now,
subtle: globalThis.crypto.subtle,
capabilityVerifier: verifier,
probe: {
async probe(request) {
return {
ok: true,
value: {
absoluteUrl: request.absoluteUrl,
mediaType: request.expectedMediaType,
encodedBytes: 1_024,
decodedWidth: request.expectedWidth,
decodedHeight: request.expectedHeight,
},
};
},
},
});
(
verifier as {
verify: ImageCapabilityVerifier["verify"];
}
).verify = vi.fn().mockResolvedValue(false);
const mutable = (await privateAsset()) as {
-readonly [Key in keyof BackendIssuedImageAsset]:
BackendIssuedImageAsset[Key];
};
const pending = runtime.assets.acceptBackendIssued(mutable);
await vi.waitFor(() => {
expect(verifierMethod).toHaveBeenCalledOnce();
});
mutable.assetId = "asset_ATTACKER";
mutable.revision = "rev_ATTACKER";
mutable.expiresAtEpochMs = 9_999_999;
releaseVerification?.(true);
const accepted = await pending;
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
if (!resolved.ok) return;
const url = new URL(resolved.value.src);
expect(url.pathname).toContain(
"/asset_Q3x8pL/rev_a8N2kP4z",
);
expect(url.pathname).not.toContain("ATTACKER");
expect([...url.searchParams.keys()]).toEqual([
"binding",
"capability",
"dpr",
"expires",
"fit",
"format",
"height",
"key",
"preset",
"quality",
"signature",
"width",
]);
expect(resolved.value).toMatchObject({
loading: "eager",
referrerPolicy: "no-referrer",
crossOrigin: "anonymous",
delivery: {
class: "PRIVATE_SIGNED",
browserCache: "NO_STORE",
sharedCache: "FORBIDDEN",
purge: "CAPABILITY_REVOCATION_OR_EXPIRY",
expiresAtEpochMs: 1_300_000,
},
});
now = 1_275_001;
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "EXPIRED_RESOURCE" },
});
});
it("rejects invalid binding/signature and private lazy or low-priority presets", async () => {
const eager = imageCdnPresetReference(
"private-eager",
"render-private",
);
const lazy = imageCdnPresetReference(
"private-lazy",
"render-private",
);
const policies = createPolicies([
createPreset(eager, {
bindingId: "private-eager-v1",
}),
createPreset(lazy, {
bindingId: "private-lazy-v1",
loading: "lazy",
fetchPriority: "low",
}),
]);
const runtime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: vi.fn().mockResolvedValue(true),
},
});
const issued = await privateAsset({
allowedPresetBindingIds: [
"private-eager-v1",
"private-lazy-v1",
],
});
const accepted = await runtime.assets.acceptBackendIssued(issued);
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset: eager,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset: lazy,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
await expect(
runtime.assets.acceptBackendIssued({
...issued,
signature: {
...issued.signature,
capabilityBindingDigestHex: "f".repeat(64),
},
}),
).resolves.toMatchObject({
ok: false,
error: { code: "INTEGRITY_FAILED" },
});
const rejectingRuntime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: vi.fn().mockResolvedValue(false),
},
});
await expect(
rejectingRuntime.assets.acceptBackendIssued(issued),
).resolves.toMatchObject({
ok: false,
error: { code: "INTEGRITY_FAILED" },
});
});
it("bounds digest and capability verification with a runtime-owned deadline and caller abort", async () => {
const preset = imageCdnPresetReference(
"deadline",
"verify-private-capability",
);
const policies = createPolicies([createPreset(preset)]);
const issued = await privateAsset();
const observations: Record<string, unknown>[] = [];
const verifierDeadline =
manualCapabilityVerificationScheduler();
const stalledVerifier = vi.fn(
() => new Promise<boolean>(() => undefined),
);
const verifierRuntime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: stalledVerifier,
},
capabilityVerificationScheduler:
verifierDeadline.scheduler,
observer: {
record(observation) {
observations.push(observation);
},
},
});
const verifierPending =
verifierRuntime.assets.acceptBackendIssued(issued);
await vi.waitFor(() => {
expect(stalledVerifier).toHaveBeenCalledOnce();
});
expect(verifierDeadline.delays).toEqual([
DEFAULT_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS,
]);
verifierDeadline.fire();
await expect(verifierPending).resolves.toMatchObject({
ok: false,
error: {
code: "UNAVAILABLE",
retryable: true,
recovery: "RETRY",
},
});
expect(verifierDeadline.clearTimeout).toHaveBeenCalledOnce();
expect(observations).toEqual([
expect.objectContaining({
operation: "IMAGE_RESOLVE",
outcome: "FAILED",
failureCode: "UNAVAILABLE",
countBucket: "ZERO",
}),
]);
const digestDeadline =
manualCapabilityVerificationScheduler();
const stalledDigest = vi.fn(
() => new Promise<ArrayBuffer>(() => undefined),
);
const digestVerifier = vi.fn().mockResolvedValue(true);
const digestRuntime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: {
digest: stalledDigest as SubtleCrypto["digest"],
},
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: digestVerifier,
},
capabilityVerificationTimeoutMs:
MAX_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS,
capabilityVerificationScheduler: digestDeadline.scheduler,
});
const digestPending =
digestRuntime.assets.acceptBackendIssued(issued);
await vi.waitFor(() => {
expect(stalledDigest).toHaveBeenCalledOnce();
});
expect(digestDeadline.delays).toEqual([
MAX_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS,
]);
digestDeadline.fire();
await expect(digestPending).resolves.toMatchObject({
ok: false,
error: { code: "UNAVAILABLE" },
});
expect(digestVerifier).not.toHaveBeenCalled();
const abortDeadline =
manualCapabilityVerificationScheduler();
const abortVerifier = vi.fn(
() => new Promise<boolean>(() => undefined),
);
const abortRuntime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: abortVerifier,
},
capabilityVerificationScheduler: abortDeadline.scheduler,
});
const controller = new AbortController();
const abortPending = abortRuntime.assets.acceptBackendIssued(
issued,
{ signal: controller.signal },
);
await vi.waitFor(() => {
expect(abortVerifier).toHaveBeenCalledOnce();
});
controller.abort();
await expect(abortPending).resolves.toMatchObject({
ok: false,
error: { code: "ABORTED" },
});
expect(abortDeadline.clearTimeout).toHaveBeenCalledOnce();
});
/**
* TR-RR-07. The concurrency cap exists to bound *physical* verification work.
* Releasing the slot when the wrapper's abort resolved let an abandoned
* verifier keep running while a new one was admitted, so repeated aborts
* produced more concurrent work than the configured cap allows.
*/
it("holds the verification slot until the raw verifier settles", async () => {
const preset = imageCdnPresetReference(
"verification-concurrency",
"bound-image-verification-concurrency",
);
const policies = createPolicies([createPreset(preset)], {
hardLimits: {
...hardLimits,
maxConcurrentCapabilityVerifications: 1,
},
});
let verificationAttempt = 0;
let releaseFirst: ((value: boolean) => void) | undefined;
const verify = vi.fn(() => {
verificationAttempt += 1;
return verificationAttempt === 1
? new Promise<boolean>((resolve) => {
releaseFirst = resolve;
})
: Promise.resolve(true);
});
const runtime = createImageCdnRuntime({
policies,
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify,
},
});
const issued = await privateAsset();
const controller = new AbortController();
const first = runtime.assets.acceptBackendIssued(issued, {
signal: controller.signal,
});
await vi.waitFor(() => {
expect(verify).toHaveBeenCalledOnce();
});
await expect(
runtime.assets.acceptBackendIssued(issued),
).resolves.toMatchObject({
ok: false,
error: { code: "LIMIT_EXCEEDED" },
});
expect(verify).toHaveBeenCalledOnce();
controller.abort();
await expect(first).resolves.toMatchObject({
ok: false,
error: { code: "ABORTED" },
});
// The caller's wait ended, but the raw verifier has not. Admitting a second
// one here would put two physical verifications under a cap of one.
await expect(
runtime.assets.acceptBackendIssued(issued),
).resolves.toMatchObject({
ok: false,
error: { code: "LIMIT_EXCEEDED" },
});
expect(verify).toHaveBeenCalledOnce();
// Once the raw verifier settles the slot is free again.
releaseFirst?.(true);
await vi.waitFor(async () => {
await expect(
runtime.assets.acceptBackendIssued(issued),
).resolves.toMatchObject({ ok: true });
});
expect(verify).toHaveBeenCalledTimes(2);
});
it("fails fast on invalid capability verification deadline composition", () => {
const preset = imageCdnPresetReference(
"deadline-config",
"validate-private-capability-deadline",
);
const policies = createPolicies([createPreset(preset)]);
expect(
DEFAULT_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS,
).toBe(5_000);
expect(MAX_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS).toBe(
60_000,
);
for (const capabilityVerificationTimeoutMs of [
0,
1.5,
MAX_IMAGE_CAPABILITY_VERIFICATION_TIMEOUT_MS + 1,
]) {
expect(() =>
createImageCdnRuntime({
policies,
capabilityVerificationTimeoutMs,
}),
).toThrow(/verification timeout/u);
}
expect(() =>
createImageCdnRuntime({
policies,
capabilityVerificationScheduler: {
setTimeout: null,
clearTimeout: vi.fn(),
} as unknown as ImageCapabilityVerificationScheduler,
}),
).toThrow(/verification scheduler/u);
});
it("closes terminally and idempotently, revoking old references and rejecting every later operation", async () => {
const preset = imageCdnPresetReference(
"lifecycle",
"close-image-runtime",
);
const runtime = createImageCdnRuntime({
policies: createPolicies([createPreset(preset)]),
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: vi.fn().mockResolvedValue(true),
},
});
const accepted =
runtime.assets.acceptPublicImmutable(publicAsset());
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({ ok: true });
const acceptedPrivate =
await runtime.assets.acceptBackendIssued(
await privateAsset(),
);
expect(acceptedPrivate.ok).toBe(true);
if (!acceptedPrivate.ok) return;
runtime.close();
expect(() => runtime.close()).not.toThrow();
await expect(
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: {
code: "UNAVAILABLE",
retryable: false,
recovery: "NONE",
},
});
await expect(
runtime.presentation.resolve({
asset: acceptedPrivate.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "UNAVAILABLE" },
});
expect(
runtime.assets.acceptPublicImmutable(publicAsset()),
).toMatchObject({
ok: false,
error: { code: "UNAVAILABLE" },
});
await expect(
runtime.assets.acceptBackendIssued(await privateAsset()),
).resolves.toMatchObject({
ok: false,
error: { code: "UNAVAILABLE" },
});
});
it("close aborts stalled private verification and image probing", async () => {
const verifierPreset = imageCdnPresetReference(
"close-verifier",
"abort-image-verification",
);
const verifierDeadline =
manualCapabilityVerificationScheduler();
const stalledVerifier = vi.fn(
() => new Promise<boolean>(() => undefined),
);
const verifierRuntime = createImageCdnRuntime({
policies: createPolicies([
createPreset(verifierPreset),
]),
now: () => 1_000_000,
subtle: globalThis.crypto.subtle,
capabilityVerifier: {
acceptsKey: acceptsTestImageKey,
verify: stalledVerifier,
},
capabilityVerificationScheduler:
verifierDeadline.scheduler,
});
const verificationPending =
verifierRuntime.assets.acceptBackendIssued(
await privateAsset(),
);
await vi.waitFor(() => {
expect(stalledVerifier).toHaveBeenCalledOnce();
});
verifierRuntime.close();
await expect(verificationPending).resolves.toMatchObject({
ok: false,
error: { code: "UNAVAILABLE", retryable: false },
});
expect(verifierDeadline.clearTimeout).toHaveBeenCalledOnce();
const probePreset = imageCdnPresetReference(
"close-probe",
"abort-image-probe",
);
const stalledProbe = vi.fn(
(_request: ImageProbeRequest) =>
new Promise<never>(() => undefined),
);
const probeRuntime = createImageCdnRuntime({
policies: createPolicies([
createPreset(probePreset, {
probeMode: "PRIMARY_REQUIRED",
}),
]),
probe: { probe: stalledProbe },
});
const accepted =
probeRuntime.assets.acceptPublicImmutable(publicAsset());
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
const caller = new AbortController();
const probePending = probeRuntime.presentation.resolve({
asset: accepted.value,
preset: probePreset,
signal: caller.signal,
});
await vi.waitFor(() => {
expect(stalledProbe).toHaveBeenCalledOnce();
});
const runtimeSignal =
stalledProbe.mock.calls[0]?.[0].signal;
expect(runtimeSignal).not.toBe(caller.signal);
probeRuntime.close();
await expect(probePending).resolves.toMatchObject({
ok: false,
error: { code: "UNAVAILABLE", retryable: false },
});
expect(runtimeSignal?.aborted).toBe(true);
});
it("requires and validates the bounded probe receipt when the preset says so", async () => {
const preset = imageCdnPresetReference(
"probed",
"render-verified-image",
);
const policy = createPreset(preset, {
bindingId: "probed-v1",
probeMode: "PRIMARY_REQUIRED",
});
const withoutProbe = createImageCdnRuntime({
policies: createPolicies([policy]),
});
const noProbeAsset =
withoutProbe.assets.acceptPublicImmutable(publicAsset());
expect(noProbeAsset.ok).toBe(true);
if (!noProbeAsset.ok) return;
await expect(
withoutProbe.presentation.resolve({
asset: noProbeAsset.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
error: { code: "UNSUPPORTED" },
});
const probe = vi.fn(async (request: ImageProbeRequest) => ({
ok: true as const,
value: {
absoluteUrl: request.absoluteUrl,
mediaType: request.expectedMediaType,
encodedBytes: 1_024,
decodedWidth: request.expectedWidth,
decodedHeight: request.expectedHeight,
},
}));
const runtime = createImageCdnRuntime({
policies: createPolicies([policy]),
probe: { probe },
});
const accepted =
runtime.assets.acceptPublicImmutable(publicAsset());
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
expect(probe).toHaveBeenCalledWith(
expect.objectContaining({
expectedMediaType: "image/jpeg",
expectedWidth: 640,
expectedHeight: 360,
maxEncodedBytes: 524_288,
maxDecodedBytes: 4_194_304,
delivery: "PUBLIC_IMMUTABLE",
}),
);
});
it("records only safe terminal buckets for accept, probe and resolve", async () => {
const preset = imageCdnPresetReference(
"observed",
"render-observed-image",
);
const observations: Record<string, unknown>[] = [];
const observer = {
record(observation: Record<string, unknown>) {
observations.push(observation);
},
};
const runtime = createImageCdnRuntime({
policies: createPolicies([
createPreset(preset, {
bindingId: "observed-v1",
probeMode: "PRIMARY_REQUIRED",
}),
]),
observer,
probe: {
async probe(request) {
return {
ok: true,
value: {
absoluteUrl: request.absoluteUrl,
mediaType: request.expectedMediaType,
encodedBytes: 2_048,
decodedWidth: request.expectedWidth,
decodedHeight: request.expectedHeight,
},
};
},
},
});
observer.record = () => {
throw new TypeError("mutated observer must not be observed");
};
const accepted =
runtime.assets.acceptPublicImmutable(publicAsset());
expect(accepted.ok).toBe(true);
if (!accepted.ok) return;
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
expect(observations).toHaveLength(3);
for (const observation of observations) {
expect(observation).toMatchObject({
operation: "IMAGE_RESOLVE",
outcome: "SUCCEEDED",
});
expect(Object.keys(observation)).not.toEqual(
expect.arrayContaining([
"assetId",
"preset",
"capability",
"signature",
"url",
]),
);
expect(JSON.stringify(observation)).not.toContain(
"images.example.test",
);
}
expect(observations.map((value) => value.countBucket)).toEqual([
"ONE",
"ONE",
"TWO_TO_TEN",
]);
expect(observations.map((value) => value.byteBucket)).toEqual([
undefined,
"LT1MIB",
"LT1MIB",
]);
});
});
describe("browser image probe", () => {
const imageUrl =
"https://images.example.test/v1/assets/a/rev?format=png";
const request = (
overrides: Partial<ImageProbeRequest> = {},
): ImageProbeRequest => ({
absoluteUrl: imageUrl,
expectedMediaType: "image/png",
expectedWidth: 640,
expectedHeight: 360,
maxEncodedBytes: 1_024,
maxDecodedPixels: 230_400,
maxDecodedBytes: 921_600,
delivery: "PUBLIC_IMMUTABLE",
minimumPublicMaxAgeSeconds: 31_536_000,
referrerPolicy: "no-referrer",
signal: new AbortController().signal,
...overrides,
});
it("parses all supported static headers before decode and closes each bitmap", async () => {
const samples = [
{
mediaType: "image/png" as const,
bytes: pngBytes(640, 360),
},
{
mediaType: "image/jpeg" as const,
bytes: jpegBytes(640, 360),
},
{
mediaType: "image/webp" as const,
bytes: webpBytes(640, 360),
},
{
mediaType: "image/avif" as const,
bytes: avifBytes(640, 360),
},
];
const close = vi.fn();
for (const sample of samples) {
const exactUrl = imageUrl.replace(
"format=png",
`format=${sample.mediaType.slice("image/".length)}`,
);
const fetcher = vi.fn(async () =>
responseAt(exactUrl, sample.bytes, {
status: 200,
headers: publicImageHeaders(
sample.mediaType,
sample.bytes.byteLength,
),
}),
);
const probe = createBrowserImageProbe({
fetcher: fetcher as typeof fetch,
createBitmap: vi.fn(async () => ({
width: 640,
height: 360,
close,
})),
});
await expect(
probe.probe(
request({
absoluteUrl: exactUrl,
expectedMediaType: sample.mediaType,
}),
),
).resolves.toMatchObject({
ok: true,
value: {
absoluteUrl: exactUrl,
mediaType: sample.mediaType,
encodedBytes: sample.bytes.byteLength,
decodedWidth: 640,
decodedHeight: 360,
},
});
expect(fetcher).toHaveBeenCalledWith(
exactUrl,
expect.objectContaining({
credentials: "omit",
redirect: "error",
mode: "cors",
cache: "no-store",
referrerPolicy: "no-referrer",
}),
);
}
expect(close).toHaveBeenCalledTimes(samples.length);
});
it("fails closed on duplicate/conflicting cache directives and oversized bodies", async () => {
const png = pngBytes(640, 360);
const createBitmap = vi.fn(async () => ({
width: 640,
height: 360,
close: vi.fn(),
}));
for (const cacheControl of [
// BT-IMG-02. Unmatched quotes must not be unwrapped into a bare number.
'public, max-age="31536000, immutable',
'public, max-age=31536000", immutable',
'public, max-age="31536000\\", immutable',
"public, public, max-age=31536000, immutable",
"public, max-age=31536000, s-maxage=60, immutable",
"public, max-age=31536000, immutable, must-revalidate",
"public=1, max-age=31536000, immutable",
"public, max-age=31536000, immutable=true",
]) {
const probe = createBrowserImageProbe({
fetcher: (async () =>
responseAt(imageUrl, png, {
status: 200,
headers: {
"cache-control": cacheControl,
"content-type": "image/png",
},
})) as typeof fetch,
createBitmap,
});
await expect(probe.probe(request())).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
}
const probe = createBrowserImageProbe({
fetcher: (async () =>
responseAt(imageUrl, new Uint8Array(2_048), {
status: 200,
headers: {
"cache-control":
"public, max-age=31536000, immutable",
"content-type": "image/png",
},
})) as typeof fetch,
createBitmap,
});
await expect(probe.probe(request())).resolves.toMatchObject({
ok: false,
error: { code: "LIMIT_EXCEEDED" },
});
expect(createBitmap).not.toHaveBeenCalled();
});
it("rejects non-identity content encoding and mismatched declared lengths", async () => {
const png = pngBytes(640, 360);
const createBitmap = vi.fn();
const cases = [
{
headers: {
"content-encoding": "gzip",
"content-length": String(png.byteLength),
},
code: "POLICY_REJECTED",
},
{
headers: {
"content-length": String(png.byteLength + 1),
},
code: "INTEGRITY_FAILED",
},
];
for (const invalid of cases) {
const headers = publicImageHeaders("image/png");
for (const [name, value] of Object.entries(invalid.headers)) {
headers.set(name, value);
}
const probe = createBrowserImageProbe({
fetcher: (async () =>
responseAt(imageUrl, png, {
status: 200,
headers,
})) as typeof fetch,
createBitmap,
});
await expect(probe.probe(request())).resolves.toMatchObject({
ok: false,
error: { code: invalid.code },
});
}
expect(createBitmap).not.toHaveBeenCalled();
});
it("rejects malicious dimensions and animated PNG/WebP before native decode", async () => {
const createBitmap = vi.fn(async () => ({
width: 640,
height: 360,
close: vi.fn(),
}));
const cases = [
{
bytes: pngBytes(20_000, 20_000),
mediaType: "image/png" as const,
code: "LIMIT_EXCEEDED",
},
{
bytes: pngBytes(320, 180),
mediaType: "image/png" as const,
code: "INTEGRITY_FAILED",
},
{
bytes: jpegBytes(320, 180),
mediaType: "image/jpeg" as const,
code: "INTEGRITY_FAILED",
},
{
bytes: webpBytes(10_000, 10_000),
mediaType: "image/webp" as const,
code: "LIMIT_EXCEEDED",
},
{
bytes: avifBytes(20_000, 20_000),
mediaType: "image/avif" as const,
code: "LIMIT_EXCEEDED",
},
{
bytes: pngBytes(640, 360, true),
mediaType: "image/png" as const,
code: "INTEGRITY_FAILED",
},
{
bytes: webpBytes(640, 360, true),
mediaType: "image/webp" as const,
code: "INTEGRITY_FAILED",
},
{
bytes: avifBytes(640, 360, "avis"),
mediaType: "image/avif" as const,
code: "INTEGRITY_FAILED",
},
];
for (const malicious of cases) {
const url = imageUrl.replace(
"format=png",
`format=${malicious.mediaType.slice("image/".length)}`,
);
const probe = createBrowserImageProbe({
fetcher: (async () =>
responseAt(url, malicious.bytes, {
status: 200,
headers: publicImageHeaders(
malicious.mediaType,
malicious.bytes.byteLength,
),
})) as typeof fetch,
createBitmap,
});
await expect(
probe.probe(
request({
absoluteUrl: url,
expectedMediaType: malicious.mediaType,
}),
),
).resolves.toMatchObject({
ok: false,
error: { code: malicious.code },
});
}
expect(createBitmap).not.toHaveBeenCalled();
});
it("enforces private no-store, omitted credentials and the exact final URL", async () => {
const png = pngBytes(640, 360);
const fetcher = vi.fn(async () =>
responseAt(imageUrl, png, {
status: 200,
headers: {
// TR-RR-09. A private response carries `no-store` and nothing else
// that describes cacheability.
"cache-control": "no-store",
"content-type": "image/png",
},
}),
);
const probe = createBrowserImageProbe({
fetcher: fetcher as typeof fetch,
createBitmap: async () => ({
width: 640,
height: 360,
close: vi.fn(),
}),
});
await expect(
probe.probe(
request({
delivery: "PRIVATE_SIGNED",
minimumPublicMaxAgeSeconds: 0,
}),
),
).resolves.toMatchObject({ ok: true });
expect(fetcher).toHaveBeenCalledWith(
imageUrl,
expect.objectContaining({
cache: "no-store",
credentials: "omit",
redirect: "error",
}),
);
for (const response of [
responseAt(imageUrl, png, {
status: 200,
headers: {
"cache-control": "private, no-store=value",
"content-type": "image/png",
},
}),
responseAt(imageUrl, png, {
status: 200,
headers: {
"cache-control": "public, no-store",
"content-type": "image/png",
},
}),
responseAt(
"https://images.example.test/v1/assets/other",
png,
{
status: 200,
headers: {
"cache-control": "private, no-store",
"content-type": "image/png",
},
},
),
]) {
const rejectingProbe = createBrowserImageProbe({
fetcher: (async () => response) as typeof fetch,
createBitmap: async () => ({
width: 640,
height: 360,
close: vi.fn(),
}),
});
await expect(
rejectingProbe.probe(
request({
delivery: "PRIVATE_SIGNED",
minimumPublicMaxAgeSeconds: 0,
}),
),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
}
await expect(
probe.probe(
request({
expectedMediaType: "image/svg+xml",
} as unknown as Partial<ImageProbeRequest>),
),
).resolves.toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
});
/**
* TR-RR-09. The recorded BT-IMG-02 contract for a private response is a
* fail-closed matrix. Accepting `no-store` next to a directive that describes
* cacheability lets a self-contradictory policy read as acceptable.
*/
it("applies the full private Cache-Control matrix", async () => {
const png = pngBytes(640, 360);
const probeWith = async (cacheControl: string) => {
const probe = createBrowserImageProbe({
fetcher: (async () =>
responseAt(imageUrl, png, {
status: 200,
headers: {
"cache-control": cacheControl,
"content-type": "image/png",
},
})) as typeof fetch,
createBitmap: async () => ({
width: 640,
height: 360,
close: vi.fn(),
}),
});
return await probe.probe(
request({
delivery: "PRIVATE_SIGNED",
minimumPublicMaxAgeSeconds: 0,
}),
);
};
// Only `no-store`, plus a syntactically valid unknown extension.
expect(await probeWith("no-store")).toMatchObject({ ok: true });
expect(await probeWith('no-store, x-vendor="a,b"')).toMatchObject({
ok: true,
});
for (const companion of [
"public",
"private",
"immutable",
"max-age=60",
"s-maxage=60",
"no-cache",
"must-revalidate",
"proxy-revalidate",
]) {
expect(await probeWith(`no-store, ${companion}`)).toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
}
for (const withoutNoStore of ["private", "no-cache", "max-age=0"]) {
expect(await probeWith(withoutNoStore)).toMatchObject({
ok: false,
error: { code: "POLICY_REJECTED" },
});
}
});
it("times out a stalled body, aborts the composed signal and cancels its reader", async () => {
const manual = manualImageProbeScheduler();
const cancel = vi.fn(async () => undefined);
const releaseLock = vi.fn();
const read = vi.fn(
() =>
new Promise<ReadableStreamReadResult<Uint8Array>>(
() => undefined,
),
);
const response = {
body: {
getReader: () => ({ cancel, read, releaseLock }),
},
headers: publicImageHeaders("image/png"),
ok: true,
redirected: false,
status: 200,
type: "cors",
url: imageUrl,
} as unknown as Response;
const fetcher = vi.fn(
async (_input: RequestInfo | URL, _init?: RequestInit) =>
response,
);
const probe = createBrowserImageProbe({
fetcher: fetcher as typeof fetch,
createBitmap: vi.fn(),
timeoutMs: 1_000,
scheduler: manual.scheduler,
});
const probeRequest = request();
const pending = probe.probe(probeRequest);
await vi.waitFor(() => {
expect(read).toHaveBeenCalledOnce();
});
const composedSignal = fetcher.mock.calls[0]?.[1]?.signal as
| AbortSignal
| null
| undefined;
expect(composedSignal).not.toBe(probeRequest.signal);
manual.fire();
await expect(pending).resolves.toMatchObject({
ok: false,
error: {
code: "UNAVAILABLE",
retryable: true,
recovery: "RETRY",
},
});
expect(cancel).toHaveBeenCalledOnce();
expect(releaseLock).toHaveBeenCalledOnce();
expect(composedSignal?.aborted).toBe(true);
});
it("times out stalled decode and closes a bitmap that resolves late", async () => {
const manual = manualImageProbeScheduler();
const close = vi.fn();
let finishDecode:
((bitmap: {
width: number;
height: number;
close(): void;
}) => void) | undefined;
const createBitmap = vi.fn(
() =>
new Promise<{
width: number;
height: number;
close(): void;
}>((resolve) => {
finishDecode = resolve;
}),
);
const png = pngBytes(640, 360);
const probe = createBrowserImageProbe({
fetcher: (async () =>
responseAt(imageUrl, png, {
status: 200,
headers: publicImageHeaders("image/png", png.byteLength),
})) as typeof fetch,
createBitmap,
timeoutMs: 1_000,
scheduler: manual.scheduler,
});
const pending = probe.probe(request());
await vi.waitFor(() => {
expect(createBitmap).toHaveBeenCalledOnce();
});
manual.fire();
await expect(pending).resolves.toMatchObject({
ok: false,
error: { code: "UNAVAILABLE" },
});
finishDecode?.({ width: 640, height: 360, close });
await vi.waitFor(() => {
expect(close).toHaveBeenCalledOnce();
});
});
});
describe("P-256 image capability verifier", () => {
it("rejects an ECDSA public key on any curve other than P-256", async () => {
const generated = await globalThis.crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-384" },
false,
["sign", "verify"],
);
if (!("publicKey" in generated)) {
throw new TypeError("Expected an ECDSA key pair.");
}
expect(() =>
createP256ImageCapabilityVerifier({
subtle: globalThis.crypto.subtle,
publicKeys: [
{
keyId: "image-signing-wrong-curve",
key: generated.publicKey,
},
],
}),
).toThrow(/public key binding/u);
});
it("verifies the exact canonical payload and rejects tampering", async () => {
const generated = await globalThis.crypto.subtle.generateKey(
{ name: "ECDSA", namedCurve: "P-256" },
false,
["sign", "verify"],
);
if (!("privateKey" in generated)) {
throw new TypeError("Expected an ECDSA key pair.");
}
const payload = new TextEncoder().encode(
'["image-cdn-capability-v1","bound"]',
);
const payloadBuffer = new Uint8Array(payload.byteLength);
payloadBuffer.set(payload);
const signature = await globalThis.crypto.subtle.sign(
{ name: "ECDSA", hash: "SHA-256" },
generated.privateKey,
payloadBuffer.buffer,
);
const verifier = createP256ImageCapabilityVerifier({
subtle: globalThis.crypto.subtle,
publicKeys: [
{
keyId: "image-signing-2026-01",
key: generated.publicKey,
},
],
});
expect(verifier.acceptsKey("image-signing-2026-01")).toBe(
true,
);
expect(verifier.acceptsKey("image-signing-unknown")).toBe(
false,
);
const signatureBase64Url = base64Url(
new Uint8Array(signature),
);
await expect(
verifier.verify({
algorithm: "ECDSA_P256_SHA256",
keyId: "image-signing-2026-01",
canonicalPayload: payload,
signatureBase64Url,
}),
).resolves.toBe(true);
const tampered = Uint8Array.from(payload);
tampered[0] ^= 1;
await expect(
verifier.verify({
algorithm: "ECDSA_P256_SHA256",
keyId: "image-signing-2026-01",
canonicalPayload: tampered,
signatureBase64Url,
}),
).resolves.toBe(false);
});
});
function responseAt(
url: string,
body: Uint8Array,
init: ResponseInit,
): Response {
const responseBytes = new Uint8Array(body.byteLength);
responseBytes.set(body);
const response = new Response(responseBytes.buffer, init);
Object.defineProperty(response, "url", {
configurable: false,
enumerable: true,
value: url,
});
return response;
}
function publicImageHeaders(
mediaType: string,
contentLength?: number,
): Headers {
const headers = new Headers({
"cache-control":
"public, max-age=31536000, s-maxage=31536000, immutable",
"content-type": mediaType,
vary: "Accept-Encoding",
});
if (contentLength !== undefined) {
headers.set("content-length", String(contentLength));
}
return headers;
}
function pngBytes(
width: number,
height: number,
animated = false,
): Uint8Array {
const header = new Uint8Array(13);
const headerView = new DataView(header.buffer);
headerView.setUint32(0, width);
headerView.setUint32(4, height);
header[8] = 8;
header[9] = 6;
return concatenateBytes([
Uint8Array.from([
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
]),
pngChunk("IHDR", header),
...(animated
? [pngChunk("acTL", new Uint8Array(8))]
: []),
pngChunk("IDAT", new Uint8Array()),
pngChunk("IEND", new Uint8Array()),
]);
}
function pngChunk(type: string, payload: Uint8Array): Uint8Array {
const chunk = new Uint8Array(12 + payload.byteLength);
const view = new DataView(chunk.buffer);
view.setUint32(0, payload.byteLength);
writeAscii(chunk, 4, type);
chunk.set(payload, 8);
return chunk;
}
function jpegBytes(width: number, height: number): Uint8Array {
return Uint8Array.from([
0xff,
0xd8,
0xff,
0xc0,
0x00,
0x11,
0x08,
(height >>> 8) & 0xff,
height & 0xff,
(width >>> 8) & 0xff,
width & 0xff,
0x03,
0x01,
0x11,
0x00,
0x02,
0x11,
0x00,
0x03,
0x11,
0x00,
0xff,
0xda,
]);
}
function webpBytes(
width: number,
height: number,
animated = false,
): Uint8Array {
const chunkType = animated ? "VP8X" : "VP8 ";
const payload = new Uint8Array(10);
if (animated) {
payload[0] = 0x02;
writeUint24LittleEndian(payload, 4, width - 1);
writeUint24LittleEndian(payload, 7, height - 1);
} else {
payload.set([0x9d, 0x01, 0x2a], 3);
const view = new DataView(payload.buffer);
view.setUint16(6, width, true);
view.setUint16(8, height, true);
}
const chunk = concatenateBytes([
asciiBytes(chunkType),
littleEndianUint32(payload.byteLength),
payload,
]);
return concatenateBytes([
asciiBytes("RIFF"),
littleEndianUint32(4 + chunk.byteLength),
asciiBytes("WEBP"),
chunk,
]);
}
function avifBytes(
width: number,
height: number,
brand = "avif",
): Uint8Array {
const fileType = isoBox(
"ftyp",
concatenateBytes([
asciiBytes(brand),
new Uint8Array(4),
asciiBytes(brand),
]),
);
const spatialExtent = new Uint8Array(12);
const extentView = new DataView(spatialExtent.buffer);
extentView.setUint32(4, width);
extentView.setUint32(8, height);
const primaryItem = new Uint8Array(6);
new DataView(primaryItem.buffer).setUint16(4, 1);
const itemInfoEntry = new Uint8Array(13);
itemInfoEntry[0] = 2;
const itemInfoView = new DataView(itemInfoEntry.buffer);
itemInfoView.setUint16(4, 1);
writeAscii(itemInfoEntry, 8, "av01");
const itemInfo = new Uint8Array(6);
new DataView(itemInfo.buffer).setUint16(4, 1);
const propertyAssociation = new Uint8Array(12);
const associationView = new DataView(
propertyAssociation.buffer,
);
associationView.setUint32(4, 1);
associationView.setUint16(8, 1);
propertyAssociation[10] = 1;
propertyAssociation[11] = 0x81;
const properties = isoBox(
"iprp",
concatenateBytes([
isoBox("ipco", isoBox("ispe", spatialExtent)),
isoBox("ipma", propertyAssociation),
]),
);
const metadata = isoBox(
"meta",
concatenateBytes([
new Uint8Array(4),
isoBox("pitm", primaryItem),
isoBox(
"iinf",
concatenateBytes([
itemInfo,
isoBox("infe", itemInfoEntry),
]),
),
properties,
]),
);
return concatenateBytes([
fileType,
metadata,
isoBox("mdat", Uint8Array.of(0)),
]);
}
function isoBox(type: string, payload: Uint8Array): Uint8Array {
const box = new Uint8Array(8 + payload.byteLength);
const view = new DataView(box.buffer);
view.setUint32(0, box.byteLength);
writeAscii(box, 4, type);
box.set(payload, 8);
return box;
}
function littleEndianUint32(value: number): Uint8Array {
const bytes = new Uint8Array(4);
new DataView(bytes.buffer).setUint32(0, value, true);
return bytes;
}
function writeUint24LittleEndian(
bytes: Uint8Array,
offset: number,
value: number,
): void {
bytes[offset] = value & 0xff;
bytes[offset + 1] = (value >>> 8) & 0xff;
bytes[offset + 2] = (value >>> 16) & 0xff;
}
function asciiBytes(value: string): Uint8Array {
return Uint8Array.from(
[...value].map((character) => character.charCodeAt(0)),
);
}
function writeAscii(
target: Uint8Array,
offset: number,
value: string,
): void {
target.set(asciiBytes(value), offset);
}
function concatenateBytes(
chunks: readonly Uint8Array[],
): Uint8Array {
const combined = new Uint8Array(
chunks.reduce((total, chunk) => total + chunk.byteLength, 0),
);
let offset = 0;
for (const chunk of chunks) {
combined.set(chunk, offset);
offset += chunk.byteLength;
}
return combined;
}
function manualImageProbeScheduler(): Readonly<{
scheduler: ImageProbeScheduler;
fire(): void;
}> {
let callback: (() => void) | undefined;
return {
scheduler: {
setTimeout(nextCallback) {
callback = nextCallback;
return 1;
},
clearTimeout: vi.fn(),
},
fire() {
if (!callback) {
throw new TypeError("No image probe timeout is scheduled.");
}
callback();
},
};
}
function manualCapabilityVerificationScheduler(): Readonly<{
scheduler: ImageCapabilityVerificationScheduler;
delays: readonly number[];
clearTimeout: ReturnType<typeof vi.fn>;
fire(): void;
}> {
let callback: (() => void) | undefined;
const delays: number[] = [];
const clearTimeout = vi.fn();
return {
scheduler: {
setTimeout(nextCallback, milliseconds) {
callback = nextCallback;
delays.push(milliseconds);
return 1;
},
clearTimeout,
},
delays,
clearTimeout,
fire() {
if (!callback) {
throw new TypeError(
"No capability verification timeout is scheduled.",
);
}
callback();
},
};
}
function base64Url(bytes: Uint8Array): string {
let binary = "";
for (const byte of bytes) binary += String.fromCharCode(byte);
return btoa(binary)
.replace(/\+/gu, "-")
.replace(/\//gu, "_")
.replace(/=+$/gu, "");
}