import { tap, expect } from '@git.zone/tstest/tapbundle'; import * as plugins from '../plugins.js'; import { PDFExtractor } from '../../../ts/index.js'; import { PerformanceTracker } from '../performance.tracker.js'; const performanceTracker = new PerformanceTracker('SEC-03: PDF Malware Detection'); tap.test('SEC-03: PDF Malware Detection - should detect and prevent malicious PDFs', async () => { // Test 1: Test PDF extraction with potentially malicious content const javascriptDetection = await performanceTracker.measureAsync( 'javascript-in-pdf-extraction', async () => { // Create a mock PDF with JavaScript content const pdfWithJS = createMockPDFWithContent('/JS (alert("malicious"))'); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(pdfWithJS); // If extraction succeeds, check if any XML was found return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0), threat: 'javascript' }; } catch (error) { // If it throws, that's expected for malicious content return { extracted: false, xmlFound: false, threat: 'javascript', error: error.message }; } } ); console.log('JavaScript detection result:', javascriptDetection); // PDFs with JavaScript might still be processed, but shouldn't contain invoice XML expect(javascriptDetection.xmlFound).toEqual(false); // Test 2: Test with embedded executable references const embeddedExecutable = await performanceTracker.measureAsync( 'embedded-executable-detection', async () => { // Create a mock PDF with embedded EXE reference const pdfWithExe = createMockPDFWithContent( '/EmbeddedFiles <>>>]>>' ); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(pdfWithExe); return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0), threat: 'executable' }; } catch (error) { return { extracted: false, xmlFound: false, threat: 'executable', error: error.message }; } } ); console.log('Embedded executable result:', embeddedExecutable); expect(embeddedExecutable.xmlFound).toEqual(false); // Test 3: Test with 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 <>>>]>>' ); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(pdfWithForm); return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0), threat: 'form-action' }; } catch (error) { return { extracted: false, xmlFound: false, threat: 'form-action', error: error.message }; } } ); console.log('Form actions result:', suspiciousFormActions); expect(suspiciousFormActions.xmlFound).toEqual(false); // Test 4: Test with malformed PDF structure const malformedPDF = await performanceTracker.measureAsync( 'malformed-pdf-handling', async () => { // Create a malformed PDF const badPDF = Buffer.from('Not a valid PDF content'); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(badPDF); return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0), error: null }; } catch (error) { return { extracted: false, xmlFound: false, error: error.message }; } } ); console.log('Malformed PDF result:', malformedPDF); expect(malformedPDF.extracted).toEqual(false); // Test 5: Test with extremely large mock PDF const largePDFTest = await performanceTracker.measureAsync( 'large-pdf-handling', async () => { // Create a PDF with lots of repeated content const largeContent = '/Pages '.repeat(10000); const largePDF = createMockPDFWithContent(largeContent); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(largePDF); return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0), size: largePDF.length }; } catch (error) { return { extracted: false, xmlFound: false, size: largePDF.length, error: error.message }; } } ); console.log('Large PDF result:', largePDFTest); // Large PDFs might fail or succeed, but shouldn't contain valid invoice XML expect(largePDFTest.xmlFound).toEqual(false); // Test 6: Test EICAR pattern in PDF const eicarTest = await performanceTracker.measureAsync( 'eicar-test-pattern', 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 <>${eicarString}>>>>]>>` ); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(pdfWithEicar); return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0), threat: 'eicar-test' }; } catch (error) { return { extracted: false, xmlFound: false, threat: 'eicar-test', error: error.message }; } } ); console.log('EICAR test result:', eicarTest); expect(eicarTest.xmlFound).toEqual(false); // Test 7: Test empty PDF const emptyPDFTest = await performanceTracker.measureAsync( 'empty-pdf-handling', async () => { const emptyPDF = Buffer.from(''); try { const extractor = new PDFExtractor(); const result = await extractor.extractXml(emptyPDF); return { extracted: result.success, xmlFound: !!(result.xml && result.xml.length > 0) }; } catch (error) { return { extracted: false, xmlFound: false, error: error.message }; } } ); console.log('Empty PDF result:', emptyPDFTest); expect(emptyPDFTest.extracted).toEqual(false); // Performance tracking complete - summary is tracked in the static PerformanceTracker }); // 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<>\n`; const eof = `startxref\n${pdfHeader.length + pdfContent.length}\n%%EOF`; return Buffer.from(pdfHeader + pdfContent + xref + trailer + eof); } // Run the test tap.start();