chore: sync the frontend template from 4dc033c to 8157ad4

The product was materialized from the template at `4dc033c` and has stayed
on it through 43 template commits, so it was missing all three rounds of
adapter remediation — including files it never had, such as the shared
`abortable-operation` primitive and the `exact-snapshot` decoder that
later fixes are written against. Taking only the newest round was not
possible for that reason: the delta is coherent only as a whole.

The product had not touched `src/adapters` at all since materialization,
so the 140-file delta applied with a three-way merge and no conflicts.
`package.json` was the single overlap and merged cleanly: the product owns
`name`, the template contributed `check:adapter-inventory`,
`check:remediation-ledger` and the image-resolve-signal type fixture.
All 24 product-owned files — README, index.html, CI workflow, i18n
catalog, home page, generated schemas, evidence scripts, component and
visual snapshots — are byte-identical to `main`.

`template.lock.json` now pins the synced revision and tree.

Verified in this repository, not inherited from the template: six type
projects, lint, nine gates (adapter inventory, remediation ledger,
registries, diagnostics, realtime boundaries, architecture, browser
file/storage boundaries, optional recipes, documentation), the production
build, and 2,054 of 2,073 tests. The 19 failures are all in
`tests/unit/ci-artifact-contract.test.ts` and are the same pre-existing
sandbox RLIMIT, EMFILE, umask and `/tmp` permission behaviour the template
records; four suites that failed once under parallel load pass in
isolation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-08-15 12:04:58 +09:00
co-authored by Claude Opus 5
parent 002ba3624e
commit 4bff9ca151
142 changed files with 23010 additions and 1544 deletions
@@ -1,5 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { PRESIGNED_TRANSFER_PROTOCOL } from "../../src/application/ports/browser-transfer/presigned-transfer.ts";
import type {
PresignedUploadPartCapabilityProvider,
} from "../../src/application/ports/browser-transfer/presigned-transfer.ts";
@@ -156,6 +158,182 @@ function rangeSource(bytes: Uint8Array) {
}
describe("resumable upload HTTP control plane", () => {
/**
* TR-RR-08. `Object.keys` sees only enumerable own string keys, so a symbol
* or non-enumerable extra passed unseen and a later property read invoked
* whatever accessor the sender installed — escaping the Result contract as a
* rejection of a public method.
*/
it("closes every hostile control-plane object as typed CORRUPT_DATA", async () => {
const fingerprint: UploadFileFingerprint = Object.freeze({
algorithm: "SHA-256-PARTS-V1",
digestHex: "a".repeat(64),
byteLength: 4,
partSizeBytes: 4,
partCount: 1,
});
const validSession = () => ({
protocol: RESUMABLE_UPLOAD_PROTOCOL,
sessionId: "session_01",
requestBindingSha256: "b".repeat(64),
fingerprint,
partSizeBytes: 4,
partCount: 1,
maxConcurrency: 1,
expiresAtEpochMs: NOW + 10_000,
});
const hostile: readonly (readonly [string, () => unknown])[] = [
[
"throwing getter",
() => {
const value = validSession() as Record<string, unknown>;
Object.defineProperty(value, "sessionId", {
configurable: true,
enumerable: true,
get: () => {
throw new TypeError("hostile getter");
},
});
return value;
},
],
[
"symbol key",
() => ({ ...validSession(), [Symbol("injected")]: "leak" }),
],
[
"non-enumerable extra",
() => {
const value = validSession() as Record<string, unknown>;
Object.defineProperty(value, "signedUrl", {
configurable: true,
enumerable: false,
value: "https://objects.example/secret?signature=leak",
});
return value;
},
],
[
"ownKeys trap",
() =>
new Proxy(validSession() as Record<string, unknown>, {
ownKeys() {
throw new TypeError("hostile ownKeys");
},
}),
],
[
"getOwnPropertyDescriptor trap",
() =>
new Proxy(validSession() as Record<string, unknown>, {
getOwnPropertyDescriptor() {
throw new TypeError("hostile descriptor");
},
}),
],
[
"a nested fingerprint with an extra field",
() => ({
...validSession(),
fingerprint: { ...fingerprint, injected: true },
}),
],
[
"a nested fingerprint behind an accessor",
() => {
const value = validSession() as Record<string, unknown>;
Object.defineProperty(value, "fingerprint", {
configurable: true,
enumerable: true,
get: () => fingerprint,
});
return value;
},
],
[
"a custom prototype",
() =>
Object.assign(Object.create({ injected: true }), validSession()),
],
];
for (const [label, build] of hostile) {
const control = createResumableUploadHttpControlPlane({
transport: {
async execute() {
return browserDataSuccess(build());
},
},
partCapabilities: { issueUploadPart: vi.fn() },
});
const result = await control.createSession({
protocol: RESUMABLE_UPLOAD_PROTOCOL,
uploadKey: "upload_key_strict",
purpose: "attachment",
mediaType: "application/octet-stream",
requestBindingSha256: "b".repeat(64),
fingerprint,
requestedPartSizeBytes: 4,
requestedMaxConcurrency: 1,
idempotencyKey: "upload-create-idempotency-01",
signal: activeSignal,
});
expect(result, label).toMatchObject({
ok: false,
error: { code: "CORRUPT_DATA", operation: "UPLOAD_SESSION" },
});
expect(JSON.stringify(result)).not.toContain("signature=leak");
}
/**
* TR-05. The decoder checked the sender's object and then read it again to
* build the result, so a stateful answer could show a safe `sessionId` to
* the regex and hand an unvalidated one to the receipt. Reading once means
* the value that was validated is the value that is returned.
*/
let sessionIdReads = 0;
const statefulControl = createResumableUploadHttpControlPlane({
transport: {
async execute() {
return browserDataSuccess(
new Proxy(validSession() as Record<string, unknown>, {
getOwnPropertyDescriptor(target, key) {
if (key === "sessionId") {
sessionIdReads += 1;
return {
configurable: true,
enumerable: true,
value: sessionIdReads > 1 ? "../../unsafe" : "session_01",
};
}
return Reflect.getOwnPropertyDescriptor(target, key);
},
}),
);
},
},
partCapabilities: { issueUploadPart: vi.fn() },
});
const stateful = await statefulControl.createSession({
protocol: RESUMABLE_UPLOAD_PROTOCOL,
uploadKey: "upload_key_strict",
purpose: "attachment",
mediaType: "application/octet-stream",
requestBindingSha256: "b".repeat(64),
fingerprint,
requestedPartSizeBytes: 4,
requestedMaxConcurrency: 1,
idempotencyKey: "upload-create-idempotency-02",
signal: activeSignal,
});
expect(sessionIdReads).toBe(1);
expect(JSON.stringify(stateful)).not.toContain("../../unsafe");
if (stateful.ok) {
expect(stateful.value.sessionId).toBe("session_01");
}
});
it("rejects unknown response fields so URLs cannot cross the DTO boundary", async () => {
const fingerprint: UploadFileFingerprint = Object.freeze({
algorithm: "SHA-256-PARTS-V1",
@@ -272,6 +450,8 @@ describe("resumable upload HTTP control plane", () => {
}),
);
return jsonResponseAt(CAPABILITY_ENDPOINT, {
// BT-PRE-02. The capability envelope declares its wire protocol.
protocol: PRESIGNED_TRANSFER_PROTOCOL,
capabilityReceipt: `capability-upload-${capabilitySequence}`,
method: "PUT",
binding: request.binding,