Files
mev-beta/orig/tools/security-audit/internal/security_auditor.go
Administrator c54c569f30 refactor: move all remaining files to orig/ directory
Completed clean root directory structure:
- Root now contains only: .git, .env, docs/, orig/
- Moved all remaining files and directories to orig/:
  - Config files (.claude, .dockerignore, .drone.yml, etc.)
  - All .env variants (except active .env)
  - Git config (.gitconfig, .github, .gitignore, etc.)
  - Tool configs (.golangci.yml, .revive.toml, etc.)
  - Documentation (*.md files, @prompts)
  - Build files (Dockerfiles, Makefile, go.mod, go.sum)
  - Docker compose files
  - All source directories (scripts, tests, tools, etc.)
  - Runtime directories (logs, monitoring, reports)
  - Dependency files (node_modules, lib, cache)
  - Special files (--delete)

- Removed empty runtime directories (bin/, data/)

V2 structure is now clean:
- docs/planning/ - V2 planning documents
- orig/ - Complete V1 codebase preserved
- .env - Active environment config (not in git)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:53:05 +01:00

1898 lines
54 KiB
Go

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
}