T-HUB/apps/api/src/storage/storage.test.ts
glass beadian aa5e262f3a Tactical Media Broadcast Hub — initial release
Self-hosted multi-platform broadcast newsroom for activist collectives:
publishing pipeline with group approval and DST-proof scheduling, TweetDeck-style
column wall with hostile-content-safe rendering, live control room with Auto
Director, bridged chat governance with polls and quorum, distributed sandboxed
transcode workers, encrypted content-addressed archive, monthly analytics.

MIT licensed. See README.md and docs/.
2026-10-10 11:56:42 +00:00

118 lines
4.8 KiB
TypeScript

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<unknown> {
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"]);
});
});