fix(api): reject non-PDF inputs in pdf-to-image

acceptedInputs is [.pdf], but pdf-to-image validated by calling mupdf.openDocument(buf, 'application/pdf'); mupdf sniffs the real format and opens JPEGs/PNGs/etc. as 1-page image-documents, returning 200. So non-PDF (incl. truncated/hostile) inputs were accepted, violating the contract and the hostile-input robustness check -- the one pre-existing failure surfaced by the full integration run. Gate all three endpoints (convert/info/preview) on the %PDF- magic bytes. Verified: truncated.jpg -> 400, valid PDF -> 200; the hostile-inputs test passes.
This commit is contained in:
SnapOtter
2026-06-17 14:28:41 +08:00
parent 965501aef9
commit 37b9c6d7bf
+19
View File
@@ -171,6 +171,13 @@ async function readPdfFromParts(
return { fileBuffer, settingsRaw };
}
// PDFs begin with "%PDF-" within the first bytes. mupdf.openDocument sniffs the
// real format and would otherwise accept non-PDF inputs (images, etc.),
// violating the .pdf-only contract; gate on the magic bytes up front.
function isPdfBuffer(buf: Buffer): boolean {
return buf.subarray(0, 1024).includes("%PDF-");
}
// ── Route registration ───────────────────────────────────────────
export function registerPdfToImage(app: FastifyInstance) {
// ── Info endpoint ────────────────────────────────────────────
@@ -190,6 +197,10 @@ export function registerPdfToImage(app: FastifyInstance) {
return reply.status(400).send({ error: "No PDF file provided" });
}
if (!isPdfBuffer(fileBuffer)) {
return reply.status(400).send({ error: "Invalid or corrupt PDF file" });
}
let doc: mupdf.Document | null = null;
try {
doc = mupdf.Document.openDocument(fileBuffer, "application/pdf");
@@ -228,6 +239,10 @@ export function registerPdfToImage(app: FastifyInstance) {
return reply.status(400).send({ error: "No PDF file provided" });
}
if (!isPdfBuffer(fileBuffer)) {
return reply.status(400).send({ error: "Invalid or corrupt PDF file" });
}
let doc: mupdf.Document | null = null;
try {
doc = mupdf.Document.openDocument(fileBuffer, "application/pdf");
@@ -292,6 +307,10 @@ export function registerPdfToImage(app: FastifyInstance) {
return reply.status(400).send({ error: "No PDF file provided" });
}
if (!isPdfBuffer(fileBuffer)) {
return reply.status(400).send({ error: "Invalid or corrupt PDF file" });
}
let settings: z.infer<typeof settingsSchema>;
try {
const parsed = settingsRaw ? JSON.parse(settingsRaw) : {};