feat(import): harden untrusted spreadsheet boundaries

This commit is contained in:
2026-03-30 08:02:52 +02:00
parent fac8c1c3a5
commit f6daf21983
13 changed files with 561 additions and 76 deletions
@@ -0,0 +1,58 @@
import { cp, mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { afterEach, describe, expect, it } from "vitest";
import {
MAX_DISPO_WORKBOOK_BYTES,
readWorksheetMatrix,
} from "../use-cases/dispo-import/read-workbook.js";
const referenceWorkbookPath = fileURLToPath(
new URL("../../../../samples/Dispov2/MandatoryDispoCategories_V3.xlsx", import.meta.url),
);
const tempDirectories: string[] = [];
afterEach(async () => {
await Promise.all(
tempDirectories.splice(0).map(async (directory) => {
await rm(directory, { recursive: true, force: true });
}),
);
});
async function makeTempDirectory(): Promise<string> {
const directory = await mkdtemp(path.join(os.tmpdir(), "capakraken-read-workbook-"));
tempDirectories.push(directory);
return directory;
}
describe("readWorksheetMatrix", () => {
it("reads trusted xlsx worksheets through the hardened reader", async () => {
const rows = await readWorksheetMatrix(referenceWorkbookPath, "EID-Attr");
expect(rows.length).toBeGreaterThan(0);
expect(rows.some((row) => row.length > 0)).toBe(true);
});
it("rejects legacy .xls workbook paths", async () => {
const directory = await makeTempDirectory();
const legacyPath = path.join(directory, "legacy-input.xls");
await cp(referenceWorkbookPath, legacyPath);
await expect(readWorksheetMatrix(legacyPath, "EID-Attr")).rejects.toThrow(
'Only .xlsx workbooks are supported for dispo imports',
);
});
it("rejects oversized workbook files before parsing", async () => {
const directory = await makeTempDirectory();
const oversizedPath = path.join(directory, "oversized.xlsx");
await writeFile(oversizedPath, Buffer.alloc(MAX_DISPO_WORKBOOK_BYTES + 1, 0));
await expect(readWorksheetMatrix(oversizedPath, "Sheet1")).rejects.toThrow(
"Workbook file exceeds the",
);
});
});