feat: add PDF magic bytes validation, 410 Gone, and manual extraction tests (refs #144)
Add filename .pdf extension fallback and %PDF magic bytes validation to extractManual controller. Update getJobStatus to return 410 Gone for expired jobs. Add 16 unit tests covering all acceptance criteria. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -371,12 +371,16 @@ export class OcrController {
|
||||
}
|
||||
|
||||
const contentType = file.mimetype as string;
|
||||
if (contentType !== 'application/pdf') {
|
||||
const fileName = file.filename as string | undefined;
|
||||
const isPdfMime = contentType === 'application/pdf';
|
||||
const isPdfExtension = fileName?.toLowerCase().endsWith('.pdf') ?? false;
|
||||
|
||||
if (!isPdfMime && !isPdfExtension) {
|
||||
logger.warn('Non-PDF file provided for manual extraction', {
|
||||
operation: 'ocr.controller.extractManual.not_pdf',
|
||||
userId,
|
||||
contentType,
|
||||
fileName: file.filename,
|
||||
fileName,
|
||||
});
|
||||
return reply.code(400).send({
|
||||
error: 'Bad Request',
|
||||
@@ -394,7 +398,7 @@ export class OcrController {
|
||||
logger.warn('Empty file provided for manual extraction', {
|
||||
operation: 'ocr.controller.extractManual.empty_file',
|
||||
userId,
|
||||
fileName: file.filename,
|
||||
fileName,
|
||||
});
|
||||
return reply.code(400).send({
|
||||
error: 'Bad Request',
|
||||
@@ -402,6 +406,21 @@ export class OcrController {
|
||||
});
|
||||
}
|
||||
|
||||
// Validate PDF magic bytes (%PDF)
|
||||
const PDF_MAGIC = Buffer.from('%PDF');
|
||||
if (fileBuffer.length < 4 || !fileBuffer.subarray(0, 4).equals(PDF_MAGIC)) {
|
||||
logger.warn('File lacks PDF magic bytes', {
|
||||
operation: 'ocr.controller.extractManual.invalid_magic',
|
||||
userId,
|
||||
fileName,
|
||||
firstBytes: fileBuffer.subarray(0, 4).toString('hex'),
|
||||
});
|
||||
return reply.code(415).send({
|
||||
error: 'Unsupported Media Type',
|
||||
message: 'File does not appear to be a valid PDF (missing %PDF header)',
|
||||
});
|
||||
}
|
||||
|
||||
// Get optional vehicle_id from form fields
|
||||
const vehicleId = file.fields?.vehicle_id?.value as string | undefined;
|
||||
|
||||
@@ -577,9 +596,9 @@ export class OcrController {
|
||||
|
||||
return reply.code(200).send(result);
|
||||
} catch (error: any) {
|
||||
if (error.statusCode === 404) {
|
||||
return reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
if (error.statusCode === 410) {
|
||||
return reply.code(410).send({
|
||||
error: 'Gone',
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user