Skip to content

漏洞报告分析

本示例展示如何使用 CVE Utils 分析安全公告、漏洞报告和相关文档,提取有价值的漏洞信息并生成分析报告。

场景描述

作为安全团队成员,您需要:

  • 从各种安全公告中提取 CVE 信息
  • 分析漏洞的时间分布和趋势
  • 生成统计报告供管理层参考
  • 识别需要优先处理的漏洞

完整示例

1. 安全公告分析器

go
package main

import (
    "fmt"
    "sort"
    "strings"
    "time"
    "github.com/scagogogo/cve"
)

// SecurityBulletin 表示一个安全公告
type SecurityBulletin struct {
    ID          string
    Title       string
    Content     string
    PublishDate time.Time
    Source      string
}

// VulnerabilityAnalyzer 漏洞分析器
type VulnerabilityAnalyzer struct {
    bulletins []SecurityBulletin
}

func NewVulnerabilityAnalyzer() *VulnerabilityAnalyzer {
    return &VulnerabilityAnalyzer{
        bulletins: make([]SecurityBulletin, 0),
    }
}

func (va *VulnerabilityAnalyzer) AddBulletin(bulletin SecurityBulletin) {
    va.bulletins = append(va.bulletins, bulletin)
}

// AnalyzeAllBulletins 分析所有公告
func (va *VulnerabilityAnalyzer) AnalyzeAllBulletins() *AnalysisReport {
    fmt.Println("=== 开始分析安全公告 ===")
    
    var allCves []string
    bulletinCves := make(map[string][]string)
    
    // 从每个公告中提取 CVE
    for _, bulletin := range va.bulletins {
        cves := va.extractCvesFromBulletin(bulletin)
        allCves = append(allCves, cves...)
        bulletinCves[bulletin.ID] = cves
        
        fmt.Printf("公告 %s: 发现 %d 个 CVE\n", bulletin.ID, len(cves))
    }
    
    // 数据清洗
    cleanedCves := va.cleanCveData(allCves)
    
    // 生成分析报告
    report := va.generateAnalysisReport(cleanedCves, bulletinCves)
    
    return report
}

// extractCvesFromBulletin 从公告中提取 CVE
func (va *VulnerabilityAnalyzer) extractCvesFromBulletin(bulletin SecurityBulletin) []string {
    // 合并标题和内容进行分析
    fullText := bulletin.Title + "\n" + bulletin.Content
    
    // 检查是否包含 CVE
    if !cve.IsContainsCve(fullText) {
        return []string{}
    }
    
    // 提取所有 CVE
    cves := cve.ExtractCve(fullText)
    
    // 验证每个 CVE
    var validCves []string
    for _, cveId := range cves {
        if cve.ValidateCve(cveId) {
            validCves = append(validCves, cveId)
        } else {
            fmt.Printf("警告: 发现无效 CVE %s 在公告 %s\n", cveId, bulletin.ID)
        }
    }
    
    return validCves
}

// cleanCveData 清洗 CVE 数据
func (va *VulnerabilityAnalyzer) cleanCveData(rawCves []string) []string {
    fmt.Printf("数据清洗: 原始数据 %d 个 CVE\n", len(rawCves))
    
    // 去重
    unique := cve.RemoveDuplicateCves(rawCves)
    fmt.Printf("去重后: %d 个 CVE\n", len(unique))
    
    // 排序
    sorted := cve.SortCves(unique)
    fmt.Printf("排序完成\n")
    
    return sorted
}

2. 分析报告生成

go
// AnalysisReport 分析报告结构
type AnalysisReport struct {
    TotalCves       int
    UniqueCves      int
    YearDistribution map[string]int
    RecentTrends    map[int]int
    TopYears        []string
    CriticalFindings []string
    Summary         string
}

// generateAnalysisReport 生成分析报告
func (va *VulnerabilityAnalyzer) generateAnalysisReport(cves []string, bulletinCves map[string][]string) *AnalysisReport {
    report := &AnalysisReport{
        UniqueCves:       len(cves),
        YearDistribution: make(map[string]int),
        RecentTrends:     make(map[int]int),
        CriticalFindings: make([]string, 0),
    }
    
    // 计算总 CVE 数(包括重复)
    totalCount := 0
    for _, bulletinCveList := range bulletinCves {
        totalCount += len(bulletinCveList)
    }
    report.TotalCves = totalCount
    
    // 年份分布分析
    grouped := cve.GroupByYear(cves)
    for year, yearCves := range grouped {
        report.YearDistribution[year] = len(yearCves)
    }
    
    // 最近几年趋势
    for i := 1; i <= 5; i++ {
        recent := cve.GetRecentCves(cves, i)
        report.RecentTrends[i] = len(recent)
    }
    
    // 找出 CVE 最多的年份
    report.TopYears = va.findTopYears(report.YearDistribution)
    
    // 关键发现
    report.CriticalFindings = va.identifyCriticalFindings(cves, grouped)
    
    // 生成摘要
    report.Summary = va.generateSummary(report)
    
    return report
}

// findTopYears 找出 CVE 最多的年份
func (va *VulnerabilityAnalyzer) findTopYears(yearDist map[string]int) []string {
    maxCount := 0
    var topYears []string
    
    for year, count := range yearDist {
        if count > maxCount {
            maxCount = count
            topYears = []string{year}
        } else if count == maxCount {
            topYears = append(topYears, year)
        }
    }
    
    sort.Strings(topYears)
    return topYears
}

// identifyCriticalFindings 识别关键发现
func (va *VulnerabilityAnalyzer) identifyCriticalFindings(cves []string, grouped map[string][]string) []string {
    var findings []string
    
    // 检查是否有大量最近的漏洞
    currentYear := time.Now().Year()
    thisYearCves := cve.FilterCvesByYear(cves, currentYear)
    if len(thisYearCves) > 10 {
        findings = append(findings, fmt.Sprintf("今年发现了 %d 个漏洞,需要重点关注", len(thisYearCves)))
    }
    
    // 检查是否有异常年份
    avgPerYear := float64(len(cves)) / float64(len(grouped))
    for year, yearCves := range grouped {
        if float64(len(yearCves)) > avgPerYear*1.5 {
            findings = append(findings, fmt.Sprintf("%s年漏洞数量异常高: %d个", year, len(yearCves)))
        }
    }
    
    // 检查最近趋势
    recent1 := cve.GetRecentCves(cves, 1)
    recent2 := cve.GetRecentCves(cves, 2)
    if len(recent1) > len(recent2)/2 {
        findings = append(findings, "最近一年漏洞增长迅速,建议加强安全防护")
    }
    
    return findings
}

// generateSummary 生成摘要
func (va *VulnerabilityAnalyzer) generateSummary(report *AnalysisReport) string {
    var summary strings.Builder
    
    summary.WriteString(fmt.Sprintf("本次分析共处理 %d 个 CVE(去重后 %d 个)。", 
        report.TotalCves, report.UniqueCves))
    
    if len(report.YearDistribution) > 0 {
        summary.WriteString(fmt.Sprintf("漏洞分布在 %d 个年份中,", len(report.YearDistribution)))
        
        if len(report.TopYears) > 0 {
            summary.WriteString(fmt.Sprintf("其中 %s 年漏洞数量最多。", 
                strings.Join(report.TopYears, "、")))
        }
    }
    
    if len(report.CriticalFindings) > 0 {
        summary.WriteString("发现以下关键问题需要关注。")
    }
    
    return summary.String()
}

3. 报告输出和可视化

go
// PrintReport 打印分析报告
func (va *VulnerabilityAnalyzer) PrintReport(report *AnalysisReport) {
    fmt.Println("\n" + strings.Repeat("=", 50))
    fmt.Println("           漏洞分析报告")
    fmt.Println(strings.Repeat("=", 50))
    
    // 基本统计
    fmt.Printf("总 CVE 数量: %d\n", report.TotalCves)
    fmt.Printf("唯一 CVE 数量: %d\n", report.UniqueCves)
    fmt.Printf("重复率: %.1f%%\n", 
        float64(report.TotalCves-report.UniqueCves)/float64(report.TotalCves)*100)
    
    // 年份分布
    fmt.Println("\n年份分布:")
    years := make([]string, 0, len(report.YearDistribution))
    for year := range report.YearDistribution {
        years = append(years, year)
    }
    sort.Strings(years)
    
    for _, year := range years {
        count := report.YearDistribution[year]
        percentage := float64(count) / float64(report.UniqueCves) * 100
        bar := strings.Repeat("█", count/2) // 简单的条形图
        fmt.Printf("  %s年: %3d个 (%5.1f%%) %s\n", year, count, percentage, bar)
    }
    
    // 最近趋势
    fmt.Println("\n最近趋势:")
    for i := 1; i <= 5; i++ {
        count := report.RecentTrends[i]
        fmt.Printf("  最近%d年: %d\n", i, count)
    }
    
    // 关键发现
    if len(report.CriticalFindings) > 0 {
        fmt.Println("\n关键发现:")
        for i, finding := range report.CriticalFindings {
            fmt.Printf("  %d. %s\n", i+1, finding)
        }
    }
    
    // 摘要
    fmt.Println("\n摘要:")
    fmt.Printf("  %s\n", report.Summary)
    
    fmt.Println(strings.Repeat("=", 50))
}

// ExportToCSV 导出为 CSV 格式
func (va *VulnerabilityAnalyzer) ExportToCSV(cves []string, filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()
    
    writer := csv.NewWriter(file)
    defer writer.Flush()
    
    // 写入标题行
    headers := []string{"CVE", "Year", "Sequence", "Age_Years"}
    writer.Write(headers)
    
    currentYear := time.Now().Year()
    
    // 写入数据行
    for _, cveId := range cves {
        year := cve.ExtractCveYearAsInt(cveId)
        seq := cve.ExtractCveSeqAsInt(cveId)
        age := currentYear - year
        
        record := []string{
            cveId,
            fmt.Sprintf("%d", year),
            fmt.Sprintf("%d", seq),
            fmt.Sprintf("%d", age),
        }
        writer.Write(record)
    }
    
    return nil
}

4. 主程序示例

go
func main() {
    // 创建分析器
    analyzer := NewVulnerabilityAnalyzer()
    
    // 添加示例安全公告
    bulletins := []SecurityBulletin{
        {
            ID:      "SEC-2024-001",
            Title:   "关键安全更新 - 修复多个高危漏洞",
            Content: `
            本次更新修复了以下关键漏洞:
            1. CVE-2021-44228 - Apache Log4j 远程代码执行漏洞
            2. CVE-2022-12345 - 自定义组件权限提升漏洞
            3. CVE-2023-1234 - 第三方库信息泄露漏洞
            
            建议所有用户立即更新到最新版本。
            `,
            PublishDate: time.Now().AddDate(0, -1, 0),
            Source:      "内部安全团队",
        },
        {
            ID:      "SEC-2024-002", 
            Title:   "紧急安全补丁",
            Content: `
            发现新的零日漏洞:
            - CVE-2024-5678 - 核心组件远程代码执行
            - cve-2024-9999 - 认证绕过漏洞
            
            同时修复了之前报告的 CVE-2023-1234。
            `,
            PublishDate: time.Now().AddDate(0, 0, -7),
            Source:      "外部安全研究员",
        },
        {
            ID:      "SEC-2024-003",
            Title:   "月度安全更新",
            Content: `
            本月修复的漏洞包括:
            CVE-2022-11111, CVE-2022-22222, CVE-2023-33333
            以及多个低危漏洞 CVE-2024-1111 到 CVE-2024-1115。
            `,
            PublishDate: time.Now().AddDate(0, 0, -15),
            Source:      "产品安全团队",
        },
    }
    
    // 添加公告到分析器
    for _, bulletin := range bulletins {
        analyzer.AddBulletin(bulletin)
    }
    
    // 执行分析
    report := analyzer.AnalyzeAllBulletins()
    
    // 打印报告
    analyzer.PrintReport(report)
    
    // 导出数据
    allCves := []string{}
    for _, bulletin := range bulletins {
        cves := analyzer.extractCvesFromBulletin(bulletin)
        allCves = append(allCves, cves...)
    }
    
    cleanedCves := analyzer.cleanCveData(allCves)
    
    if err := analyzer.ExportToCSV(cleanedCves, "vulnerability_analysis.csv"); err != nil {
        fmt.Printf("导出 CSV 失败: %v\n", err)
    } else {
        fmt.Println("数据已导出到 vulnerability_analysis.csv")
    }
}

高级分析功能

1. 时间序列分析

go
func (va *VulnerabilityAnalyzer) analyzeTimeSeries(cves []string) {
    fmt.Println("\n=== 时间序列分析 ===")
    
    grouped := cve.GroupByYear(cves)
    
    // 按年份排序
    years := make([]string, 0, len(grouped))
    for year := range grouped {
        years = append(years, year)
    }
    sort.Strings(years)
    
    // 计算年度增长率
    fmt.Println("年度增长率:")
    for i := 1; i < len(years); i++ {
        prevYear := years[i-1]
        currYear := years[i]
        
        prevCount := len(grouped[prevYear])
        currCount := len(grouped[currYear])
        
        if prevCount > 0 {
            growthRate := float64(currCount-prevCount) / float64(prevCount) * 100
            fmt.Printf("  %s -> %s: %+.1f%% (%d -> %d)\n", 
                prevYear, currYear, growthRate, prevCount, currCount)
        }
    }
    
    // 移动平均
    if len(years) >= 3 {
        fmt.Println("\n3年移动平均:")
        for i := 2; i < len(years); i++ {
            sum := 0
            for j := i - 2; j <= i; j++ {
                sum += len(grouped[years[j]])
            }
            avg := float64(sum) / 3.0
            fmt.Printf("  %s: %.1f\n", years[i], avg)
        }
    }
}

2. 严重性评估

go
func (va *VulnerabilityAnalyzer) assessSeverity(cves []string) map[string]string {
    severity := make(map[string]string)
    
    for _, cveId := range cves {
        year := cve.ExtractCveYearAsInt(cveId)
        seq := cve.ExtractCveSeqAsInt(cveId)
        
        // 简单的严重性评估逻辑(实际应用中应该查询 CVSS 数据库)
        currentYear := time.Now().Year()
        age := currentYear - year
        
        if age <= 1 && seq > 10000 {
            severity[cveId] = "Critical"
        } else if age <= 2 && seq > 5000 {
            severity[cveId] = "High"
        } else if age <= 3 {
            severity[cveId] = "Medium"
        } else {
            severity[cveId] = "Low"
        }
    }
    
    return severity
}

3. 相关性分析

go
func (va *VulnerabilityAnalyzer) analyzeCorrelations(bulletinCves map[string][]string) {
    fmt.Println("\n=== 相关性分析 ===")
    
    // 找出经常一起出现的 CVE
    cooccurrence := make(map[string]map[string]int)
    
    for _, cves := range bulletinCves {
        for i, cveA := range cves {
            if cooccurrence[cveA] == nil {
                cooccurrence[cveA] = make(map[string]int)
            }
            
            for j, cveB := range cves {
                if i != j {
                    cooccurrence[cveA][cveB]++
                }
            }
        }
    }
    
    // 输出高相关性的 CVE 对
    fmt.Println("高相关性 CVE 对:")
    for cveA, related := range cooccurrence {
        for cveB, count := range related {
            if count >= 2 && cveA < cveB { // 避免重复输出
                fmt.Printf("  %s <-> %s: 共同出现 %d\n", cveA, cveB, count)
            }
        }
    }
}

使用场景

1. 安全团队日常分析

bash
# 运行分析器
go run vulnerability_analyzer.go

# 查看生成的报告
cat vulnerability_analysis.csv

2. 自动化集成

go
// 定期分析任务
func scheduleAnalysis() {
    ticker := time.NewTicker(24 * time.Hour) // 每天运行一次
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            analyzer := NewVulnerabilityAnalyzer()
            // 从数据源加载最新公告
            // analyzer.LoadFromDatabase()
            
            report := analyzer.AnalyzeAllBulletins()
            
            // 发送报告
            // sendReportToTeam(report)
        }
    }
}

3. 与其他系统集成

go
// 与威胁情报系统集成
func enrichWithThreatIntel(cves []string) map[string]ThreatInfo {
    threatInfo := make(map[string]ThreatInfo)
    
    for _, cveId := range cves {
        // 查询威胁情报数据库
        info := queryThreatIntelligence(cveId)
        threatInfo[cveId] = info
    }
    
    return threatInfo
}

最佳实践

  1. 数据验证: 始终验证提取的 CVE 格式
  2. 错误处理: 对无效数据进行适当的错误处理
  3. 性能优化: 对大量数据使用批量处理
  4. 报告格式: 提供多种输出格式(控制台、CSV、JSON)
  5. 自动化: 集成到 CI/CD 流程中进行定期分析

这个示例展示了如何使用 CVE Utils 构建一个完整的漏洞分析系统,可以根据实际需求进行扩展和定制。

基于 MIT 许可证发布。