"""Security scanning for dependencies and secrets.""" from __future__ import annotations import re from pathlib import Path from typing import Any, Dict, List from codebase_analyzer import CodebaseAnalyzer class SecurityScanner: """Scans codebase for security issues.""" def __init__(self): self.analyzer = CodebaseAnalyzer() def scan_dependencies(self, folder_path: str) -> Dict[str, Any]: """Scan dependencies for known vulnerabilities.""" analysis = self.analyzer.analyze_folder(folder_path) dependencies = analysis.get("dependencies", []) # Known vulnerable packages (simplified - would use real vulnerability DB) vulnerable_packages = { "lodash": "< 4.17.21", "axios": "< 0.21.1", "express": "< 4.17.1", } issues = [] for dep in dependencies[:20]: # Check first 20 dep_name = dep.lower() if isinstance(dep, str) else dep.get("name", "").lower() if dep_name in vulnerable_packages: issues.append({ "package": dep_name, "severity": "high", "issue": f"Known vulnerability, update to {vulnerable_packages[dep_name]}", "type": "dependency_vulnerability" }) return { "total_dependencies": len(dependencies), "scanned": min(20, len(dependencies)), "vulnerabilities_found": len(issues), "issues": issues, "status": "safe" if not issues else "vulnerabilities_detected" } def scan_secrets(self, folder_path: str) -> Dict[str, Any]: """Scan for exposed secrets and API keys.""" path = Path(folder_path) secrets_found = [] # Patterns for common secrets secret_patterns = { "api_key": r'(?i)(api[_-]?key|apikey)\s*[:=]\s*["\']?([a-zA-Z0-9_\-]{20,})["\']?', "secret": r'(?i)(secret|password|pwd)\s*[:=]\s*["\']?([a-zA-Z0-9_\-]{10,})["\']?', "token": r'(?i)(token|bearer)\s*[:=]\s*["\']?([a-zA-Z0-9_\-]{20,})["\']?', "aws_key": r'AKIA[0-9A-Z]{16}', "private_key": r'-----BEGIN\s+(RSA\s+)?PRIVATE\s+KEY-----', } # Scan common config files config_files = ["*.env", "*.env.*", "*.config.js", "*.config.ts", "*.json"] for pattern in config_files: for file_path in path.rglob(pattern): if file_path.is_file() and ".git" not in str(file_path): try: content = file_path.read_text() for secret_type, regex in secret_patterns.items(): matches = re.findall(regex, content) if matches: secrets_found.append({ "file": str(file_path.relative_to(path)), "type": secret_type, "severity": "critical", "issue": f"Potential {secret_type} exposed in {file_path.name}" }) except Exception: pass return { "secrets_found": len(secrets_found), "issues": secrets_found[:10], # Limit to 10 "status": "safe" if not secrets_found else "secrets_detected", "recommendation": "Use environment variables and secrets management" } def scan_codebase(self, folder_path: str) -> Dict[str, Any]: """Complete security scan.""" deps_scan = self.scan_dependencies(folder_path) secrets_scan = self.scan_secrets(folder_path) return { "dependencies": deps_scan, "secrets": secrets_scan, "overall_status": "safe" if deps_scan["status"] == "safe" and secrets_scan["status"] == "safe" else "issues_found", "total_issues": deps_scan["vulnerabilities_found"] + secrets_scan["secrets_found"] }