56 lines
2.5 KiB
TypeScript
56 lines
2.5 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
UploadPolicyError,
|
|
createSignedFileToken,
|
|
enforceUploadPolicy,
|
|
inspectUpload,
|
|
sniffContentType,
|
|
verifySignedFileToken,
|
|
} from "../src/security.ts";
|
|
|
|
describe("upload policy hardening", () => {
|
|
test("enforces size, type, filename, and checksum independently", async () => {
|
|
const inspection = await inspectUpload("report.pdf", new Uint8Array([0x25, 0x50, 0x44, 0x46]));
|
|
const cases: Array<[Parameters<typeof enforceUploadPolicy>[1], string | undefined, string]> = [
|
|
[{ maxBytes: 3 }, undefined, "UPLOAD_TOO_LARGE"],
|
|
[{ accept: ["image/png"] }, undefined, "UPLOAD_TYPE_REJECTED"],
|
|
[{ filenamePattern: /^invoice-/ }, undefined, "UPLOAD_FILENAME_REJECTED"],
|
|
[{ requireChecksum: true }, undefined, "UPLOAD_CHECKSUM_REQUIRED"],
|
|
[{}, "0".repeat(64), "UPLOAD_CHECKSUM_MISMATCH"],
|
|
];
|
|
for (const [policy, checksum, code] of cases) {
|
|
try {
|
|
enforceUploadPolicy(inspection, policy, checksum);
|
|
throw new Error("policy unexpectedly accepted upload");
|
|
} catch (error) {
|
|
expect(error).toBeInstanceOf(UploadPolicyError);
|
|
expect((error as UploadPolicyError).code).toBe(code);
|
|
}
|
|
}
|
|
enforceUploadPolicy(
|
|
inspection,
|
|
{ maxBytes: 4, accept: ["application/pdf"] },
|
|
inspection.sha256,
|
|
);
|
|
});
|
|
|
|
test("recognizes bounded signatures and rejects malformed signed-token inputs", async () => {
|
|
expect(sniffContentType(new Uint8Array([0xff, 0xd8, 0xff]))).toBe("image/jpeg");
|
|
expect(sniffContentType(new Uint8Array([0x50, 0x4b, 0x03, 0x04]))).toBe("application/zip");
|
|
expect(sniffContentType(new Uint8Array([1, 2, 3]))).toBeNull();
|
|
await expect(
|
|
createSignedFileToken({ store: "", key: "x", expiresAt: 1 }, "long-enough-secret"),
|
|
).rejects.toThrow("store");
|
|
expect(await verifySignedFileToken("not-a-token", "long-enough-secret")).toBeNull();
|
|
});
|
|
|
|
test("signed file tokens reject expiry, tampering, and the wrong signing key", async () => {
|
|
const value = { store: "uploads", key: "safe/report.pdf", expiresAt: 2_000 };
|
|
const token = await createSignedFileToken(value, "long-enough-secret");
|
|
expect(await verifySignedFileToken(token, "long-enough-secret", 1_999)).toEqual(value);
|
|
expect(await verifySignedFileToken(token, "long-enough-secret", 2_000)).toBeNull();
|
|
expect(await verifySignedFileToken(`${token}x`, "long-enough-secret", 1_999)).toBeNull();
|
|
expect(await verifySignedFileToken(token, "different-long-secret", 1_999)).toBeNull();
|
|
});
|
|
});
|