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
JavaScript
/**
* @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 = { '<': '<', '>': '>', '&': '&', '"': '"', "'": ''' };
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 };