UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

459 lines (376 loc) 15.4 kB
/** * @file Unit tests for security vulnerability analysis core functionality * @description Tests vulnerability detection, risk assessment, and security recommendations */ const { analyzeFileSecurityVulns, analyzeProjectSecurityVulns, calculateSecurityScore, generateSecurityRecommendations } = require('./analyzeSecurityVulns'); const qtests = require('qtests'); const fs = require('fs'); const path = require('path'); /** * Test runner for security vulnerability analysis */ async function runTests() { console.log('=== Testing Security Vulnerability Analysis ==='); const results = { total: 0, passed: 0 }; // Test XSS vulnerability detection results.total++; try { const tempFile = path.join(__dirname, 'temp-xss-test.js'); const xssCode = ` function renderUserContent(userInput) { // XSS vulnerability - direct innerHTML assignment document.getElementById('content').innerHTML = userInput; // Another XSS pattern const div = document.createElement('div'); div.innerHTML = '<span>' + userInput + '</span>'; // Dangerous template literal const template = \`<div class="user-content">\${userInput}</div>\`; document.body.innerHTML += template; } function updateTitle(title) { // Document write XSS document.write('<title>' + title + '</title>'); } `; fs.writeFileSync(tempFile, xssCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(typeof analysis === 'object', 'analyzeFileSecurityVulns should return object'); qtests.assert(Array.isArray(analysis.vulnerabilities), 'Analysis should include vulnerabilities array'); qtests.assert(analysis.vulnerabilities.length > 0, 'Should detect XSS vulnerabilities'); qtests.assert(analysis.vulnerabilities.some(vuln => vuln.type.includes('xss') || vuln.category === 'XSS' ), 'Should identify XSS vulnerabilities'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly detects XSS vulnerabilities'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns XSS test failed: ${error.message}`); } // Test SQL injection vulnerability detection results.total++; try { const tempFile = path.join(__dirname, 'temp-sqli-test.js'); const sqliCode = ` const mysql = require('mysql'); function getUserById(userId) { // SQL injection vulnerability - string concatenation const query = "SELECT * FROM users WHERE id = " + userId; return db.query(query); } function searchUsers(searchTerm) { // Template literal SQL injection const query = \`SELECT * FROM users WHERE name LIKE '%\${searchTerm}%'\`; return db.query(query); } function updateUserEmail(userId, email) { // Another SQL injection pattern const updateQuery = "UPDATE users SET email = '" + email + "' WHERE id = " + userId; return db.query(updateQuery); } // Safe parameterized query (should not trigger) function getUserSafe(userId) { const query = "SELECT * FROM users WHERE id = ?"; return db.query(query, [userId]); } `; fs.writeFileSync(tempFile, sqliCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(analysis.vulnerabilities.some(vuln => vuln.type.includes('sql_injection') || vuln.category === 'SQL Injection' ), 'Should identify SQL injection vulnerabilities'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly detects SQL injection vulnerabilities'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns SQL injection test failed: ${error.message}`); } // Test eval() and code injection detection results.total++; try { const tempFile = path.join(__dirname, 'temp-eval-test.js'); const evalCode = ` function executeUserCode(userCode) { // Code injection vulnerability - eval return eval(userCode); } function parseUserData(data) { // JSON.parse with eval-like patterns return eval('(' + data + ')'); } function dynamicFunction(funcBody) { // Function constructor injection return new Function('return ' + funcBody)(); } function setTimeout_injection(userInput) { // setTimeout with string (acts like eval) setTimeout(userInput, 1000); } function setInterval_injection(code) { // setInterval with string setInterval(code, 5000); } `; fs.writeFileSync(tempFile, evalCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(analysis.vulnerabilities.some(vuln => vuln.type.includes('code_injection') || vuln.type.includes('eval') || vuln.category === 'Code Injection' ), 'Should identify code injection vulnerabilities'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly detects code injection vulnerabilities'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns code injection test failed: ${error.message}`); } // Test path traversal vulnerability detection results.total++; try { const tempFile = path.join(__dirname, 'temp-path-traversal-test.js'); const pathTraversalCode = ` const fs = require('fs'); const path = require('path'); function readUserFile(filename) { // Path traversal vulnerability - direct concatenation const filePath = './uploads/' + filename; return fs.readFileSync(filePath); } function serveStaticFile(req, res) { // Express path traversal const filePath = path.join(__dirname, 'public', req.params.filename); // Missing path validation res.sendFile(filePath); } function downloadFile(filename) { // Template literal path traversal const filePath = \`./files/\${filename}\`; return fs.readFileSync(filePath); } // Safe implementation (should not trigger) function readFileSafe(filename) { const sanitized = path.basename(filename); const filePath = path.join('./uploads/', sanitized); return fs.readFileSync(filePath); } `; fs.writeFileSync(tempFile, pathTraversalCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(analysis.vulnerabilities.some(vuln => vuln.type.includes('path_traversal') || vuln.category === 'Path Traversal' ), 'Should identify path traversal vulnerabilities'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly detects path traversal vulnerabilities'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns path traversal test failed: ${error.message}`); } // Test insecure randomness detection results.total++; try { const tempFile = path.join(__dirname, 'temp-random-test.js'); const randomCode = ` function generateToken() { // Insecure randomness - Math.random for security return Math.random().toString(36).substr(2, 9); } function generateSessionId() { // Weak random ID generation return Date.now() + Math.random(); } function createPassword() { // Predictable password generation const chars = 'abcdefghijklmnopqrstuvwxyz'; let password = ''; for (let i = 0; i < 8; i++) { password += chars[Math.floor(Math.random() * chars.length)]; } return password; } // Secure implementation (should not trigger) function generateSecureToken() { const crypto = require('crypto'); return crypto.randomBytes(32).toString('hex'); } `; fs.writeFileSync(tempFile, randomCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(analysis.vulnerabilities.some(vuln => vuln.type.includes('insecure_random') || vuln.category === 'Cryptography' ), 'Should identify insecure randomness vulnerabilities'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly detects insecure randomness'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns insecure randomness test failed: ${error.message}`); } // Test hardcoded credentials detection results.total++; try { const tempFile = path.join(__dirname, 'temp-credentials-test.js'); const credentialsCode = ` // Hardcoded credentials - security vulnerability const API_KEY = 'sk-1234567890abcdef'; const DATABASE_PASSWORD = 'admin123'; const SECRET_KEY = 'my-secret-key-12345'; function connectToDatabase() { return mysql.createConnection({ host: 'localhost', user: 'admin', password: 'hardcoded-password', // Vulnerability database: 'myapp' }); } function makeApiCall() { return fetch('https://api.example.com/data', { headers: { 'Authorization': 'Bearer abc123xyz789' // Hardcoded token } }); } // Configuration with secrets const config = { aws: { accessKeyId: 'AKIAIOSFODNN7EXAMPLE', secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' } }; `; fs.writeFileSync(tempFile, credentialsCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(analysis.vulnerabilities.some(vuln => vuln.type.includes('hardcoded_secret') || vuln.category === 'Secrets' ), 'Should identify hardcoded credentials'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly detects hardcoded credentials'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns credentials test failed: ${error.message}`); } // Test security score calculation results.total++; try { const testVulnerabilities = [ { severity: 'CRITICAL', confidence: 'HIGH', cwe: 'CWE-79' }, { severity: 'HIGH', confidence: 'MEDIUM', cwe: 'CWE-89' }, { severity: 'MEDIUM', confidence: 'HIGH', cwe: 'CWE-22' }, { severity: 'LOW', confidence: 'LOW', cwe: 'CWE-330' } ]; const score = calculateSecurityScore(testVulnerabilities, 200); // 200 lines of code qtests.assert(typeof score === 'number', 'calculateSecurityScore should return number'); qtests.assert(score >= 0 && score <= 100, 'Security score should be between 0 and 100'); qtests.assert(score < 80, 'Score should be reduced for critical vulnerabilities'); // Test with no vulnerabilities const perfectScore = calculateSecurityScore([], 200); qtests.assert(perfectScore === 100, 'Perfect score should be 100 with no vulnerabilities'); console.log('✓ calculateSecurityScore correctly calculates security scores'); results.passed++; } catch (error) { console.log(`✗ calculateSecurityScore test failed: ${error.message}`); } // Test security recommendations generation results.total++; try { const testVulnerabilities = [ { type: 'xss', severity: 'HIGH', confidence: 'HIGH', cwe: 'CWE-79', line: 15, pattern: 'innerHTML assignment' }, { type: 'sql_injection', severity: 'CRITICAL', confidence: 'HIGH', cwe: 'CWE-89', line: 25, pattern: 'string concatenation in query' }, { type: 'hardcoded_secret', severity: 'MEDIUM', confidence: 'MEDIUM', cwe: 'CWE-798', line: 5, pattern: 'API key in source code' } ]; const recommendations = generateSecurityRecommendations(testVulnerabilities); qtests.assert(Array.isArray(recommendations), 'generateSecurityRecommendations should return array'); qtests.assert(recommendations.length > 0, 'Should generate recommendations for vulnerabilities'); qtests.assert(recommendations.every(rec => typeof rec.priority === 'string'), 'Each recommendation should have priority'); qtests.assert(recommendations.every(rec => typeof rec.remediation === 'string'), 'Each recommendation should have remediation'); qtests.assert(recommendations.every(rec => typeof rec.cweReference === 'string'), 'Each recommendation should have CWE reference'); console.log('✓ generateSecurityRecommendations correctly generates actionable recommendations'); results.passed++; } catch (error) { console.log(`✗ generateSecurityRecommendations test failed: ${error.message}`); } // Test clean, secure code results.total++; try { const tempFile = path.join(__dirname, 'temp-secure-test.js'); const secureCode = ` const crypto = require('crypto'); const mysql = require('mysql'); // Secure user input handling function renderUserContent(userInput) { const sanitized = userInput.replace(/[<>&"']/g, (char) => { const entities = { '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;', "'": '&#x27;' }; return entities[char]; }); document.getElementById('content').textContent = sanitized; } // Parameterized queries function getUserById(userId) { const query = "SELECT * FROM users WHERE id = ?"; return db.query(query, [userId]); } // Secure token generation function generateSecureToken() { return crypto.randomBytes(32).toString('hex'); } // Environment variables for secrets const config = { apiKey: process.env.API_KEY, dbPassword: process.env.DB_PASSWORD }; // Safe file handling function readUserFile(filename) { const safeName = path.basename(filename); const filePath = path.join('./uploads/', safeName); if (!filePath.startsWith('./uploads/')) { throw new Error('Invalid file path'); } return fs.readFileSync(filePath); } `; fs.writeFileSync(tempFile, secureCode); const analysis = await analyzeFileSecurityVulns(tempFile); qtests.assert(analysis.vulnerabilities.length === 0 || analysis.vulnerabilities.length < 2, 'Secure code should have minimal vulnerabilities'); fs.unlinkSync(tempFile); console.log('✓ analyzeFileSecurityVulns correctly identifies secure code'); results.passed++; } catch (error) { console.log(`✗ analyzeFileSecurityVulns secure code test failed: ${error.message}`); } console.log(`=== Security Vulnerability Analysis Test Results ===`); console.log(`Tests passed: ${results.passed}/${results.total}`); console.log(`Success rate: ${((results.passed / results.total) * 100).toFixed(1)}%`); return results; } module.exports = { runTests };