Files
advx/src/services/FileValidator.ts
T
2025-04-10 23:07:21 -03:00

123 lines
3.4 KiB
TypeScript

import { createHash } from 'crypto';
import { ExifTool } from 'exiftool-vendored';
import imageSize from 'image-size';
import PDFParser from 'pdf2json';
import { detectFileType } from 'detect-file-type';
export class FileValidator {
private exiftool: ExifTool;
constructor() {
this.exiftool = new ExifTool();
}
async validateFile(file: Buffer, mimeType: string) {
// Validação básica de integridade
const basicValidation = await this.performBasicValidation(file);
if (!basicValidation.valid) {
return basicValidation;
}
// Validação específica por tipo
switch (mimeType) {
case 'application/pdf':
return await this.validatePDF(file);
case 'image/jpeg':
case 'image/png':
return await this.validateImage(file, mimeType);
default:
return { valid: false, error: 'Tipo de arquivo não suportado' };
}
}
private async performBasicValidation(file: Buffer) {
// Verificar tamanho mínimo
if (file.length < 100) {
return { valid: false, error: 'Arquivo muito pequeno ou vazio' };
}
// Verificar assinatura do arquivo
const fileType = await detectFileType(file);
if (!fileType) {
return { valid: false, error: 'Tipo de arquivo não reconhecido' };
}
return { valid: true };
}
private async validatePDF(file: Buffer) {
return new Promise((resolve) => {
const pdfParser = new PDFParser();
pdfParser.on('pdfParser_dataReady', () => {
resolve({ valid: true, metadata: pdfParser.getAllFieldsTypes() });
});
pdfParser.on('pdfParser_dataError', (error) => {
resolve({ valid: false, error: `PDF inválido: ${error}` });
});
pdfParser.parseBuffer(file);
});
}
private async validateImage(file: Buffer, mimeType: string) {
try {
// Verificar dimensões
const dimensions = imageSize(file);
if (!dimensions.width || !dimensions.height) {
return { valid: false, error: 'Dimensões da imagem inválidas' };
}
// Extrair metadados EXIF
const metadata = await this.exiftool.readBuffer(file);
// Verificações específicas para JPEG
if (mimeType === 'image/jpeg') {
if (!metadata.FileType || metadata.FileType !== 'JPEG') {
return { valid: false, error: 'Assinatura JPEG inválida' };
}
}
return {
valid: true,
metadata: {
dimensions,
exif: metadata
}
};
} catch (error) {
return { valid: false, error: `Erro na validação da imagem: ${error.message}` };
}
}
async validateMetadata(metadata: any) {
const requiredFields = ['userAgent', 'ipAddress', 'timestamp'];
const missingFields = requiredFields.filter(field => !metadata[field]);
if (missingFields.length > 0) {
return {
valid: false,
error: `Campos obrigatórios ausentes: ${missingFields.join(', ')}`
};
}
return { valid: true };
}
generateHashes(file: Buffer) {
return {
sha256: createHash('sha256').update(file).digest('hex'),
sha512: createHash('sha512').update(file).digest('hex'),
md5: createHash('md5').update(file).digest('hex'),
blake2b: createHash('blake2b512').update(file).digest('hex'), // Hash adicional
whirlpool: createHash('whirlpool').update(file).digest('hex'), // Hash adicional
};
}
async cleanup() {
await this.exiftool.end();
}
}