fix: complete presigned capability and upload transport contracts

BT-PRE-02: add the top-level PRESIGNED_TRANSFER_V1 protocol literal to the
capability request and response. A missing, V0 or V2 envelope is closed as
POLICY_REJECTED before the vault registers anything. The server negotiates by
request shape; fields are never dual-emitted into a strict decoder, and the
nested PRESIGNED_MULTIPART_V1 binding protocol is unchanged.

BT-PRE-03: aborting a controller does not settle a fetch that ignores its
signal, so both presigned scopes now race the task, cancel a late response body
and survive a throwing scheduler without leaking the external abort listener.

BT-PRE-04: the vault owns its registration invariants, re-checking method,
href/origin/path agreement, embedded credentials, byte bounds, digest shape and
expiry, so a second issuer cannot register a weaker capability of the same type.

BT-PRE-05: decode each path segment once and require it to round-trip through
the canonical uppercase percent encoder, closing %2f, %5c, %252e%252e, mixed-case
escapes and encoded NUL while still admitting valid opaque UTF-8 segments.

BT-UP-02: inject and snapshot the upload transport clock and scheduler, so
Retry-After delta-seconds and HTTP-date resolve against the same captured now
and a clock rollback clamps to zero instead of producing a negative delay.

BT-IMG-01: make the image resolve() lifetime signal required, replacing the
hidden PRIMARY_REQUIRED preset precondition with a type-level one, and add the
negative typecheck fixture and gate that prove it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-08-14 02:19:00 +09:00
co-authored by Claude Opus 5
parent 976c8a8da4
commit 000a2581af
13 changed files with 683 additions and 43 deletions
+10 -1
View File
@@ -203,6 +203,7 @@ describe("production image CDN runtime", () => {
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
if (!resolved.ok) return;
@@ -317,10 +318,11 @@ describe("production image CDN runtime", () => {
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
width: 9_999,
query: "format=svg",
src: "data:text/html,active",
} as Parameters<typeof runtime.presentation.resolve>[0]),
} as unknown as Parameters<typeof runtime.presentation.resolve>[0]),
).resolves.toMatchObject({
ok: false,
error: { code: "INVALID_INPUT" },
@@ -332,6 +334,7 @@ describe("production image CDN runtime", () => {
"card-landscape",
"render-public-product-image",
),
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
@@ -341,6 +344,7 @@ describe("production image CDN runtime", () => {
runtime.presentation.resolve({
asset: {} as typeof accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
@@ -385,6 +389,7 @@ describe("production image CDN runtime", () => {
const resolved = await runtime.presentation.resolve({
asset: accepted.value,
preset: presetReference,
signal: new AbortController().signal,
});
expect(resolved.ok).toBe(true);
if (!resolved.ok) return;
@@ -782,6 +787,7 @@ describe("production image CDN runtime", () => {
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
@@ -840,6 +846,7 @@ describe("production image CDN runtime", () => {
runtime.presentation.resolve({
asset: accepted.value,
preset: lazy,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,
@@ -1110,6 +1117,7 @@ describe("production image CDN runtime", () => {
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({ ok: true });
const acceptedPrivate =
@@ -1125,6 +1133,7 @@ describe("production image CDN runtime", () => {
runtime.presentation.resolve({
asset: accepted.value,
preset,
signal: new AbortController().signal,
}),
).resolves.toMatchObject({
ok: false,