UNPKG

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
/** * @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 };