Skip to content

CVE 验证处理

本示例展示如何使用 CVE Utils 实现健壮的 CVE 验证和处理系统,包括用户输入验证、批量数据处理、错误处理和性能优化等最佳实践。

场景描述

作为 Web 应用或 API 开发者,您需要:

  • 验证用户输入的 CVE 格式
  • 处理各种异常情况和错误
  • 实现高性能的批量验证
  • 提供友好的错误信息和建议
  • 确保系统的稳定性和可靠性

完整示例

1. CVE 验证器

go
package main

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

// ValidationResult 验证结果
type ValidationResult struct {
    IsValid     bool      `json:"is_valid"`
    CVE         string    `json:"cve,omitempty"`
    ErrorType   string    `json:"error_type,omitempty"`
    ErrorMsg    string    `json:"error_message,omitempty"`
    Suggestions []string  `json:"suggestions,omitempty"`
    ProcessTime time.Duration `json:"process_time_ns"`
}

// CVEValidator CVE 验证器
type CVEValidator struct {
    strictMode    bool
    maxYearOffset int
    enableSuggestions bool
}

func NewCVEValidator() *CVEValidator {
    return &CVEValidator{
        strictMode:        false,
        maxYearOffset:     10,
        enableSuggestions: true,
    }
}

func (v *CVEValidator) SetStrictMode(strict bool) {
    v.strictMode = strict
}

func (v *CVEValidator) SetMaxYearOffset(offset int) {
    v.maxYearOffset = offset
}

// ValidateSingle 验证单个 CVE
func (v *CVEValidator) ValidateSingle(input string) ValidationResult {
    start := time.Now()

    result := ValidationResult{
        ProcessTime: time.Since(start),
    }

    // 基本输入检查
    if input == "" {
        result.ErrorType = "EMPTY_INPUT"
        result.ErrorMsg = "输入不能为空"
        result.Suggestions = []string{"请输入有效的 CVE 编号,格式如:CVE-2022-12345"}
        return result
    }

    // 去除前后空格
    trimmed := strings.TrimSpace(input)

    // 基本格式检查
    if !cve.IsCve(trimmed) {
        result.ErrorType = "INVALID_FORMAT"
        result.ErrorMsg = "CVE 格式无效"
        result.Suggestions = v.generateFormatSuggestions(trimmed)
        return result
    }

    // 格式化
    formatted := cve.Format(trimmed)

    // 严格模式下的额外验证
    if v.strictMode {
        if !cve.ValidateCve(formatted) {
            result.ErrorType = "VALIDATION_FAILED"
            result.ErrorMsg = "CVE 验证失败"
            result.Suggestions = v.generateValidationSuggestions(formatted)
            return result
        }

        // 年份合理性检查
        if !cve.IsCveYearOk(formatted, v.maxYearOffset) {
            result.ErrorType = "INVALID_YEAR"
            result.ErrorMsg = "CVE 年份不在合理范围内"
            result.Suggestions = v.generateYearSuggestions(formatted)
            return result
        }
    }

    // 验证成功
    result.IsValid = true
    result.CVE = formatted
    result.ProcessTime = time.Since(start)

    return result
}

// generateFormatSuggestions 生成格式建议
func (v *CVEValidator) generateFormatSuggestions(input string) []string {
    if !v.enableSuggestions {
        return nil
    }

    var suggestions []string

    // 检查是否缺少 CVE 前缀
    if strings.Contains(input, "-") && !strings.HasPrefix(strings.ToUpper(input), "CVE-") {
        suggestions = append(suggestions, "尝试添加 'CVE-' 前缀:CVE-"+input)
    }

    // 检查是否有常见的格式错误
    if strings.Contains(input, "/") {
        fixed := strings.ReplaceAll(input, "/", "-")
        suggestions = append(suggestions, "尝试将 '/' 替换为 '-':"+fixed)
    }

    // 检查是否有空格
    if strings.Contains(input, " ") {
        fixed := strings.ReplaceAll(input, " ", "-")
        suggestions = append(suggestions, "尝试将空格替换为 '-':"+fixed)
    }

    // 提供标准格式示例
    suggestions = append(suggestions, "标准格式示例:CVE-2022-12345")

    return suggestions
}

// generateValidationSuggestions 生成验证建议
func (v *CVEValidator) generateValidationSuggestions(cveId string) []string {
    if !v.enableSuggestions {
        return nil
    }

    var suggestions []string

    year := cve.ExtractCveYearAsInt(cveId)
    seq := cve.ExtractCveSeqAsInt(cveId)

    currentYear := time.Now().Year()

    if year < 1970 {
        suggestions = append(suggestions, "年份不能早于 1970 年")
    }

    if year > currentYear {
        suggestions = append(suggestions, fmt.Sprintf("年份不能晚于当前年份 %d", currentYear))
    }

    if seq <= 0 {
        suggestions = append(suggestions, "序列号必须是正整数")
    }

    return suggestions
}

// generateYearSuggestions 生成年份建议
func (v *CVEValidator) generateYearSuggestions(cveId string) []string {
    if !v.enableSuggestions {
        return nil
    }

    var suggestions []string
    year := cve.ExtractCveYearAsInt(cveId)
    currentYear := time.Now().Year()

    if year < 1970 {
        suggestions = append(suggestions, "CVE 系统始于 1970 年左右,请检查年份")
    } else if year > currentYear+v.maxYearOffset {
        suggestions = append(suggestions,
            fmt.Sprintf("年份 %d 超出合理范围(当前年份+%d),请检查输入",
                year, v.maxYearOffset))
    }

    return suggestions
}

2. 批量验证处理器

go
// BatchProcessor 批量处理器
type BatchProcessor struct {
    validator     *CVEValidator
    maxBatchSize  int
    maxConcurrency int
}

func NewBatchProcessor(validator *CVEValidator) *BatchProcessor {
    return &BatchProcessor{
        validator:      validator,
        maxBatchSize:   10000,
        maxConcurrency: 10,
    }
}

// BatchValidationResult 批量验证结果
type BatchValidationResult struct {
    TotalCount    int                    `json:"total_count"`
    ValidCount    int                    `json:"valid_count"`
    InvalidCount  int                    `json:"invalid_count"`
    ProcessTime   time.Duration          `json:"process_time_ns"`
    ValidCVEs     []string              `json:"valid_cves"`
    InvalidCVEs   []ValidationResult     `json:"invalid_cves"`
    ErrorSummary  map[string]int         `json:"error_summary"`
}

// ValidateBatch 批量验证
func (bp *BatchProcessor) ValidateBatch(inputs []string) BatchValidationResult {
    start := time.Now()

    result := BatchValidationResult{
        TotalCount:   len(inputs),
        ValidCVEs:    make([]string, 0),
        InvalidCVEs:  make([]ValidationResult, 0),
        ErrorSummary: make(map[string]int),
    }

    // 检查批量大小限制
    if len(inputs) > bp.maxBatchSize {
        // 分批处理
        return bp.processBatchesSequentially(inputs)
    }

    // 并发处理
    if len(inputs) > 100 && bp.maxConcurrency > 1 {
        return bp.processBatchesConcurrently(inputs)
    }

    // 顺序处理
    for _, input := range inputs {
        validationResult := bp.validator.ValidateSingle(input)

        if validationResult.IsValid {
            result.ValidCount++
            result.ValidCVEs = append(result.ValidCVEs, validationResult.CVE)
        } else {
            result.InvalidCount++
            result.InvalidCVEs = append(result.InvalidCVEs, validationResult)
            result.ErrorSummary[validationResult.ErrorType]++
        }
    }

    result.ProcessTime = time.Since(start)
    return result
}

// processBatchesConcurrently 并发处理批次
func (bp *BatchProcessor) processBatchesConcurrently(inputs []string) BatchValidationResult {
    start := time.Now()

    // 创建工作通道
    inputChan := make(chan string, len(inputs))
    resultChan := make(chan ValidationResult, len(inputs))

    // 启动工作协程
    for i := 0; i < bp.maxConcurrency; i++ {
        go func() {
            for input := range inputChan {
                result := bp.validator.ValidateSingle(input)
                resultChan <- result
            }
        }()
    }

    // 发送输入数据
    for _, input := range inputs {
        inputChan <- input
    }
    close(inputChan)

    // 收集结果
    result := BatchValidationResult{
        TotalCount:   len(inputs),
        ValidCVEs:    make([]string, 0),
        InvalidCVEs:  make([]ValidationResult, 0),
        ErrorSummary: make(map[string]int),
    }

    for i := 0; i < len(inputs); i++ {
        validationResult := <-resultChan

        if validationResult.IsValid {
            result.ValidCount++
            result.ValidCVEs = append(result.ValidCVEs, validationResult.CVE)
        } else {
            result.InvalidCount++
            result.InvalidCVEs = append(result.InvalidCVEs, validationResult)
            result.ErrorSummary[validationResult.ErrorType]++
        }
    }

    result.ProcessTime = time.Since(start)
    return result
}

// processBatchesSequentially 分批顺序处理
func (bp *BatchProcessor) processBatchesSequentially(inputs []string) BatchValidationResult {
    start := time.Now()

    result := BatchValidationResult{
        TotalCount:   len(inputs),
        ValidCVEs:    make([]string, 0),
        InvalidCVEs:  make([]ValidationResult, 0),
        ErrorSummary: make(map[string]int),
    }

    // 分批处理
    for i := 0; i < len(inputs); i += bp.maxBatchSize {
        end := i + bp.maxBatchSize
        if end > len(inputs) {
            end = len(inputs)
        }

        batch := inputs[i:end]
        batchResult := bp.ValidateBatch(batch)

        // 合并结果
        result.ValidCount += batchResult.ValidCount
        result.InvalidCount += batchResult.InvalidCount
        result.ValidCVEs = append(result.ValidCVEs, batchResult.ValidCVEs...)
        result.InvalidCVEs = append(result.InvalidCVEs, batchResult.InvalidCVEs...)

        for errorType, count := range batchResult.ErrorSummary {
            result.ErrorSummary[errorType] += count
        }

        fmt.Printf("已处理批次 %d-%d (%d/%d)\n", i+1, end, end, len(inputs))
    }

    result.ProcessTime = time.Since(start)
    return result
}

3. 错误处理和恢复

go
// ErrorHandler 错误处理器
type ErrorHandler struct {
    maxRetries    int
    retryDelay    time.Duration
    enableLogging bool
}

func NewErrorHandler() *ErrorHandler {
    return &ErrorHandler{
        maxRetries:    3,
        retryDelay:    100 * time.Millisecond,
        enableLogging: true,
    }
}

// ProcessWithRetry 带重试的处理
func (eh *ErrorHandler) ProcessWithRetry(input string, validator *CVEValidator) ValidationResult {
    var lastResult ValidationResult

    for attempt := 0; attempt <= eh.maxRetries; attempt++ {
        // 尝试处理
        result := eh.safeValidate(input, validator)

        // 如果成功或者是明确的格式错误,直接返回
        if result.IsValid || result.ErrorType == "INVALID_FORMAT" || result.ErrorType == "EMPTY_INPUT" {
            return result
        }

        lastResult = result

        // 记录重试
        if eh.enableLogging && attempt < eh.maxRetries {
            fmt.Printf("验证失败,第 %d 次重试: %s (错误: %s)\n",
                attempt+1, input, result.ErrorMsg)
        }

        // 等待后重试
        if attempt < eh.maxRetries {
            time.Sleep(eh.retryDelay)
        }
    }

    // 所有重试都失败
    lastResult.ErrorMsg = fmt.Sprintf("经过 %d 次重试后仍然失败: %s",
        eh.maxRetries, lastResult.ErrorMsg)

    return lastResult
}

// safeValidate 安全的验证函数
func (eh *ErrorHandler) safeValidate(input string, validator *CVEValidator) (result ValidationResult) {
    defer func() {
        if r := recover(); r != nil {
            result = ValidationResult{
                IsValid:   false,
                ErrorType: "PANIC_ERROR",
                ErrorMsg:  fmt.Sprintf("验证过程中发生异常: %v", r),
            }

            if eh.enableLogging {
                fmt.Printf("验证异常: %v, 输入: %s\n", r, input)
            }
        }
    }()

    return validator.ValidateSingle(input)
}

// RecoverableProcessor 可恢复的处理器
type RecoverableProcessor struct {
    validator    *CVEValidator
    errorHandler *ErrorHandler
    failureLog   []FailureRecord
}

type FailureRecord struct {
    Input     string    `json:"input"`
    Error     string    `json:"error"`
    Timestamp time.Time `json:"timestamp"`
    Attempts  int       `json:"attempts"`
}

func NewRecoverableProcessor() *RecoverableProcessor {
    return &RecoverableProcessor{
        validator:    NewCVEValidator(),
        errorHandler: NewErrorHandler(),
        failureLog:   make([]FailureRecord, 0),
    }
}

// ProcessWithRecovery 带恢复的处理
func (rp *RecoverableProcessor) ProcessWithRecovery(inputs []string) BatchValidationResult {
    result := BatchValidationResult{
        TotalCount:   len(inputs),
        ValidCVEs:    make([]string, 0),
        InvalidCVEs:  make([]ValidationResult, 0),
        ErrorSummary: make(map[string]int),
    }

    start := time.Now()

    for _, input := range inputs {
        validationResult := rp.errorHandler.ProcessWithRetry(input, rp.validator)

        if validationResult.IsValid {
            result.ValidCount++
            result.ValidCVEs = append(result.ValidCVEs, validationResult.CVE)
        } else {
            result.InvalidCount++
            result.InvalidCVEs = append(result.InvalidCVEs, validationResult)
            result.ErrorSummary[validationResult.ErrorType]++

            // 记录失败
            rp.failureLog = append(rp.failureLog, FailureRecord{
                Input:     input,
                Error:     validationResult.ErrorMsg,
                Timestamp: time.Now(),
                Attempts:  rp.errorHandler.maxRetries + 1,
            })
        }
    }

    result.ProcessTime = time.Since(start)
    return result
}

// GetFailureReport 获取失败报告
func (rp *RecoverableProcessor) GetFailureReport() map[string]interface{} {
    errorTypes := make(map[string]int)
    for _, failure := range rp.failureLog {
        // 简单的错误分类
        if strings.Contains(failure.Error, "格式") {
            errorTypes["FORMAT_ERRORS"]++
        } else if strings.Contains(failure.Error, "年份") {
            errorTypes["YEAR_ERRORS"]++
        } else if strings.Contains(failure.Error, "异常") {
            errorTypes["SYSTEM_ERRORS"]++
        } else {
            errorTypes["OTHER_ERRORS"]++
        }
    }

    return map[string]interface{}{
        "total_failures":    len(rp.failureLog),
        "error_distribution": errorTypes,
        "recent_failures":   rp.getRecentFailures(10),
        "failure_rate":      rp.calculateFailureRate(),
    }
}

func (rp *RecoverableProcessor) getRecentFailures(limit int) []FailureRecord {
    if len(rp.failureLog) <= limit {
        return rp.failureLog
    }
    return rp.failureLog[len(rp.failureLog)-limit:]
}

func (rp *RecoverableProcessor) calculateFailureRate() float64 {
    if len(rp.failureLog) == 0 {
        return 0.0
    }

    // 计算最近一小时的失败率
    oneHourAgo := time.Now().Add(-time.Hour)
    recentFailures := 0

    for _, failure := range rp.failureLog {
        if failure.Timestamp.After(oneHourAgo) {
            recentFailures++
        }
    }

    return float64(recentFailures) / float64(len(rp.failureLog)) * 100
}

4. 性能优化

go
// PerformanceOptimizer 性能优化器
type PerformanceOptimizer struct {
    cache          map[string]ValidationResult
    cacheSize      int
    cacheHits      int
    cacheMisses    int
    enableCache    bool
    enableMetrics  bool
}

func NewPerformanceOptimizer() *PerformanceOptimizer {
    return &PerformanceOptimizer{
        cache:         make(map[string]ValidationResult),
        cacheSize:     10000,
        enableCache:   true,
        enableMetrics: true,
    }
}

// OptimizedValidate 优化的验证函数
func (po *PerformanceOptimizer) OptimizedValidate(input string, validator *CVEValidator) ValidationResult {
    // 缓存查找
    if po.enableCache {
        if cached, exists := po.cache[input]; exists {
            po.cacheHits++
            return cached
        }
        po.cacheMisses++
    }

    // 执行验证
    result := validator.ValidateSingle(input)

    // 缓存结果
    if po.enableCache && len(po.cache) < po.cacheSize {
        po.cache[input] = result
    }

    return result
}

// GetCacheStats 获取缓存统计
func (po *PerformanceOptimizer) GetCacheStats() map[string]interface{} {
    total := po.cacheHits + po.cacheMisses
    hitRate := 0.0
    if total > 0 {
        hitRate = float64(po.cacheHits) / float64(total) * 100
    }

    return map[string]interface{}{
        "cache_size":    len(po.cache),
        "cache_hits":    po.cacheHits,
        "cache_misses":  po.cacheMisses,
        "hit_rate":      hitRate,
        "max_cache_size": po.cacheSize,
    }
}

// ClearCache 清空缓存
func (po *PerformanceOptimizer) ClearCache() {
    po.cache = make(map[string]ValidationResult)
    po.cacheHits = 0
    po.cacheMisses = 0
}

// BenchmarkValidator 性能基准测试
func BenchmarkValidator(validator *CVEValidator, testData []string, iterations int) {
    fmt.Printf("开始性能基准测试: %d 次迭代, %d 个测试样本\n", iterations, len(testData))

    start := time.Now()
    totalProcessed := 0

    for i := 0; i < iterations; i++ {
        for _, input := range testData {
            validator.ValidateSingle(input)
            totalProcessed++
        }
    }

    duration := time.Since(start)
    avgTime := duration / time.Duration(totalProcessed)
    throughput := float64(totalProcessed) / duration.Seconds()

    fmt.Printf("基准测试结果:\n")
    fmt.Printf("  总处理数: %d\n", totalProcessed)
    fmt.Printf("  总耗时: %v\n", duration)
    fmt.Printf("  平均耗时: %v\n", avgTime)
    fmt.Printf("  吞吐量: %.2f 次/秒\n", throughput)
}

5. 主程序示例

go
func main() {
    fmt.Println("=== CVE 验证处理系统 ===")

    // 1. 基本验证示例
    fmt.Println("\n1. 基本验证示例")
    validator := NewCVEValidator()

    testInputs := []string{
        "CVE-2022-12345",        // 有效
        " cve-2021-44228 ",      // 有效(需要格式化)
        "2022-12345",            // 无效(缺少前缀)
        "CVE-2022-ABC",          // 无效(序列号不是数字)
        "",                      // 无效(空输入)
        "CVE/2022/12345",        // 无效(错误分隔符)
    }

    for _, input := range testInputs {
        result := validator.ValidateSingle(input)
        fmt.Printf("输入: %-20s -> ", fmt.Sprintf("'%s'", input))
        if result.IsValid {
            fmt.Printf("✅ 有效: %s\n", result.CVE)
        } else {
            fmt.Printf("❌ 无效: %s\n", result.ErrorMsg)
            if len(result.Suggestions) > 0 {
                fmt.Printf("    建议: %s\n", result.Suggestions[0])
            }
        }
    }

    // 2. 批量验证示例
    fmt.Println("\n2. 批量验证示例")
    batchProcessor := NewBatchProcessor(validator)

    largeBatch := []string{
        "CVE-2021-44228", "CVE-2022-12345", "CVE-2023-1234",
        "invalid-cve", "CVE-2024-5678", "2022-9999",
        "CVE-2020-1111", "cve-2021-2222", "",
    }

    batchResult := batchProcessor.ValidateBatch(largeBatch)

    fmt.Printf("批量验证结果:\n")
    fmt.Printf("  总数: %d\n", batchResult.TotalCount)
    fmt.Printf("  有效: %d (%.1f%%)\n", batchResult.ValidCount,
        float64(batchResult.ValidCount)/float64(batchResult.TotalCount)*100)
    fmt.Printf("  无效: %d (%.1f%%)\n", batchResult.InvalidCount,
        float64(batchResult.InvalidCount)/float64(batchResult.TotalCount)*100)
    fmt.Printf("  处理时间: %v\n", batchResult.ProcessTime)

    fmt.Printf("  错误分布:\n")
    for errorType, count := range batchResult.ErrorSummary {
        fmt.Printf("    %s: %d\n", errorType, count)
    }

    // 3. 错误处理和恢复示例
    fmt.Println("\n3. 错误处理和恢复示例")
    recoverable := NewRecoverableProcessor()

    problematicInputs := []string{
        "CVE-2022-12345",
        "invalid-format",
        "CVE-1960-12345",  // 年份过早
        "CVE-2099-12345",  // 年份过晚
    }

    recoveryResult := recoverable.ProcessWithRecovery(problematicInputs)
    fmt.Printf("恢复处理结果: %d 有效, %d 无效\n",
        recoveryResult.ValidCount, recoveryResult.InvalidCount)

    failureReport := recoverable.GetFailureReport()
    fmt.Printf("失败报告: %v\n", failureReport)

    // 4. 性能优化示例
    fmt.Println("\n4. 性能优化示例")
    optimizer := NewPerformanceOptimizer()

    // 测试缓存效果
    testData := []string{"CVE-2022-12345", "CVE-2021-44228", "CVE-2023-1234"}

    // 第一次验证(缓存未命中)
    for _, input := range testData {
        optimizer.OptimizedValidate(input, validator)
    }

    // 第二次验证(缓存命中)
    for _, input := range testData {
        optimizer.OptimizedValidate(input, validator)
    }

    cacheStats := optimizer.GetCacheStats()
    fmt.Printf("缓存统计: %v\n", cacheStats)

    // 5. 性能基准测试
    fmt.Println("\n5. 性能基准测试")
    benchmarkData := []string{
        "CVE-2022-12345", "CVE-2021-44228", "CVE-2023-1234",
        "CVE-2020-1111", "CVE-2024-5678",
    }

    BenchmarkValidator(validator, benchmarkData, 1000)

    fmt.Println("\n=== 验证处理系统演示完成 ===")
}

Web API 集成示例

go
// HTTP 处理器示例
func handleCVEValidation(w http.ResponseWriter, r *http.Request) {
    validator := NewCVEValidator()
    validator.SetStrictMode(true)

    cveInput := r.URL.Query().Get("cve")
    if cveInput == "" {
        http.Error(w, "缺少 CVE 参数", http.StatusBadRequest)
        return
    }

    result := validator.ValidateSingle(cveInput)

    w.Header().Set("Content-Type", "application/json")
    if result.IsValid {
        w.WriteHeader(http.StatusOK)
    } else {
        w.WriteHeader(http.StatusBadRequest)
    }

    json.NewEncoder(w).Encode(result)
}

// 批量验证 API
func handleBatchValidation(w http.ResponseWriter, r *http.Request) {
    var request struct {
        CVEs []string `json:"cves"`
    }

    if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
        http.Error(w, "无效的 JSON 格式", http.StatusBadRequest)
        return
    }

    validator := NewCVEValidator()
    processor := NewBatchProcessor(validator)

    result := processor.ValidateBatch(request.CVEs)

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(result)
}

最佳实践

  1. 分层验证: 先进行基本格式检查,再进行详细验证
  2. 缓存优化: 对重复验证的 CVE 使用缓存
  3. 错误恢复: 实现重试机制和优雅降级
  4. 性能监控: 监控验证性能和错误率
  5. 用户友好: 提供清晰的错误信息和修复建议

这个示例展示了如何构建一个健壮、高性能的 CVE 验证系统,适用于各种生产环境。

基于 MIT 许可证发布。