47 lines
1.9 KiB
TypeScript
47 lines
1.9 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();
|
|
});
|
|
});
|