agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
201 lines (181 loc) • 8.7 kB
JavaScript
/**
* @file Framework detection for security analysis
* @description Identifies web frameworks and libraries to provide context-aware security recommendations
* This module enables targeted security analysis by detecting the underlying technology stack,
* allowing the security scanner to provide framework-specific vulnerability patterns and
* remediation strategies. Different frameworks have different attack surfaces and built-in
* security features that affect both vulnerability detection and fix recommendations.
*/
const fs = require('fs');
const path = require('path');
// Framework detection patterns and their associated security characteristics
// Rationale: Each framework has unique security features and common vulnerability patterns
// This mapping enables context-aware security analysis and targeted recommendations
const FRAMEWORK_PATTERNS = {
react: {
files: ['package.json'], // Primary detection through package dependencies
patterns: ['"react":', 'import React', 'from "react"'], // Multiple detection strategies for reliability
securityFeatures: ['JSX auto-escaping', 'Component isolation'] // Built-in XSS protection features
},
express: {
files: ['package.json', '*.js'], // Check both dependencies and actual usage patterns
patterns: ['"express":', 'require("express")', 'app.use('], // Detect both installation and usage
securityFeatures: ['Helmet.js integration', 'CORS middleware'] // Common security extensions
},
django: {
files: ['requirements.txt', 'settings.py'], // Python-specific dependency and config files
patterns: ['Django==', 'from django', 'INSTALLED_APPS'], // Version pinning and import patterns
securityFeatures: ['CSRF protection', 'XSS protection', 'SQL injection protection'] // Comprehensive built-in security
}
};
/**
* Detect all frameworks present in the project for security context
* @param {string} projectPath - Root directory of the project to analyze
* @returns {Array<Object>} Array of detected frameworks with security feature information
*
* Rationale: Multiple frameworks can coexist in modern applications (e.g., React frontend
* with Express backend), so we detect all present frameworks rather than stopping at the
* first match. This enables comprehensive security analysis across the entire stack.
*/
async function detectFrameworks(projectPath) {
const detectedFrameworks = [];
// Check each known framework pattern independently
for (const [framework, config] of Object.entries(FRAMEWORK_PATTERNS)) {
if (await isFrameworkPresent(projectPath, config)) {
detectedFrameworks.push({
name: framework,
securityFeatures: config.securityFeatures, // Built-in protections to consider during analysis
confidence: 'high' // High confidence when file patterns and content patterns both match
});
}
}
return detectedFrameworks;
}
/**
* Determine if a specific framework is present using multiple detection strategies
* @param {string} projectPath - Project root directory
* @param {Object} config - Framework detection configuration
* @param {Array<string>} config.files - File patterns to check for framework presence
* @param {Array<string>} config.patterns - Content patterns that indicate framework usage
* @returns {boolean} True if framework is detected with high confidence
*
* Rationale: Uses both file presence and content analysis for robust detection.
* File-only detection can miss frameworks added via CDN or alternative installation methods.
* Content-only detection can miss frameworks that are installed but not yet used.
*/
async function isFrameworkPresent(projectPath, config) {
for (const filePattern of config.files) {
const filePath = path.join(projectPath, filePattern);
try {
await fs.promises.access(filePath); // Check if file exists before reading
const content = await fs.promises.readFile(filePath, 'utf8');
// Check if any of the framework-specific patterns are present in the file
for (const pattern of config.patterns) {
if (content.includes(pattern)) {
return true; // Early return on first pattern match for efficiency
}
}
} catch (error) {
continue; // Skip files that don't exist or can't be read, continue checking other patterns
}
}
return false; // No patterns matched in any of the expected files
}
/**
* Generate framework-specific security guidance based on detected frameworks
* @param {Array<Object>} frameworks - Array of detected framework objects
* @returns {Array<Object>} Security guidance tailored to each detected framework
*
* Rationale: Different frameworks require different security configurations and have
* different vulnerability patterns. Providing framework-specific guidance significantly
* improves the actionability of security recommendations by focusing on the tools
* and patterns developers are actually using.
*/
function getFrameworkSecurityGuidance(frameworks) {
const guidance = [];
frameworks.forEach(framework => {
guidance.push({
framework: framework.name,
recommendations: getFrameworkSpecificRecommendations(framework.name), // Actionable security steps
builtInSecurity: framework.securityFeatures // Existing protections to leverage
});
});
return guidance;
}
/**
* Get specific security recommendations for each framework
* @param {string} frameworkName - Name of the framework to get recommendations for
* @returns {Array<string>} Array of actionable security recommendations
*
* Rationale: Framework-specific recommendations are more actionable than generic security
* advice because they reference specific APIs, configurations, and patterns that developers
* can immediately implement. These recommendations focus on the most common security
* misconfigurations and missing protections for each framework.
*/
function getFrameworkSpecificRecommendations(frameworkName) {
const recommendations = {
react: [
'Use dangerouslySetInnerHTML sparingly and sanitize content', // Prevents XSS through unsafe HTML injection
'Implement proper prop validation with PropTypes', // Catches type-related vulnerabilities early
'Use React.StrictMode for development warnings' // Identifies deprecated patterns that may have security implications
],
express: [
'Install and configure Helmet.js for security headers', // Adds multiple security headers automatically
'Use express-rate-limit for DoS protection', // Prevents abuse through request limiting
'Implement proper session management with express-session' // Secure session handling with proper configuration
],
django: [
'Enable CSRF protection in settings', // Leverages Django's built-in CSRF middleware
'Use Django forms for input validation', // Framework-level input sanitization and validation
'Configure secure session and cookie settings' // Proper session security configuration
]
};
return recommendations[frameworkName] || []; // Return empty array for unknown frameworks
}
/**
* Detect framework-specific security vulnerabilities and configurations
* @param {string} content - File content to analyze
* @param {string} filePath - Path to the file being analyzed
* @returns {Array<Object>} Array of framework-specific security issues
*/
function detectFrameworkSecurity(content, filePath) {
const issues = [];
// React-specific security checks
if (content.includes('dangerouslySetInnerHTML') && !content.includes('DOMPurify')) {
issues.push({
type: 'react_unsafe_html',
severity: 'HIGH',
message: 'Use of dangerouslySetInnerHTML without sanitization',
line: getLineNumber(content, 'dangerouslySetInnerHTML'),
file: filePath
});
}
// Express-specific security checks
if (content.includes('app.use') && !content.includes('helmet')) {
issues.push({
type: 'express_missing_helmet',
severity: 'MEDIUM',
message: 'Express app missing security headers (consider helmet.js)',
line: getLineNumber(content, 'app.use'),
file: filePath
});
}
return issues;
}
/**
* Get line number where a pattern occurs in content
* @param {string} content - File content
* @param {string} pattern - Pattern to find
* @returns {number} Line number (1-based)
*/
function getLineNumber(content, pattern) {
const index = content.indexOf(pattern);
if (index === -1) return 1;
return content.substring(0, index).split('\n').length;
}
module.exports = {
detectFrameworks,
getFrameworkSecurityGuidance,
isFrameworkPresent,
detectFrameworkSecurity
};