import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as plugins from '../plugins.js';
import { EInvoice, FormatDetector } from '../../../ts/index.js';
import { PerformanceTracker } from '../performance.tracker.js';
const performanceTracker = new PerformanceTracker('SEC-06: Memory DoS Prevention');
tap.test('SEC-06: Memory DoS Prevention - should prevent memory exhaustion attacks', async () => {
// Test 1: Large attribute count attack (reduced for practical testing)
const largeAttributeAttack = await performanceTracker.measureAsync(
'large-attribute-count-attack',
async () => {
// Create XML with many attributes (reduced from 1M to 10K for practical testing)
let attributes = '';
const attrCount = 10000;
for (let i = 0; i < attrCount; i++) {
attributes += ` attr${i}="value${i}"`;
}
const maliciousXML = `
test
2024-01-01
`;
const startMemory = process.memoryUsage();
const startTime = Date.now();
try {
await EInvoice.fromXml(maliciousXML);
const endMemory = process.memoryUsage();
const endTime = Date.now();
const memoryIncrease = endMemory.heapUsed - startMemory.heapUsed;
const timeTaken = endTime - startTime;
return {
prevented: memoryIncrease < 100 * 1024 * 1024, // Less than 100MB
memoryIncrease,
timeTaken,
attributeCount: attrCount
};
} catch (error) {
return {
prevented: true,
rejected: true,
error: error.message
};
}
}
);
console.log('Large attribute attack result:', largeAttributeAttack);
expect(largeAttributeAttack.prevented).toEqual(true);
// Test 2: Deep nesting attack (reduced depth)
const deepNestingAttack = await performanceTracker.measureAsync(
'deep-nesting-attack',
async () => {
// Create deeply nested XML (reduced from 50K to 500 for practical testing)
const depth = 500;
let xml = '\n';
for (let i = 0; i < depth; i++) {
xml += ``;
}
xml += 'data';
for (let i = 0; i < depth; i++) {
xml += ``;
}
xml += 'test2024-01-01';
const startMemory = process.memoryUsage();
try {
await EInvoice.fromXml(xml);
const endMemory = process.memoryUsage();
const memoryIncrease = endMemory.heapUsed - startMemory.heapUsed;
return {
prevented: memoryIncrease < 50 * 1024 * 1024, // Less than 50MB
memoryIncrease,
depth
};
} catch (error) {
// Stack overflow or depth limit is also prevention
return {
prevented: true,
rejected: true,
error: error.message
};
}
}
);
console.log('Deep nesting attack result:', deepNestingAttack);
expect(deepNestingAttack.prevented).toEqual(true);
// Test 3: Large element content
const largeContentAttack = await performanceTracker.measureAsync(
'large-content-attack',
async () => {
// Create XML with very large content
const contentSize = 10 * 1024 * 1024; // 10MB
const largeContent = 'A'.repeat(contentSize);
const xml = `
test
${largeContent}
2024-01-01
`;
const startMemory = process.memoryUsage();
try {
await EInvoice.fromXml(xml);
const endMemory = process.memoryUsage();
const memoryIncrease = endMemory.heapUsed - startMemory.heapUsed;
return {
// Should handle large content efficiently
efficient: memoryIncrease < contentSize * 3, // Allow up to 3x content size
memoryIncrease,
contentSize
};
} catch (error) {
return {
efficient: true,
rejected: true,
error: error.message
};
}
}
);
console.log('Large content attack result:', largeContentAttack);
expect(largeContentAttack.efficient).toEqual(true);
// Test 4: Entity expansion attack
const entityExpansionAttack = await performanceTracker.measureAsync(
'entity-expansion-attack',
async () => {
// Billion laughs attack variant
const xml = `
]>
&lol5;
2024-01-01
`;
const startMemory = process.memoryUsage();
try {
await EInvoice.fromXml(xml);
const endMemory = process.memoryUsage();
const memoryIncrease = endMemory.heapUsed - startMemory.heapUsed;
return {
prevented: memoryIncrease < 10 * 1024 * 1024, // Less than 10MB
memoryIncrease
};
} catch (error) {
// Parser should reject or limit entity expansion
return {
prevented: true,
rejected: true,
error: error.message
};
}
}
);
console.log('Entity expansion attack result:', entityExpansionAttack);
expect(entityExpansionAttack.prevented).toEqual(true);
// Test 5: Quadratic blowup via attribute value normalization
const quadraticBlowupAttack = await performanceTracker.measureAsync(
'quadratic-blowup-attack',
async () => {
// Create attribute with many spaces that might be normalized
const spaceCount = 100000;
const spaces = ' '.repeat(spaceCount);
const xml = `
test
2024-01-01
`;
const startTime = Date.now();
try {
await EInvoice.fromXml(xml);
const endTime = Date.now();
const timeTaken = endTime - startTime;
return {
prevented: timeTaken < 5000, // Should process in under 5 seconds
timeTaken,
spaceCount
};
} catch (error) {
return {
prevented: true,
rejected: true,
error: error.message
};
}
}
);
console.log('Quadratic blowup attack result:', quadraticBlowupAttack);
expect(quadraticBlowupAttack.prevented).toEqual(true);
// Test 6: Multiple large attachments
const largeAttachmentsAttack = await performanceTracker.measureAsync(
'large-attachments-attack',
async () => {
// Create multiple large base64 attachments
const attachmentSize = 1 * 1024 * 1024; // 1MB each
const attachmentCount = 10;
const base64Data = Buffer.from('A'.repeat(attachmentSize)).toString('base64');
let attachments = '';
for (let i = 0; i < attachmentCount; i++) {
attachments += `
${i}
${base64Data}
`;
}
const xml = `
test
2024-01-01
${attachments}
`;
const startMemory = process.memoryUsage();
try {
await EInvoice.fromXml(xml);
const endMemory = process.memoryUsage();
const memoryIncrease = endMemory.heapUsed - startMemory.heapUsed;
return {
// Should handle attachments efficiently
efficient: memoryIncrease < attachmentSize * attachmentCount * 5,
memoryIncrease,
totalSize: attachmentSize * attachmentCount
};
} catch (error) {
return {
efficient: true,
rejected: true,
error: error.message
};
}
}
);
console.log('Large attachments attack result:', largeAttachmentsAttack);
expect(largeAttachmentsAttack.efficient).toEqual(true);
// Test 7: Format detection with large input
const largeFormatDetection = await performanceTracker.measureAsync(
'large-format-detection',
async () => {
// Large input for format detection
const size = 5 * 1024 * 1024; // 5MB
const content = '' + 'A'.repeat(size) + '';
const startMemory = process.memoryUsage();
const startTime = Date.now();
try {
const format = FormatDetector.detectFormat(content);
const endMemory = process.memoryUsage();
const endTime = Date.now();
return {
efficient: endTime - startTime < 1000, // Should be fast
memoryIncrease: endMemory.heapUsed - startMemory.heapUsed,
timeTaken: endTime - startTime,
format
};
} catch (error) {
return {
efficient: true,
error: error.message
};
}
}
);
console.log('Large format detection result:', largeFormatDetection);
expect(largeFormatDetection.efficient).toEqual(true);
console.log('Memory DoS prevention tests completed');
});
// Run the test
tap.start();