| | """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", []) |
| | |
| | |
| | vulnerable_packages = { |
| | "lodash": "< 4.17.21", |
| | "axios": "< 0.21.1", |
| | "express": "< 4.17.1", |
| | } |
| | |
| | issues = [] |
| | for dep in dependencies[: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 = [] |
| | |
| | |
| | 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-----', |
| | } |
| | |
| | |
| | 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], |
| | "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"] |
| | } |
| |
|
| |
|