security: workbook path allowlist + stronger image polyglot validation (#54)
- dispo workbook imports are pinned to DISPO_IMPORT_DIR (default ./imports): tRPC input rejects absolute paths and .. segments, runtime reader re-validates containment via path.relative. Closes a path-traversal class that reached ExcelJS CVEs through admin/compromised tokens. - image validator now checks the full 8-byte PNG magic, enforces PNG IEND and JPEG EOI trailers, scans the decoded buffer for markup polyglot markers (<script, <svg, <iframe, javascript:, onerror=, ...), and explicitly rejects SVG. Provider-generated covers (DALL-E, Gemini) run through the same validator before persistence — an untrusted upstream cannot smuggle a stored-XSS payload past us. - added image-validation.test.ts and tightened documentation. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -58,22 +58,22 @@ describe("assistant dispo import batch delegation tools", () => {
|
||||
const result = await executeTool(
|
||||
"stage_dispo_import_batch",
|
||||
JSON.stringify({
|
||||
chargeabilityWorkbookPath: "/imports/chargeability.xlsx",
|
||||
planningWorkbookPath: "/imports/planning.xlsx",
|
||||
referenceWorkbookPath: "/imports/reference.xlsx",
|
||||
costWorkbookPath: "/imports/cost.xlsx",
|
||||
rosterWorkbookPath: "/imports/roster.xlsx",
|
||||
chargeabilityWorkbookPath: "chargeability.xlsx",
|
||||
planningWorkbookPath: "planning.xlsx",
|
||||
referenceWorkbookPath: "reference.xlsx",
|
||||
costWorkbookPath: "cost.xlsx",
|
||||
rosterWorkbookPath: "roster.xlsx",
|
||||
notes: "March import",
|
||||
}),
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(stageDispoImportBatch).toHaveBeenCalledWith(ctx.db, {
|
||||
chargeabilityWorkbookPath: "/imports/chargeability.xlsx",
|
||||
planningWorkbookPath: "/imports/planning.xlsx",
|
||||
referenceWorkbookPath: "/imports/reference.xlsx",
|
||||
costWorkbookPath: "/imports/cost.xlsx",
|
||||
rosterWorkbookPath: "/imports/roster.xlsx",
|
||||
chargeabilityWorkbookPath: "chargeability.xlsx",
|
||||
planningWorkbookPath: "planning.xlsx",
|
||||
referenceWorkbookPath: "reference.xlsx",
|
||||
costWorkbookPath: "cost.xlsx",
|
||||
rosterWorkbookPath: "roster.xlsx",
|
||||
notes: "March import",
|
||||
});
|
||||
expect(JSON.parse(result.content)).toEqual({
|
||||
@@ -92,18 +92,18 @@ describe("assistant dispo import batch delegation tools", () => {
|
||||
const result = await executeTool(
|
||||
"validate_dispo_import_batch",
|
||||
JSON.stringify({
|
||||
chargeabilityWorkbookPath: "/imports/chargeability.xlsx",
|
||||
planningWorkbookPath: "/imports/planning.xlsx",
|
||||
referenceWorkbookPath: "/imports/reference.xlsx",
|
||||
chargeabilityWorkbookPath: "chargeability.xlsx",
|
||||
planningWorkbookPath: "planning.xlsx",
|
||||
referenceWorkbookPath: "reference.xlsx",
|
||||
importBatchId: "batch_1",
|
||||
}),
|
||||
ctx,
|
||||
);
|
||||
|
||||
expect(assessDispoImportReadiness).toHaveBeenCalledWith({
|
||||
chargeabilityWorkbookPath: "/imports/chargeability.xlsx",
|
||||
planningWorkbookPath: "/imports/planning.xlsx",
|
||||
referenceWorkbookPath: "/imports/reference.xlsx",
|
||||
chargeabilityWorkbookPath: "chargeability.xlsx",
|
||||
planningWorkbookPath: "planning.xlsx",
|
||||
referenceWorkbookPath: "reference.xlsx",
|
||||
importBatchId: "batch_1",
|
||||
});
|
||||
expect(JSON.parse(result.content)).toEqual({
|
||||
|
||||
Reference in New Issue
Block a user