UNPKG

npm-api-analyzer

Version:

CLI tool to analyze npm packages for network API usage, prototype pollution, and security vulnerabilities

260 lines (210 loc) 9.13 kB
import { APIDetector } from '../src/apiDetector'; describe('Performance Tests', () => { const measureTime = (fn: () => void): number => { const start = Date.now(); fn(); return Date.now() - start; }; const generateTestCode = (iterations: number, pattern: string): string => { return Array(iterations).fill(pattern).join('\n'); }; describe('Pattern Detection Performance', () => { test('should handle large files efficiently', () => { const largeCode = generateTestCode(1000, 'Object.prototype.test = true;'); const duration = measureTime(() => { APIDetector.detectAPIUsages(largeCode, 'large-test.js'); }); // Should complete within reasonable time (less than 100ms for 1000 patterns) expect(duration).toBeLessThan(100); }); test('should handle mixed patterns efficiently', () => { const mixedPatterns = [ 'fetch("/api/data");', 'Object.prototype.polluted = true;', 'eval("malicious");', 'document.createElement("div");', 'obj.__proto__ = evil;', 'for (let k in user) { target[k] = user[k]; }', 'JSON.parse(\'{"__proto__": {}}\');', 'new WebSocket("ws://test");' ]; const largeCode = Array(100).fill(mixedPatterns.join('\n')).join('\n'); const duration = measureTime(() => { const results = APIDetector.detectAPIUsages(largeCode, 'mixed-test.js'); expect(results.length).toBeGreaterThan(0); }); // Should handle 800 mixed patterns efficiently expect(duration).toBeLessThan(50); }); test('should maintain performance with repetitive calls', () => { const testCode = ` Object.prototype.admin = true; fetch('/api/user'); eval('dangerous'); obj.__proto__ = malicious; `; const iterations = 100; const durations: number[] = []; for (let i = 0; i < iterations; i++) { const duration = measureTime(() => { APIDetector.detectAPIUsages(testCode, `test-${i}.js`); }); durations.push(duration); } const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length; const maxDuration = Math.max(...durations); // Average should be very fast expect(avgDuration).toBeLessThan(2); // No single call should be too slow expect(maxDuration).toBeLessThan(10); }); }); describe('Memory Usage Tests', () => { test('should not cause memory leaks with repeated calls', () => { const testCode = 'Object.prototype.test = "value";'; const initialMemory = process.memoryUsage().heapUsed; // Run many iterations for (let i = 0; i < 1000; i++) { APIDetector.detectAPIUsages(testCode, `memory-test-${i}.js`); } // Force garbage collection if available if (global.gc) { global.gc(); } const finalMemory = process.memoryUsage().heapUsed; const memoryIncrease = finalMemory - initialMemory; // Memory increase should be reasonable (less than 10MB) expect(memoryIncrease).toBeLessThan(10 * 1024 * 1024); }); test('should handle very long single lines', () => { const longLine = 'const data = "' + 'x'.repeat(10000) + '"; Object.prototype.test = data;'; const duration = measureTime(() => { const results = APIDetector.detectAPIUsages(longLine, 'long-line-test.js'); expect(results.length).toBeGreaterThan(0); }); // Should handle long lines without significant performance impact expect(duration).toBeLessThan(20); }); }); describe('Regex Pattern Efficiency', () => { test('should not have exponential backtracking issues', () => { // Create patterns that could cause catastrophic backtracking const problematicPatterns = [ 'Object.prototype.' + 'a'.repeat(1000) + ' = value;', 'obj["' + 'b'.repeat(1000) + '"] = test;', 'for (let ' + 'c'.repeat(100) + ' in data) { target[key] = value; }' ]; problematicPatterns.forEach((pattern, index) => { const duration = measureTime(() => { APIDetector.detectAPIUsages(pattern, `backtracking-test-${index}.js`); }); // Should complete quickly even with complex patterns expect(duration).toBeLessThan(10); }); }); }); describe('Scalability Tests', () => { test('should scale linearly with input size', () => { const basePattern = 'Object.prototype.test = true;\n'; const sizes = [100, 500, 1000]; const durations: number[] = []; sizes.forEach(size => { const code = generateTestCode(size, basePattern); const duration = measureTime(() => { APIDetector.detectAPIUsages(code, `scale-test-${size}.js`); }); durations.push(duration); }); // Performance should scale reasonably (not exponentially) // Larger input should not be dramatically slower per pattern const perPatternTimes = durations.map((duration, i) => Math.max(duration, 1) / sizes[i]); const maxTime = Math.max(...perPatternTimes); const minTime = Math.min(...perPatternTimes); const ratio = minTime > 0 ? maxTime / minTime : 1; expect(ratio).toBeLessThan(10); // Performance shouldn't degrade more than 10x }); test('should handle diverse file types and structures', () => { const testFiles = [ // Minified code 'fetch("/api");eval("x");Object.prototype.a=1;for(let k in u)t[k]=u[k];', // Well-formatted code ` class SecurityTest { constructor() { this.api = fetch('/test'); } dangerousMethod() { Object.prototype.polluted = true; eval('malicious code'); } } `, // Mixed patterns with comments ` /* This file contains various security patterns */ // Network request const response = fetch('/api/data'); // Prototype pollution Object.prototype.isAdmin = true; // Dynamic execution setTimeout('alert("xss")', 1000); ` ]; testFiles.forEach((code, index) => { const duration = measureTime(() => { const results = APIDetector.detectAPIUsages(code, `diverse-test-${index}.js`); expect(results.length).toBeGreaterThan(0); }); expect(duration).toBeLessThan(10); }); }); }); describe('Throughput Benchmarks', () => { test('should achieve high detection throughput', () => { const testCode = ` Object.prototype.admin = true; fetch('/api/user'); eval('dangerous'); obj.__proto__ = malicious; for (let k in user) { obj[k] = user[k]; } `; const iterations = 1000; const startTime = Date.now(); let totalDetections = 0; for (let i = 0; i < iterations; i++) { const results = APIDetector.detectAPIUsages(testCode, `throughput-test-${i}.js`); totalDetections += results.length; } const duration = Date.now() - startTime; const detectionsPerSecond = (totalDetections / duration) * 1000; // Should achieve high throughput (>10,000 detections per second) expect(detectionsPerSecond).toBeGreaterThan(10000); console.log(`Throughput: ${Math.round(detectionsPerSecond).toLocaleString()} detections/second`); console.log(`Total detections: ${totalDetections.toLocaleString()}`); console.log(`Duration: ${duration}ms`); }); test('should maintain consistent performance under load', () => { const testCode = 'Object.prototype.test = true; fetch("/api"); eval("code");'; const batchSize = 100; const batches = 10; const batchTimes: number[] = []; for (let batch = 0; batch < batches; batch++) { const batchStart = Date.now(); for (let i = 0; i < batchSize; i++) { APIDetector.detectAPIUsages(testCode, `load-test-${batch}-${i}.js`); } const batchTime = Date.now() - batchStart; batchTimes.push(Math.max(batchTime, 1)); // Ensure minimum 1ms to avoid division by zero } const avgBatchTime = batchTimes.reduce((a, b) => a + b, 0) / batchTimes.length; const maxBatchTime = Math.max(...batchTimes); const minBatchTime = Math.min(...batchTimes); // Performance should be consistent across batches const variabilityRatio = maxBatchTime / minBatchTime; expect(variabilityRatio).toBeLessThan(10); // More lenient threshold console.log(`Average batch time: ${avgBatchTime}ms`); console.log(`Min/Max batch time: ${minBatchTime}ms / ${maxBatchTime}ms`); console.log(`Variability ratio: ${variabilityRatio.toFixed(2)}x`); }); }); });