import { beforeEach, describe, expect, it, vi } from "vitest"; import { createHash, randomBytes } from "node:crypto"; const s3Calls = vi.hoisted(() => ({ sent: [] as string[] })); vi.mock("@aws-sdk/client-s3", () => { class FakeCommand { constructor(readonly input: unknown, readonly marker: string) {} } class S3ServiceException extends Error { readonly $metadata: { httpStatusCode?: number }; constructor(message: string, status: number, name: string) { super(message); this.name = name; this.$metadata = { httpStatusCode: status }; } } return { CreateBucketCommand: class extends FakeCommand { constructor(input: unknown) { super(input, "Create"); } }, HeadObjectCommand: class extends FakeCommand { constructor(input: unknown) { super(input, "Head"); } }, PutObjectCommand: class extends FakeCommand { constructor(input: unknown) { super(input, "Put"); } }, GetObjectCommand: class extends FakeCommand { constructor(input: unknown) { super(input, "Get"); } }, S3ServiceException, S3Client: class { constructor(readonly config: unknown) {} async send(cmd: FakeCommand): Promise { s3Calls.sent.push(cmd.marker); if (cmd.marker === "Create") throw new S3ServiceException("Access Denied", 403, "AccessDenied"); if (cmd.marker === "Head") throw new S3ServiceException("Not Found", 404, "NotFound"); return {}; } }, }; }); import { S3Storage, extensionFor, keyForHash, open, seal, sha256 } from "./storage.js"; const TEST_KEK = createHash("sha256").update("test-kek").digest(); describe("storage key scheme (pure)", () => { it("maps content types to extensions, unknown -> bin", () => { expect(extensionFor("image/png")).toBe("png"); expect(extensionFor("video/mp4")).toBe("mp4"); expect(extensionFor("application/x-weird")).toBe("bin"); }); it("keys are content-addressed and deterministic", () => { const hash = sha256(Buffer.from("same bytes")); expect(keyForHash(hash, "image/png")).toBe(`sha256/${hash}.png`); expect(keyForHash(hash, "video/mp4")).toBe(`sha256/${hash}.mp4`); expect(keyForHash(sha256(Buffer.from("same bytes")), "image/png")).toBe(keyForHash(hash, "image/png")); expect(keyForHash(sha256(Buffer.from("other")), "image/png")).not.toBe(keyForHash(hash, "image/png")); }); it("hash is stable across buffer instances", () => { expect(sha256(Buffer.from("abc"))).toBe(sha256(Buffer.from("abc"))); expect(sha256(Buffer.from("abc"))).toHaveLength(64); }); }); describe("client-side encryption (pure)", () => { it("seal → open roundtrips arbitrary bytes", () => { const plaintext = randomBytes(1024 * 64); const sealed = seal(plaintext, TEST_KEK); expect(open(sealed, TEST_KEK).equals(plaintext)).toBe(true); }); it("ciphertext never contains plaintext (endpoint sees only sealed frame)", () => { const plaintext = Buffer.from("TOPSECRET-media-bytes-123"); const sealed = seal(plaintext, TEST_KEK); expect(sealed.includes(plaintext)).toBe(false); }); it("fresh CEK per seal → identical plaintext, different ciphertexts (dedup still by key)", () => { const plaintext = Buffer.from("identical"); const a = seal(plaintext, TEST_KEK); const b = seal(plaintext, TEST_KEK); expect(a.equals(b)).toBe(false); expect(open(a, TEST_KEK).equals(open(b, TEST_KEK))).toBe(true); }); it("tampered ciphertext fails GCM authentication", () => { const sealed = seal(Buffer.from("authentic"), TEST_KEK); const last = sealed.length - 1; sealed[last] = (sealed[last] ?? 0) ^ 0xff; expect(() => open(sealed, TEST_KEK)).toThrow(); }); it("wrong KEK cannot open", () => { const otherKek = createHash("sha256").update("other-kek").digest(); const sealed = seal(Buffer.from("private"), TEST_KEK); expect(() => open(sealed, otherKek)).toThrow(); }); it("plaintext hash addressing + seal preserves dedup semantics", () => { const a = Buffer.from("media"); const b = Buffer.from("media"); expect(sha256(a)).toBe(sha256(b)); expect(keyForHash(sha256(a), "video/mp4")).toBe(keyForHash(sha256(b), "video/mp4")); }); }); describe("S3Storage with object-scoped credentials", () => { beforeEach(() => { s3Calls.sent.length = 0; }); it("stores despite 403 on bucket create (pre-provisioned bucket, least-privilege token)", async () => { const storage = new S3Storage({ endpoint: "https://s3.example.invalid", accessKeyId: "scoped-key", secretAccessKey: "scoped-secret", bucket: "pre-provisioned", mediaKek: TEST_KEK, }); const stored = await storage.putContent(Buffer.from("scoped-token-probe"), "text/plain"); expect(stored.deduplicated).toBe(false); expect(s3Calls.sent).toEqual(["Create", "Head", "Put"]); }); });