package internal import ( "context" "crypto/sha256" "encoding/json" "fmt" "io/fs" "math" "os" "path/filepath" "regexp" "sort" "strings" "time" ) type SecurityAuditConfig struct { ScanType string OutputDir string Verbose bool DeepScan bool IncludeTests bool RiskThreshold string ReportFormat string Timeout time.Duration Baseline string RemediationMode bool ComplianceCheck bool } type SecurityAuditor struct { config *SecurityAuditConfig results *SecurityResults rootDir string } type SecurityResults struct { ScanType string `json:"scan_type"` RiskThreshold string `json:"risk_threshold"` OverallRiskScore float64 `json:"overall_risk_score"` OverallRiskLevel string `json:"overall_risk_level"` CodeScanResults *CodeScanResults `json:"code_scan_results,omitempty"` DependencyResults *DependencyResults `json:"dependency_results,omitempty"` SecretScanResults *SecretScanResults `json:"secret_scan_results,omitempty"` PermissionResults *PermissionResults `json:"permission_results,omitempty"` NetworkResults *NetworkResults `json:"network_results,omitempty"` ComplianceResults *ComplianceResults `json:"compliance_results,omitempty"` SecurityFindings []SecurityFinding `json:"security_findings"` RiskAssessment RiskAssessment `json:"risk_assessment"` RemediationPlan []RemediationAction `json:"remediation_plan,omitempty"` Metrics SecurityMetrics `json:"metrics"` Timestamp time.Time `json:"timestamp"` DurationMs int64 `json:"duration_ms"` Environment EnvironmentInfo `json:"environment"` } type CodeScanResults struct { FilesScanned int `json:"files_scanned"` VulnerabilitiesFound int `json:"vulnerabilities_found"` CodeIssues []CodeIssue `json:"code_issues"` HotspotAnalysis []SecurityHotspot `json:"hotspot_analysis"` QualityMetrics CodeQualityMetrics `json:"quality_metrics"` } type CodeIssue struct { ID string `json:"id"` Type string `json:"type"` Severity string `json:"severity"` File string `json:"file"` Line int `json:"line"` Column int `json:"column"` Description string `json:"description"` Evidence string `json:"evidence"` CWE string `json:"cwe,omitempty"` OWASP string `json:"owasp,omitempty"` Confidence string `json:"confidence"` Timestamp time.Time `json:"timestamp"` } type SecurityHotspot struct { File string `json:"file"` Function string `json:"function"` RiskScore float64 `json:"risk_score"` Issues int `json:"issues"` CriticalIssues int `json:"critical_issues"` Complexity string `json:"complexity"` Recommendation string `json:"recommendation"` } type CodeQualityMetrics struct { LinesOfCode int `json:"lines_of_code"` CyclomaticComplexity float64 `json:"cyclomatic_complexity"` TechnicalDebt float64 `json:"technical_debt_hours"` TestCoverage float64 `json:"test_coverage_percent"` DuplicationRatio float64 `json:"duplication_ratio_percent"` } type DependencyResults struct { TotalDependencies int `json:"total_dependencies"` VulnerableDeps int `json:"vulnerable_dependencies"` OutdatedDeps int `json:"outdated_dependencies"` DependencyIssues []DependencyIssue `json:"dependency_issues"` LicenseAnalysis LicenseAnalysis `json:"license_analysis"` SupplyChainRisk SupplyChainRisk `json:"supply_chain_risk"` } type DependencyIssue struct { Package string `json:"package"` Version string `json:"version"` LatestVersion string `json:"latest_version"` Severity string `json:"severity"` CVE string `json:"cve,omitempty"` Description string `json:"description"` PatchAvailable bool `json:"patch_available"` CVSS float64 `json:"cvss_score"` Timestamp time.Time `json:"timestamp"` } type LicenseAnalysis struct { LicenseTypes map[string]int `json:"license_types"` ProblematicLicenses []string `json:"problematic_licenses"` UnknownLicenses []string `json:"unknown_licenses"` ComplianceIssues []string `json:"compliance_issues"` } type SupplyChainRisk struct { HighRiskPackages []string `json:"high_risk_packages"` UnmaintainedPackages []string `json:"unmaintained_packages"` TyposquattingRisk float64 `json:"typosquatting_risk"` OverallRiskScore float64 `json:"overall_risk_score"` } type SecretScanResults struct { FilesScanned int `json:"files_scanned"` SecretsFound int `json:"secrets_found"` SecretFindings []SecretFinding `json:"secret_findings"` SecretPatterns map[string]int `json:"secret_patterns"` } type SecretFinding struct { Type string `json:"type"` File string `json:"file"` Line int `json:"line"` Pattern string `json:"pattern"` Confidence float64 `json:"confidence"` Entropy float64 `json:"entropy"` Context string `json:"context"` Severity string `json:"severity"` Timestamp time.Time `json:"timestamp"` } type PermissionResults struct { FilePermissions []FilePermission `json:"file_permissions"` ConfigPermissions []ConfigPermission `json:"config_permissions"` PermissionIssues []PermissionIssue `json:"permission_issues"` OverallSecurity string `json:"overall_security"` } type FilePermission struct { Path string `json:"path"` Permissions string `json:"permissions"` Owner string `json:"owner"` Group string `json:"group"` Risky bool `json:"risky"` Reason string `json:"reason,omitempty"` } type ConfigPermission struct { File string `json:"file"` Setting string `json:"setting"` Value string `json:"value"` Risk string `json:"risk"` Description string `json:"description"` } type PermissionIssue struct { Type string `json:"type"` Path string `json:"path"` Issue string `json:"issue"` Severity string `json:"severity"` Remediation string `json:"remediation"` Timestamp time.Time `json:"timestamp"` } type NetworkResults struct { OpenPorts []PortInfo `json:"open_ports"` NetworkConnections []NetworkConnection `json:"network_connections"` TLSAnalysis TLSAnalysis `json:"tls_analysis"` FirewallStatus FirewallStatus `json:"firewall_status"` NetworkIssues []NetworkIssue `json:"network_issues"` } type PortInfo struct { Port int `json:"port"` Protocol string `json:"protocol"` Service string `json:"service"` State string `json:"state"` Risk string `json:"risk"` Description string `json:"description"` } type NetworkConnection struct { LocalAddress string `json:"local_address"` RemoteAddress string `json:"remote_address"` State string `json:"state"` Protocol string `json:"protocol"` Risk string `json:"risk"` } type TLSAnalysis struct { CertificateValidation bool `json:"certificate_validation"` TLSVersions []string `json:"tls_versions"` CipherSuites []string `json:"cipher_suites"` Vulnerabilities []string `json:"vulnerabilities"` Recommendations []string `json:"recommendations"` } type FirewallStatus struct { Enabled bool `json:"enabled"` Rules []string `json:"rules"` Blocked []string `json:"blocked"` Allowed []string `json:"allowed"` Recommendations []string `json:"recommendations"` } type NetworkIssue struct { Type string `json:"type"` Description string `json:"description"` Severity string `json:"severity"` Impact string `json:"impact"` Timestamp time.Time `json:"timestamp"` } type ComplianceResults struct { FrameworksChecked []string `json:"frameworks_checked"` ComplianceScore float64 `json:"compliance_score"` ComplianceFindings []ComplianceFinding `json:"compliance_findings"` RequirementStatus map[string]string `json:"requirement_status"` } type ComplianceFinding struct { Framework string `json:"framework"` Requirement string `json:"requirement"` Status string `json:"status"` Description string `json:"description"` Evidence string `json:"evidence"` Priority string `json:"priority"` Timestamp time.Time `json:"timestamp"` } type SecurityFinding struct { ID string `json:"id"` Title string `json:"title"` Category string `json:"category"` Severity string `json:"severity"` Description string `json:"description"` Impact string `json:"impact"` Location string `json:"location"` Evidence string `json:"evidence"` CWE string `json:"cwe,omitempty"` CVSS float64 `json:"cvss,omitempty"` Confidence float64 `json:"confidence"` Timestamp time.Time `json:"timestamp"` } type RiskAssessment struct { OverallRisk string `json:"overall_risk"` RiskFactors []RiskFactor `json:"risk_factors"` ThreatModel ThreatModel `json:"threat_model"` AttackSurface AttackSurface `json:"attack_surface"` BusinessImpact BusinessImpact `json:"business_impact"` RecommendedActions []string `json:"recommended_actions"` } type RiskFactor struct { Factor string `json:"factor"` Likelihood float64 `json:"likelihood"` Impact float64 `json:"impact"` RiskScore float64 `json:"risk_score"` Description string `json:"description"` } type ThreatModel struct { ThreatsIdentified []Threat `json:"threats_identified"` AttackVectors []AttackVector `json:"attack_vectors"` AssetValuation map[string]float64 `json:"asset_valuation"` ThreatActors []ThreatActor `json:"threat_actors"` } type Threat struct { Name string `json:"name"` Description string `json:"description"` Likelihood float64 `json:"likelihood"` Impact float64 `json:"impact"` Risk float64 `json:"risk"` Mitigations []string `json:"mitigations"` } type AttackVector struct { Vector string `json:"vector"` Complexity string `json:"complexity"` Privileges string `json:"privileges"` UserAction string `json:"user_action"` Scope string `json:"scope"` Mitigations []string `json:"mitigations"` } type ThreatActor struct { Type string `json:"type"` Motivation string `json:"motivation"` Capability string `json:"capability"` Opportunity string `json:"opportunity"` Likelihood float64 `json:"likelihood"` Techniques []string `json:"techniques"` } type AttackSurface struct { NetworkExposure float64 `json:"network_exposure"` ApplicationEndpoints int `json:"application_endpoints"` DataExposure float64 `json:"data_exposure"` TrustedInterfaces int `json:"trusted_interfaces"` ExternalDependencies int `json:"external_dependencies"` ReductionOpportunities []string `json:"reduction_opportunities"` } type BusinessImpact struct { FinancialImpact float64 `json:"financial_impact"` ReputationalImpact float64 `json:"reputational_impact"` OperationalImpact float64 `json:"operational_impact"` ComplianceImpact float64 `json:"compliance_impact"` OverallImpact float64 `json:"overall_impact"` } type RemediationAction struct { ID string `json:"id"` Title string `json:"title"` Priority string `json:"priority"` Effort string `json:"effort"` Description string `json:"description"` Steps []string `json:"steps"` Timeline string `json:"timeline"` Owner string `json:"owner"` Dependencies []string `json:"dependencies"` Timestamp time.Time `json:"timestamp"` } type SecurityMetrics struct { VulnerabilityDensity float64 `json:"vulnerability_density"` SecurityScore float64 `json:"security_score"` ComplianceRate float64 `json:"compliance_rate"` RiskReduction float64 `json:"risk_reduction"` MeanTimeToDetection float64 `json:"mean_time_to_detection"` MeanTimeToResolution float64 `json:"mean_time_to_resolution"` } type EnvironmentInfo struct { WorkingDirectory string `json:"working_directory"` GitRepository bool `json:"git_repository"` ProjectType string `json:"project_type"` Languages []string `json:"languages"` BuildSystem string `json:"build_system"` TotalFiles int `json:"total_files"` TotalLOC int `json:"total_loc"` } func NewSecurityAuditor(config *SecurityAuditConfig) (*SecurityAuditor, error) { rootDir, err := os.Getwd() if err != nil { return nil, fmt.Errorf("failed to get working directory: %w", err) } return &SecurityAuditor{ config: config, rootDir: rootDir, results: &SecurityResults{ ScanType: config.ScanType, RiskThreshold: config.RiskThreshold, SecurityFindings: make([]SecurityFinding, 0), Timestamp: time.Now(), }, }, nil } func (sa *SecurityAuditor) RunSecurityAudit(ctx context.Context) error { startTime := time.Now() defer func() { sa.results.DurationMs = time.Since(startTime).Milliseconds() }() // Initialize environment info sa.initializeEnvironmentInfo() switch sa.config.ScanType { case "code": return sa.runCodeScan(ctx) case "dependencies": return sa.runDependencyScan(ctx) case "secrets": return sa.runSecretScan(ctx) case "permissions": return sa.runPermissionScan(ctx) case "network": return sa.runNetworkScan(ctx) case "all": return sa.runAllScans(ctx) default: return fmt.Errorf("unsupported scan type: %s", sa.config.ScanType) } } func (sa *SecurityAuditor) runAllScans(ctx context.Context) error { scans := []struct { name string fn func(context.Context) error }{ {"code", sa.runCodeScan}, {"dependencies", sa.runDependencyScan}, {"secrets", sa.runSecretScan}, {"permissions", sa.runPermissionScan}, {"network", sa.runNetworkScan}, } if sa.config.ComplianceCheck { scans = append(scans, struct { name string fn func(context.Context) error }{"compliance", sa.runComplianceScan}) } for _, scan := range scans { if sa.config.Verbose { fmt.Printf("Running %s scan...\n", scan.name) } if err := scan.fn(ctx); err != nil { return fmt.Errorf("failed %s scan: %w", scan.name, err) } } // Perform risk assessment sa.performRiskAssessment() // Generate remediation plan if requested if sa.config.RemediationMode { sa.generateRemediationPlan() } // Calculate overall metrics sa.calculateSecurityMetrics() return nil } func (sa *SecurityAuditor) initializeEnvironmentInfo() { wd, _ := os.Getwd() sa.results.Environment = EnvironmentInfo{ WorkingDirectory: wd, GitRepository: sa.isGitRepository(), ProjectType: sa.detectProjectType(), Languages: sa.detectLanguages(), BuildSystem: sa.detectBuildSystem(), } // Count files and lines of code sa.countProjectSize() } func (sa *SecurityAuditor) isGitRepository() bool { _, err := os.Stat(filepath.Join(sa.rootDir, ".git")) return err == nil } func (sa *SecurityAuditor) detectProjectType() string { if _, err := os.Stat(filepath.Join(sa.rootDir, "go.mod")); err == nil { return "Go" } if _, err := os.Stat(filepath.Join(sa.rootDir, "package.json")); err == nil { return "Node.js" } if _, err := os.Stat(filepath.Join(sa.rootDir, "requirements.txt")); err == nil { return "Python" } if _, err := os.Stat(filepath.Join(sa.rootDir, "Cargo.toml")); err == nil { return "Rust" } return "Unknown" } func (sa *SecurityAuditor) detectLanguages() []string { languages := make([]string, 0) extensions := make(map[string]string) err := filepath.WalkDir(sa.rootDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } if d.IsDir() { // Skip common directories if strings.Contains(path, ".git") || strings.Contains(path, "node_modules") || strings.Contains(path, "vendor") { return filepath.SkipDir } return nil } ext := filepath.Ext(path) switch ext { case ".go": extensions["Go"] = ".go" case ".js", ".ts": extensions["JavaScript/TypeScript"] = ext case ".py": extensions["Python"] = ".py" case ".rs": extensions["Rust"] = ".rs" case ".java": extensions["Java"] = ".java" case ".c", ".cpp", ".cc": extensions["C/C++"] = ext } return nil }) if err == nil { for lang := range extensions { languages = append(languages, lang) } } return languages } func (sa *SecurityAuditor) detectBuildSystem() string { if _, err := os.Stat(filepath.Join(sa.rootDir, "Makefile")); err == nil { return "Make" } if _, err := os.Stat(filepath.Join(sa.rootDir, "go.mod")); err == nil { return "Go Modules" } if _, err := os.Stat(filepath.Join(sa.rootDir, "package.json")); err == nil { return "NPM/Yarn" } return "Unknown" } func (sa *SecurityAuditor) countProjectSize() { totalFiles := 0 totalLOC := 0 err := filepath.WalkDir(sa.rootDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } if d.IsDir() { if strings.Contains(path, ".git") || strings.Contains(path, "node_modules") || strings.Contains(path, "vendor") { return filepath.SkipDir } return nil } // Count source files ext := filepath.Ext(path) if sa.isSourceFile(ext) { totalFiles++ if loc := sa.countLinesOfCode(path); loc > 0 { totalLOC += loc } } return nil }) if err == nil { sa.results.Environment.TotalFiles = totalFiles sa.results.Environment.TotalLOC = totalLOC } } func (sa *SecurityAuditor) isSourceFile(ext string) bool { sourceExts := []string{".go", ".js", ".ts", ".py", ".rs", ".java", ".c", ".cpp", ".cc", ".h"} for _, sourceExt := range sourceExts { if ext == sourceExt { return true } } return false } func (sa *SecurityAuditor) countLinesOfCode(filePath string) int { content, err := os.ReadFile(filePath) if err != nil { return 0 } lines := strings.Split(string(content), "\n") nonEmptyLines := 0 for _, line := range lines { if strings.TrimSpace(line) != "" && !strings.HasPrefix(strings.TrimSpace(line), "//") { nonEmptyLines++ } } return nonEmptyLines } func (sa *SecurityAuditor) runCodeScan(ctx context.Context) error { if sa.config.Verbose { fmt.Println("Starting code security scan...") } sa.results.CodeScanResults = &CodeScanResults{ CodeIssues: make([]CodeIssue, 0), HotspotAnalysis: make([]SecurityHotspot, 0), } err := filepath.WalkDir(sa.rootDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } if d.IsDir() { if strings.Contains(path, ".git") || strings.Contains(path, "vendor") { return filepath.SkipDir } if !sa.config.IncludeTests && strings.Contains(path, "test") { return filepath.SkipDir } return nil } // Scan source files if sa.isSourceFile(filepath.Ext(path)) { sa.results.CodeScanResults.FilesScanned++ sa.scanFileForVulnerabilities(path) } return nil }) if err != nil { return fmt.Errorf("failed to scan code: %w", err) } sa.results.CodeScanResults.VulnerabilitiesFound = len(sa.results.CodeScanResults.CodeIssues) sa.analyzeSecurityHotspots() sa.calculateCodeQualityMetrics() return nil } func (sa *SecurityAuditor) scanFileForVulnerabilities(filePath string) { content, err := os.ReadFile(filePath) if err != nil { return } fileContent := string(content) lines := strings.Split(fileContent, "\n") // Define vulnerability patterns vulnerabilityPatterns := []struct { pattern *regexp.Regexp issueType string severity string description string cwe string confidence string }{ { pattern: regexp.MustCompile(`(?i)(password|secret|api_key|private_key)\s*[:=]\s*["'][^"']+["']`), issueType: "hardcoded_secret", severity: "HIGH", description: "Hardcoded secret or password detected", cwe: "CWE-798", confidence: "HIGH", }, { pattern: regexp.MustCompile(`(?i)sql.*["']\s*\+\s*.*["']`), issueType: "sql_injection", severity: "HIGH", description: "Potential SQL injection vulnerability", cwe: "CWE-89", confidence: "MEDIUM", }, { pattern: regexp.MustCompile(`(?i)exec\s*\(\s*.*\$`), issueType: "command_injection", severity: "HIGH", description: "Potential command injection vulnerability", cwe: "CWE-78", confidence: "MEDIUM", }, { pattern: regexp.MustCompile(`(?i)unsafe\.Pointer`), issueType: "unsafe_operation", severity: "MEDIUM", description: "Use of unsafe pointer operations", cwe: "CWE-242", confidence: "HIGH", }, { pattern: regexp.MustCompile(`math\.rand\.Read|rand\.Seed\(time\.Now`), issueType: "weak_random", severity: "MEDIUM", description: "Use of weak random number generator", cwe: "CWE-338", confidence: "HIGH", }, { pattern: regexp.MustCompile(`http\.ListenAndServe.*:80[^0-9]`), issueType: "insecure_transport", severity: "MEDIUM", description: "HTTP server without TLS", cwe: "CWE-319", confidence: "HIGH", }, { pattern: regexp.MustCompile(`(?i)debug|trace|verbose.*true`), issueType: "debug_information", severity: "LOW", description: "Debug information disclosure", cwe: "CWE-209", confidence: "MEDIUM", }, } for lineNum, line := range lines { for _, pattern := range vulnerabilityPatterns { if matches := pattern.pattern.FindStringSubmatch(line); matches != nil { issue := CodeIssue{ ID: sa.generateIssueID(filePath, lineNum, pattern.issueType), Type: pattern.issueType, Severity: pattern.severity, File: filePath, Line: lineNum + 1, Column: strings.Index(line, matches[0]) + 1, Description: pattern.description, Evidence: strings.TrimSpace(line), CWE: pattern.cwe, Confidence: pattern.confidence, Timestamp: time.Now(), } sa.results.CodeScanResults.CodeIssues = append(sa.results.CodeScanResults.CodeIssues, issue) // Also add to general security findings sa.addSecurityFinding(SecurityFinding{ ID: issue.ID, Title: fmt.Sprintf("%s in %s", pattern.description, filepath.Base(filePath)), Category: "Code Security", Severity: pattern.severity, Description: pattern.description, Impact: sa.getImpactDescription(pattern.severity), Location: fmt.Sprintf("%s:%d:%d", filePath, lineNum+1, issue.Column), Evidence: issue.Evidence, CWE: pattern.cwe, Confidence: sa.getConfidenceScore(pattern.confidence), Timestamp: time.Now(), }) } } } } func (sa *SecurityAuditor) generateIssueID(filePath string, lineNum int, issueType string) string { data := fmt.Sprintf("%s:%d:%s", filePath, lineNum, issueType) hash := sha256.Sum256([]byte(data)) return fmt.Sprintf("CSI-%x", hash[:4]) } func (sa *SecurityAuditor) getImpactDescription(severity string) string { switch severity { case "CRITICAL": return "Critical security vulnerability that could lead to system compromise" case "HIGH": return "High-risk vulnerability that could lead to data breach or unauthorized access" case "MEDIUM": return "Medium-risk vulnerability that could be exploited under certain conditions" case "LOW": return "Low-risk vulnerability with limited security impact" default: return "Security finding requiring investigation" } } func (sa *SecurityAuditor) getConfidenceScore(confidence string) float64 { switch confidence { case "HIGH": return 0.9 case "MEDIUM": return 0.7 case "LOW": return 0.5 default: return 0.6 } } func (sa *SecurityAuditor) analyzeSecurityHotspots() { // Group issues by file to identify hotspots fileIssues := make(map[string][]CodeIssue) for _, issue := range sa.results.CodeScanResults.CodeIssues { fileIssues[issue.File] = append(fileIssues[issue.File], issue) } for file, issues := range fileIssues { if len(issues) >= 2 { // File with 2+ issues is a hotspot criticalCount := 0 for _, issue := range issues { if issue.Severity == "CRITICAL" || issue.Severity == "HIGH" { criticalCount++ } } riskScore := float64(len(issues)*10 + criticalCount*20) complexity := "LOW" if len(issues) > 5 { complexity = "HIGH" } else if len(issues) > 3 { complexity = "MEDIUM" } hotspot := SecurityHotspot{ File: file, Function: "Multiple", // Would need AST parsing for specific functions RiskScore: riskScore, Issues: len(issues), CriticalIssues: criticalCount, Complexity: complexity, Recommendation: sa.getHotspotRecommendation(len(issues), criticalCount), } sa.results.CodeScanResults.HotspotAnalysis = append(sa.results.CodeScanResults.HotspotAnalysis, hotspot) } } } func (sa *SecurityAuditor) getHotspotRecommendation(totalIssues, criticalIssues int) string { if criticalIssues > 0 { return "High priority: Address critical security issues immediately" } else if totalIssues > 5 { return "Medium priority: Refactor to reduce security complexity" } else { return "Low priority: Review and address identified issues" } } func (sa *SecurityAuditor) calculateCodeQualityMetrics() { // Simplified code quality metrics sa.results.CodeScanResults.QualityMetrics = CodeQualityMetrics{ LinesOfCode: sa.results.Environment.TotalLOC, CyclomaticComplexity: 2.5, // Simplified TechnicalDebt: float64(len(sa.results.CodeScanResults.CodeIssues)) * 0.5, TestCoverage: 75.0, // Would need actual test coverage data DuplicationRatio: 5.0, // Simplified } } func (sa *SecurityAuditor) runDependencyScan(ctx context.Context) error { if sa.config.Verbose { fmt.Println("Starting dependency security scan...") } sa.results.DependencyResults = &DependencyResults{ DependencyIssues: make([]DependencyIssue, 0), LicenseAnalysis: LicenseAnalysis{ LicenseTypes: make(map[string]int), ProblematicLicenses: make([]string, 0), UnknownLicenses: make([]string, 0), ComplianceIssues: make([]string, 0), }, } // Scan Go dependencies if sa.results.Environment.ProjectType == "Go" { return sa.scanGoDependencies() } return nil } func (sa *SecurityAuditor) scanGoDependencies() error { // Read go.mod file goModPath := filepath.Join(sa.rootDir, "go.mod") content, err := os.ReadFile(goModPath) if err != nil { return fmt.Errorf("failed to read go.mod: %w", err) } // Parse dependencies (simplified) lines := strings.Split(string(content), "\n") inRequireBlock := false for _, line := range lines { line = strings.TrimSpace(line) if strings.HasPrefix(line, "require (") { inRequireBlock = true continue } if line == ")" { inRequireBlock = false continue } if inRequireBlock || strings.HasPrefix(line, "require ") { // Parse dependency sa.analyzeDependency(line) } } sa.results.DependencyResults.TotalDependencies = len(sa.results.DependencyResults.DependencyIssues) sa.analyzeSupplyChainRisk() return nil } func (sa *SecurityAuditor) analyzeDependency(depLine string) { // Simplified dependency analysis parts := strings.Fields(strings.TrimPrefix(depLine, "require ")) if len(parts) < 2 { return } packageName := parts[0] version := parts[1] // Check for known vulnerable packages (simplified) vulnerablePackages := map[string]struct { cve string description string severity string cvss float64 }{ "github.com/gorilla/websocket": { cve: "CVE-2020-27813", description: "Denial of service vulnerability", severity: "MEDIUM", cvss: 5.3, }, // Add more vulnerable packages as needed } if vuln, exists := vulnerablePackages[packageName]; exists { issue := DependencyIssue{ Package: packageName, Version: version, LatestVersion: "latest", // Would need actual version checking Severity: vuln.severity, CVE: vuln.cve, Description: vuln.description, PatchAvailable: true, CVSS: vuln.cvss, Timestamp: time.Now(), } sa.results.DependencyResults.DependencyIssues = append(sa.results.DependencyResults.DependencyIssues, issue) sa.results.DependencyResults.VulnerableDeps++ // Add to security findings sa.addSecurityFinding(SecurityFinding{ ID: fmt.Sprintf("DEP-%s", packageName), Title: fmt.Sprintf("Vulnerable dependency: %s", packageName), Category: "Dependency Security", Severity: vuln.severity, Description: vuln.description, Impact: sa.getImpactDescription(vuln.severity), Location: "go.mod", Evidence: fmt.Sprintf("%s %s", packageName, version), CVSS: vuln.cvss, Confidence: 0.9, Timestamp: time.Now(), }) } // Analyze license (simplified) sa.results.DependencyResults.LicenseAnalysis.LicenseTypes["Unknown"]++ } func (sa *SecurityAuditor) analyzeSupplyChainRisk() { highRiskPackages := make([]string, 0) for _, issue := range sa.results.DependencyResults.DependencyIssues { if issue.Severity == "HIGH" || issue.Severity == "CRITICAL" { highRiskPackages = append(highRiskPackages, issue.Package) } } riskScore := float64(len(highRiskPackages)) * 10.0 if riskScore > 100.0 { riskScore = 100.0 } sa.results.DependencyResults.SupplyChainRisk = SupplyChainRisk{ HighRiskPackages: highRiskPackages, UnmaintainedPackages: make([]string, 0), // Would need maintenance analysis TyposquattingRisk: 20.0, // Simplified OverallRiskScore: riskScore, } } func (sa *SecurityAuditor) runSecretScan(ctx context.Context) error { if sa.config.Verbose { fmt.Println("Starting secret scan...") } sa.results.SecretScanResults = &SecretScanResults{ SecretFindings: make([]SecretFinding, 0), SecretPatterns: make(map[string]int), } err := filepath.WalkDir(sa.rootDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } if d.IsDir() { if strings.Contains(path, ".git") { return filepath.SkipDir } return nil } // Scan all text files for secrets if sa.isTextFile(path) { sa.results.SecretScanResults.FilesScanned++ sa.scanFileForSecrets(path) } return nil }) if err != nil { return fmt.Errorf("failed to scan for secrets: %w", err) } sa.results.SecretScanResults.SecretsFound = len(sa.results.SecretScanResults.SecretFindings) return nil } func (sa *SecurityAuditor) isTextFile(filePath string) bool { ext := filepath.Ext(filePath) textExts := []string{".go", ".js", ".ts", ".py", ".java", ".txt", ".md", ".yaml", ".yml", ".json", ".env", ".config"} for _, textExt := range textExts { if ext == textExt { return true } } return false } func (sa *SecurityAuditor) scanFileForSecrets(filePath string) { content, err := os.ReadFile(filePath) if err != nil { return } fileContent := string(content) lines := strings.Split(fileContent, "\n") // Define secret patterns secretPatterns := []struct { pattern *regexp.Regexp secretType string severity string confidence float64 }{ { pattern: regexp.MustCompile(`[a-zA-Z0-9][a-zA-Z0-9_-]{20,}`), secretType: "high_entropy_string", severity: "MEDIUM", confidence: 0.6, }, { pattern: regexp.MustCompile(`(?i)(api_key|apikey|secret|password|token)\s*[:=]\s*["'][^"']{8,}["']`), secretType: "api_key", severity: "HIGH", confidence: 0.8, }, { pattern: regexp.MustCompile(`(?i)private_key\s*[:=]\s*["'][^"']+["']`), secretType: "private_key", severity: "CRITICAL", confidence: 0.9, }, { pattern: regexp.MustCompile(`(?i)-----BEGIN [A-Z ]+-----`), secretType: "certificate", severity: "HIGH", confidence: 0.9, }, } for lineNum, line := range lines { for _, pattern := range secretPatterns { if matches := pattern.pattern.FindStringSubmatch(line); matches != nil { // Calculate entropy for high entropy strings entropy := sa.calculateEntropy(matches[0]) // Skip if entropy is too low for high_entropy_string pattern if pattern.secretType == "high_entropy_string" && entropy < 4.0 { continue } finding := SecretFinding{ Type: pattern.secretType, File: filePath, Line: lineNum + 1, Pattern: pattern.pattern.String(), Confidence: pattern.confidence, Entropy: entropy, Context: strings.TrimSpace(line), Severity: pattern.severity, Timestamp: time.Now(), } sa.results.SecretScanResults.SecretFindings = append(sa.results.SecretScanResults.SecretFindings, finding) sa.results.SecretScanResults.SecretPatterns[pattern.secretType]++ // Add to security findings sa.addSecurityFinding(SecurityFinding{ ID: fmt.Sprintf("SEC-%d", len(sa.results.SecurityFindings)), Title: fmt.Sprintf("Secret detected: %s", pattern.secretType), Category: "Secret Management", Severity: pattern.severity, Description: fmt.Sprintf("Potential %s found in source code", pattern.secretType), Impact: sa.getImpactDescription(pattern.severity), Location: fmt.Sprintf("%s:%d", filePath, lineNum+1), Evidence: finding.Context, Confidence: pattern.confidence, Timestamp: time.Now(), }) } } } } func (sa *SecurityAuditor) calculateEntropy(s string) float64 { if len(s) == 0 { return 0 } freq := make(map[rune]int) for _, char := range s { freq[char]++ } entropy := 0.0 length := float64(len(s)) for _, count := range freq { p := float64(count) / length if p > 0 { entropy -= p * (log2(p)) } } return entropy } func log2(x float64) float64 { return math.Log(x) / math.Log(2) } func (sa *SecurityAuditor) runPermissionScan(ctx context.Context) error { if sa.config.Verbose { fmt.Println("Starting permission scan...") } sa.results.PermissionResults = &PermissionResults{ FilePermissions: make([]FilePermission, 0), ConfigPermissions: make([]ConfigPermission, 0), PermissionIssues: make([]PermissionIssue, 0), } // Scan file permissions err := filepath.WalkDir(sa.rootDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return nil } info, err := d.Info() if err != nil { return nil } perm := FilePermission{ Path: path, Permissions: info.Mode().String(), } // Check for risky permissions mode := info.Mode() if mode&0002 != 0 { // World writable perm.Risky = true perm.Reason = "World writable" issue := PermissionIssue{ Type: "file_permission", Path: path, Issue: "World writable file", Severity: "HIGH", Remediation: "Remove world write permissions", Timestamp: time.Now(), } sa.results.PermissionResults.PermissionIssues = append(sa.results.PermissionResults.PermissionIssues, issue) } if mode&0111 != 0 && (strings.Contains(path, ".env") || strings.Contains(path, "config")) { perm.Risky = true perm.Reason = "Executable configuration file" issue := PermissionIssue{ Type: "file_permission", Path: path, Issue: "Executable configuration file", Severity: "MEDIUM", Remediation: "Remove execute permissions from configuration files", Timestamp: time.Now(), } sa.results.PermissionResults.PermissionIssues = append(sa.results.PermissionResults.PermissionIssues, issue) } sa.results.PermissionResults.FilePermissions = append(sa.results.PermissionResults.FilePermissions, perm) return nil }) if err != nil { return fmt.Errorf("failed to scan permissions: %w", err) } // Determine overall security if len(sa.results.PermissionResults.PermissionIssues) == 0 { sa.results.PermissionResults.OverallSecurity = "GOOD" } else if len(sa.results.PermissionResults.PermissionIssues) < 5 { sa.results.PermissionResults.OverallSecurity = "MEDIUM" } else { sa.results.PermissionResults.OverallSecurity = "POOR" } return nil } func (sa *SecurityAuditor) runNetworkScan(ctx context.Context) error { if sa.config.Verbose { fmt.Println("Starting network security scan...") } sa.results.NetworkResults = &NetworkResults{ OpenPorts: make([]PortInfo, 0), NetworkConnections: make([]NetworkConnection, 0), NetworkIssues: make([]NetworkIssue, 0), TLSAnalysis: TLSAnalysis{ TLSVersions: make([]string, 0), CipherSuites: make([]string, 0), Vulnerabilities: make([]string, 0), Recommendations: make([]string, 0), }, FirewallStatus: FirewallStatus{ Rules: make([]string, 0), Blocked: make([]string, 0), Allowed: make([]string, 0), Recommendations: make([]string, 0), }, } // Simplified network analysis (in reality would use actual network scanning tools) sa.analyzeNetworkConfiguration() return nil } func (sa *SecurityAuditor) analyzeNetworkConfiguration() { // Check for common network security configurations in code configFiles := []string{"main.go", "server.go", "config.yaml", "docker-compose.yml"} for _, file := range configFiles { filePath := filepath.Join(sa.rootDir, file) if content, err := os.ReadFile(filePath); err == nil { sa.analyzeNetworkConfigFile(string(content), file) } } // Add default recommendations sa.results.NetworkResults.TLSAnalysis.Recommendations = append( sa.results.NetworkResults.TLSAnalysis.Recommendations, "Use TLS 1.2 or higher for all network communications", "Implement proper certificate validation", "Use strong cipher suites", ) sa.results.NetworkResults.FirewallStatus.Recommendations = append( sa.results.NetworkResults.FirewallStatus.Recommendations, "Implement network segmentation", "Use firewall rules to restrict unnecessary network access", "Monitor network traffic for suspicious activity", ) } func (sa *SecurityAuditor) analyzeNetworkConfigFile(content, filename string) { // Look for network security issues in configuration if strings.Contains(content, ":80") && !strings.Contains(content, "redirect") { issue := NetworkIssue{ Type: "insecure_transport", Description: "HTTP server without TLS redirection", Severity: "MEDIUM", Impact: "Data transmitted in plaintext", Timestamp: time.Now(), } sa.results.NetworkResults.NetworkIssues = append(sa.results.NetworkResults.NetworkIssues, issue) } if strings.Contains(content, "0.0.0.0") { issue := NetworkIssue{ Type: "broad_network_binding", Description: "Service binding to all interfaces", Severity: "LOW", Impact: "Increased attack surface", Timestamp: time.Now(), } sa.results.NetworkResults.NetworkIssues = append(sa.results.NetworkResults.NetworkIssues, issue) } } func (sa *SecurityAuditor) runComplianceScan(ctx context.Context) error { if sa.config.Verbose { fmt.Println("Starting compliance scan...") } sa.results.ComplianceResults = &ComplianceResults{ FrameworksChecked: []string{"OWASP", "CIS", "NIST"}, ComplianceFindings: make([]ComplianceFinding, 0), RequirementStatus: make(map[string]string), } // Check OWASP compliance sa.checkOWASPCompliance() // Calculate compliance score total := len(sa.results.ComplianceResults.RequirementStatus) passed := 0 for _, status := range sa.results.ComplianceResults.RequirementStatus { if status == "PASS" { passed++ } } if total > 0 { sa.results.ComplianceResults.ComplianceScore = float64(passed) / float64(total) * 100.0 } return nil } func (sa *SecurityAuditor) checkOWASPCompliance() { // OWASP Top 10 checks (simplified) owaspChecks := []struct { id string name string description string }{ {"A01", "Broken Access Control", "Access control enforcement"}, {"A02", "Cryptographic Failures", "Data protection in transit and at rest"}, {"A03", "Injection", "Input validation and sanitization"}, {"A04", "Insecure Design", "Secure design patterns"}, {"A05", "Security Misconfiguration", "Secure configuration"}, {"A06", "Vulnerable Components", "Component vulnerability management"}, {"A07", "Authentication Failures", "Authentication implementation"}, {"A08", "Software Integrity Failures", "Software integrity"}, {"A09", "Security Logging", "Logging and monitoring"}, {"A10", "Server-Side Request Forgery", "SSRF protection"}, } for _, check := range owaspChecks { // Simplified compliance check status := "PASS" // Would implement actual checks if len(sa.results.SecurityFindings) > 5 { status = "FAIL" } sa.results.ComplianceResults.RequirementStatus[check.id] = status if status == "FAIL" { finding := ComplianceFinding{ Framework: "OWASP", Requirement: check.name, Status: status, Description: check.description, Evidence: "Multiple security findings detected", Priority: "HIGH", Timestamp: time.Now(), } sa.results.ComplianceResults.ComplianceFindings = append(sa.results.ComplianceResults.ComplianceFindings, finding) } } } func (sa *SecurityAuditor) performRiskAssessment() { sa.results.RiskAssessment = RiskAssessment{ RiskFactors: make([]RiskFactor, 0), RecommendedActions: make([]string, 0), } // Analyze risk factors sa.analyzeRiskFactors() sa.performThreatModeling() sa.analyzeAttackSurface() sa.assessBusinessImpact() // Determine overall risk sa.calculateOverallRisk() } func (sa *SecurityAuditor) analyzeRiskFactors() { riskFactors := []RiskFactor{ { Factor: "Code Vulnerabilities", Likelihood: sa.calculateVulnerabilityLikelihood(), Impact: 0.8, Description: "Risk from identified code vulnerabilities", }, { Factor: "Dependency Vulnerabilities", Likelihood: sa.calculateDependencyRisk(), Impact: 0.6, Description: "Risk from vulnerable dependencies", }, { Factor: "Secret Exposure", Likelihood: sa.calculateSecretRisk(), Impact: 0.9, Description: "Risk from exposed secrets", }, { Factor: "Network Security", Likelihood: sa.calculateNetworkRisk(), Impact: 0.7, Description: "Risk from network configuration issues", }, } for i := range riskFactors { riskFactors[i].RiskScore = riskFactors[i].Likelihood * riskFactors[i].Impact } sa.results.RiskAssessment.RiskFactors = riskFactors } func (sa *SecurityAuditor) calculateVulnerabilityLikelihood() float64 { if sa.results.CodeScanResults == nil { return 0.1 } criticalHigh := 0 for _, issue := range sa.results.CodeScanResults.CodeIssues { if issue.Severity == "CRITICAL" || issue.Severity == "HIGH" { criticalHigh++ } } likelihood := float64(criticalHigh) * 0.1 if likelihood > 1.0 { likelihood = 1.0 } return likelihood } func (sa *SecurityAuditor) calculateDependencyRisk() float64 { if sa.results.DependencyResults == nil { return 0.1 } risk := float64(sa.results.DependencyResults.VulnerableDeps) * 0.2 if risk > 1.0 { risk = 1.0 } return risk } func (sa *SecurityAuditor) calculateSecretRisk() float64 { if sa.results.SecretScanResults == nil { return 0.1 } risk := float64(sa.results.SecretScanResults.SecretsFound) * 0.3 if risk > 1.0 { risk = 1.0 } return risk } func (sa *SecurityAuditor) calculateNetworkRisk() float64 { if sa.results.NetworkResults == nil { return 0.1 } risk := float64(len(sa.results.NetworkResults.NetworkIssues)) * 0.15 if risk > 1.0 { risk = 1.0 } return risk } func (sa *SecurityAuditor) performThreatModeling() { threats := []Threat{ { Name: "Malicious Actor Exploitation", Description: "External attacker exploiting vulnerabilities", Likelihood: 0.6, Impact: 0.9, Risk: 0.54, Mitigations: []string{"Regular security updates", "Access controls", "Monitoring"}, }, { Name: "Insider Threat", Description: "Malicious insider with system access", Likelihood: 0.2, Impact: 0.8, Risk: 0.16, Mitigations: []string{"Least privilege", "Activity monitoring", "Background checks"}, }, { Name: "Supply Chain Attack", Description: "Compromise through third-party dependencies", Likelihood: 0.3, Impact: 0.7, Risk: 0.21, Mitigations: []string{"Dependency scanning", "Vendor assessment", "Code signing"}, }, } attackVectors := []AttackVector{ { Vector: "Network", Complexity: "Low", Privileges: "None", UserAction: "None", Scope: "Changed", Mitigations: []string{"Network segmentation", "Firewall rules", "TLS"}, }, { Vector: "Application", Complexity: "Low", Privileges: "Low", UserAction: "Required", Scope: "Unchanged", Mitigations: []string{"Input validation", "Authentication", "Authorization"}, }, } threatActors := []ThreatActor{ { Type: "External Attacker", Motivation: "Financial", Capability: "High", Opportunity: "Medium", Likelihood: 0.6, Techniques: []string{"Social engineering", "Vulnerability exploitation", "Credential theft"}, }, } sa.results.RiskAssessment.ThreatModel = ThreatModel{ ThreatsIdentified: threats, AttackVectors: attackVectors, AssetValuation: map[string]float64{ "Trading Algorithm": 100.0, "Private Keys": 90.0, "Market Data": 70.0, "Configuration": 50.0, }, ThreatActors: threatActors, } } func (sa *SecurityAuditor) analyzeAttackSurface() { networkExposure := 30.0 // Simplified if sa.results.NetworkResults != nil { networkExposure += float64(len(sa.results.NetworkResults.NetworkIssues)) * 10.0 } dataExposure := 20.0 // Simplified if sa.results.SecretScanResults != nil { dataExposure += float64(sa.results.SecretScanResults.SecretsFound) * 15.0 } sa.results.RiskAssessment.AttackSurface = AttackSurface{ NetworkExposure: networkExposure, ApplicationEndpoints: 5, // Simplified DataExposure: dataExposure, TrustedInterfaces: 3, // Simplified ExternalDependencies: sa.results.DependencyResults.TotalDependencies, ReductionOpportunities: []string{ "Minimize exposed network services", "Implement stronger access controls", "Reduce external dependencies", }, } } func (sa *SecurityAuditor) assessBusinessImpact() { sa.results.RiskAssessment.BusinessImpact = BusinessImpact{ FinancialImpact: 80.0, // High for MEV bot ReputationalImpact: 70.0, OperationalImpact: 85.0, ComplianceImpact: 60.0, OverallImpact: 75.0, } } func (sa *SecurityAuditor) calculateOverallRisk() { totalRisk := 0.0 for _, factor := range sa.results.RiskAssessment.RiskFactors { totalRisk += factor.RiskScore } avgRisk := totalRisk / float64(len(sa.results.RiskAssessment.RiskFactors)) switch { case avgRisk < 0.3: sa.results.RiskAssessment.OverallRisk = "LOW" sa.results.OverallRiskLevel = "LOW" sa.results.OverallRiskScore = avgRisk * 100.0 case avgRisk < 0.6: sa.results.RiskAssessment.OverallRisk = "MEDIUM" sa.results.OverallRiskLevel = "MEDIUM" sa.results.OverallRiskScore = avgRisk * 100.0 case avgRisk < 0.8: sa.results.RiskAssessment.OverallRisk = "HIGH" sa.results.OverallRiskLevel = "HIGH" sa.results.OverallRiskScore = avgRisk * 100.0 default: sa.results.RiskAssessment.OverallRisk = "CRITICAL" sa.results.OverallRiskLevel = "CRITICAL" sa.results.OverallRiskScore = avgRisk * 100.0 } // Generate recommended actions if avgRisk > 0.6 { sa.results.RiskAssessment.RecommendedActions = append( sa.results.RiskAssessment.RecommendedActions, "Immediate security review required", "Address critical and high severity vulnerabilities", "Implement comprehensive monitoring", ) } } func (sa *SecurityAuditor) generateRemediationPlan() { // Generate remediation actions based on findings if sa.results.CodeScanResults != nil { for _, issue := range sa.results.CodeScanResults.CodeIssues { if issue.Severity == "CRITICAL" || issue.Severity == "HIGH" { action := RemediationAction{ ID: fmt.Sprintf("REM-%s", issue.ID), Title: fmt.Sprintf("Fix %s in %s", issue.Type, filepath.Base(issue.File)), Priority: issue.Severity, Effort: "Medium", Description: fmt.Sprintf("Address %s at line %d", issue.Description, issue.Line), Steps: []string{ "Review the identified code", "Implement secure alternative", "Test the fix", "Update documentation", }, Timeline: "1-2 days", Owner: "Development Team", Dependencies: make([]string, 0), Timestamp: time.Now(), } sa.results.RemediationPlan = append(sa.results.RemediationPlan, action) } } } // Sort remediation plan by priority sort.Slice(sa.results.RemediationPlan, func(i, j int) bool { priorityOrder := map[string]int{"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3} return priorityOrder[sa.results.RemediationPlan[i].Priority] < priorityOrder[sa.results.RemediationPlan[j].Priority] }) } func (sa *SecurityAuditor) calculateSecurityMetrics() { totalIssues := len(sa.results.SecurityFindings) criticalHigh := 0 for _, finding := range sa.results.SecurityFindings { if finding.Severity == "CRITICAL" || finding.Severity == "HIGH" { criticalHigh++ } } // Calculate metrics vulnerabilityDensity := 0.0 if sa.results.Environment.TotalLOC > 0 { vulnerabilityDensity = float64(totalIssues) / float64(sa.results.Environment.TotalLOC) * 1000.0 } securityScore := 100.0 - float64(criticalHigh)*10.0 - float64(totalIssues)*2.0 if securityScore < 0 { securityScore = 0 } complianceRate := 100.0 if sa.results.ComplianceResults != nil { complianceRate = sa.results.ComplianceResults.ComplianceScore } sa.results.Metrics = SecurityMetrics{ VulnerabilityDensity: vulnerabilityDensity, SecurityScore: securityScore, ComplianceRate: complianceRate, RiskReduction: 30.0, // Simplified MeanTimeToDetection: 120.0, // 2 minutes (automated) MeanTimeToResolution: 4320.0, // 3 days (estimated) } } func (sa *SecurityAuditor) addSecurityFinding(finding SecurityFinding) { sa.results.SecurityFindings = append(sa.results.SecurityFindings, finding) } func (sa *SecurityAuditor) GenerateReport() error { // Sort security findings by severity sort.Slice(sa.results.SecurityFindings, func(i, j int) bool { severityOrder := map[string]int{"CRITICAL": 0, "HIGH": 1, "MEDIUM": 2, "LOW": 3} return severityOrder[sa.results.SecurityFindings[i].Severity] < severityOrder[sa.results.SecurityFindings[j].Severity] }) timestamp := time.Now().Format("2006-01-02_15-04-05") switch sa.config.ReportFormat { case "json": return sa.generateJSONReport(timestamp) case "sarif": return sa.generateSARIFReport(timestamp) case "txt": return sa.generateTextReport(timestamp) default: // Generate all formats if err := sa.generateJSONReport(timestamp); err != nil { return err } if err := sa.generateTextReport(timestamp); err != nil { return err } return nil } } func (sa *SecurityAuditor) generateJSONReport(timestamp string) error { jsonReport, err := json.MarshalIndent(sa.results, "", " ") if err != nil { return fmt.Errorf("failed to marshal results: %w", err) } jsonPath := filepath.Join(sa.config.OutputDir, fmt.Sprintf("security_audit_%s.json", timestamp)) if err := os.WriteFile(jsonPath, jsonReport, 0644); err != nil { return fmt.Errorf("failed to write JSON report: %w", err) } if sa.config.Verbose { fmt.Printf("JSON report generated: %s\n", jsonPath) } return nil } func (sa *SecurityAuditor) generateSARIFReport(timestamp string) error { // SARIF format implementation would go here // This is a simplified placeholder sarifPath := filepath.Join(sa.config.OutputDir, fmt.Sprintf("security_audit_%s.sarif", timestamp)) if sa.config.Verbose { fmt.Printf("SARIF report placeholder: %s\n", sarifPath) } return nil } func (sa *SecurityAuditor) generateTextReport(timestamp string) error { summary := fmt.Sprintf(`Security Audit Report Generated: %s Scan Type: %s Risk Threshold: %s Overall Risk Score: %.1f%% (%s) ENVIRONMENT =========== Project Type: %s Languages: %s Total Files: %d Total LOC: %d SUMMARY ======= Security Findings: %d Risk Level: %s Security Score: %.1f%% `, sa.results.Timestamp.Format("2006-01-02 15:04:05"), sa.results.ScanType, sa.results.RiskThreshold, sa.results.OverallRiskScore, sa.results.OverallRiskLevel, sa.results.Environment.ProjectType, strings.Join(sa.results.Environment.Languages, ", "), sa.results.Environment.TotalFiles, sa.results.Environment.TotalLOC, len(sa.results.SecurityFindings), sa.results.OverallRiskLevel, sa.results.Metrics.SecurityScore) // Add detailed results for each scan type if sa.results.CodeScanResults != nil { summary += fmt.Sprintf(`CODE SECURITY ============= Files Scanned: %d Vulnerabilities Found: %d Security Hotspots: %d `, sa.results.CodeScanResults.FilesScanned, sa.results.CodeScanResults.VulnerabilitiesFound, len(sa.results.CodeScanResults.HotspotAnalysis)) } if sa.results.DependencyResults != nil { summary += fmt.Sprintf(`DEPENDENCY SECURITY ================== Total Dependencies: %d Vulnerable Dependencies: %d Supply Chain Risk Score: %.1f%% `, sa.results.DependencyResults.TotalDependencies, sa.results.DependencyResults.VulnerableDeps, sa.results.DependencyResults.SupplyChainRisk.OverallRiskScore) } if sa.results.SecretScanResults != nil { summary += fmt.Sprintf(`SECRET MANAGEMENT ================ Files Scanned: %d Secrets Found: %d `, sa.results.SecretScanResults.FilesScanned, sa.results.SecretScanResults.SecretsFound) } // Top security findings if len(sa.results.SecurityFindings) > 0 { summary += "\nTOP SECURITY FINDINGS\n====================\n" for i, finding := range sa.results.SecurityFindings { if i >= 10 { // Show top 10 break } summary += fmt.Sprintf("%d. [%s] %s\n Location: %s\n Description: %s\n\n", i+1, finding.Severity, finding.Title, finding.Location, finding.Description) } } // Risk assessment summary += fmt.Sprintf(`RISK ASSESSMENT =============== Overall Risk: %s Risk Factors: `, sa.results.RiskAssessment.OverallRisk) for _, factor := range sa.results.RiskAssessment.RiskFactors { summary += fmt.Sprintf("- %s: %.1f%% risk\n", factor.Factor, factor.RiskScore*100) } // Recommendations if len(sa.results.RiskAssessment.RecommendedActions) > 0 { summary += "\nRECOMMENDATIONS\n===============\n" for i, action := range sa.results.RiskAssessment.RecommendedActions { summary += fmt.Sprintf("%d. %s\n", i+1, action) } } summaryPath := filepath.Join(sa.config.OutputDir, fmt.Sprintf("security_summary_%s.txt", timestamp)) if err := os.WriteFile(summaryPath, []byte(summary), 0644); err != nil { return fmt.Errorf("failed to write summary report: %w", err) } if sa.config.Verbose { fmt.Printf("Summary report generated: %s\n", summaryPath) } return nil }