123 lines
3.4 KiB
TypeScript
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();
|
|
}
|
|
}
|