高级匹配
本示例演示 CPE 库中的高级匹配技术,包括模糊匹配、语义匹配和复杂的匹配策略。
概述
高级匹配超越了基本的字符串比较,提供了智能匹配算法,可以处理版本范围、同义词、模糊匹配和复杂的匹配条件。
下图勾勒了高级匹配管线:输入一对 CPE,字段级匹配选项控制每个字段是忽略、精确匹配还是通配,然后选择匹配模式,计算匹配得分或距离,最终输出带评分的匹配结果。
完整示例
package main
import (
"fmt"
"log"
"github.com/scagogogo/cpe-skills"
)
func main() {
fmt.Println("=== CPE 高级匹配示例 ===")
// 示例 1:基本匹配(CPE.Match / MatchCPE)
fmt.Println("\n1. 基本匹配:")
// 通配符模式匹配任意版本的 Windows。
patternCPE, err := cpeskills.ParseCpe23("cpe:2.3:a:microsoft:windows:*:*:*:*:*:*:*:*")
if err != nil {
log.Fatal(err)
}
targetCPE, err := cpeskills.ParseCpe23("cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*")
if err != nil {
log.Fatal(err)
}
// 方法形式:pattern.Match(target) 遵循 CPE 名称匹配规范,
// 任一字段中的 "*" 可匹配对应字段的任意值。
fmt.Printf("模式 %s 匹配 %s: %v\n",
patternCPE.GetURI(), targetCPE.GetURI(), patternCPE.Match(targetCPE))
// 使用 MatchOptions 的函数形式。此处 IgnoreVersion 使版本字段在比较时被跳过。
opts := cpeskills.DefaultMatchOptions()
opts.IgnoreVersion = true
fmt.Printf("MatchCPE (IgnoreVersion=true): %v\n",
cpeskills.MatchCPE(patternCPE, targetCPE, opts))
// 示例 2:高级匹配模式(exact / subset / superset / distance)
fmt.Println("\n2. 高级匹配模式:")
// criteria 是更通用的模式,target 是具体的 CPE。
criteria, _ := cpeskills.ParseCpe23("cpe:2.3:a:microsoft:windows:*:*:*:*:*:*:*:*")
specific, _ := cpeskills.ParseCpe23("cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*")
modes := []struct {
name string
mode string
}{
{"exact", "exact"},
{"subset", "subset"}, // criteria 是 target 的子集(更具体)
{"superset", "superset"}, // criteria 是 target 的超集(更宽泛)
{"distance", "distance"}, // 基于距离/相似度的匹配
}
for _, m := range modes {
o := cpeskills.NewAdvancedMatchOptions()
o.MatchMode = m.mode
result := cpeskills.AdvancedMatchCPE(criteria, specific, o)
fmt.Printf(" mode=%-8s -> %v\n", m.name, result)
}
// 示例 3:正则、忽略大小写与部分匹配选项
fmt.Println("\n3. 正则 / 忽略大小写 / 部分匹配选项:")
// UseRegex 将 vendor/product 字段当作正则表达式处理。
// IgnoreCase 使这些正则匹配不区分大小写。
regexCriteria, _ := cpeskills.ParseCpe23("cpe:2.3:a:.*soft.*:.*:1.*:*:*:*:*:*:*")
regexTarget, _ := cpeskills.ParseCpe23("cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*")
regexOpts := cpeskills.NewAdvancedMatchOptions()
regexOpts.MatchMode = "exact"
regexOpts.UseRegex = true
regexOpts.IgnoreCase = true
fmt.Printf("UseRegex+IgnoreCase 匹配: %v\n",
cpeskills.AdvancedMatchCPE(regexCriteria, regexTarget, regexOpts))
// PartialMatch 允许对字符串字段进行子串匹配。
partialCriteria, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*")
partialTarget, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat_server:9.0.0:*:*:*:*:*:*:*")
partialOpts := cpeskills.NewAdvancedMatchOptions()
partialOpts.MatchMode = "exact"
partialOpts.PartialMatch = true
fmt.Printf("PartialMatch (tomcat 在 tomcat_server 中): %v\n",
cpeskills.AdvancedMatchCPE(partialCriteria, partialTarget, partialOpts))
// 示例 4:FieldOptions(按字段配置权重与是否必需)
fmt.Println("\n4. FieldOptions (权重 / 必需):")
// 配置按字段的行为。"vendor" 和 "product" 必需且权重更高;
// "version" 可选,权重较低。
fieldCriteria, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:*:*:*:*:*:*:*:*")
fieldTarget, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*")
fieldOpts := cpeskills.NewAdvancedMatchOptions()
fieldOpts.MatchMode = "exact"
fieldOpts.FieldOptions = map[string]cpeskills.FieldMatchOption{
"vendor": {Weight: 0.3, Required: true, MatchMethod: "exact"},
"product": {Weight: 0.4, Required: true, MatchMethod: "exact"},
"version": {Weight: 0.2, Required: false, MatchMethod: "exact"},
}
fmt.Printf("FieldOptions 匹配: %v\n",
cpeskills.AdvancedMatchCPE(fieldCriteria, fieldTarget, fieldOpts))
// 示例 5:ScoreThreshold
fmt.Println("\n5. ScoreThreshold:")
// 降低 ScoreThreshold 会放宽 distance 匹配,使近似匹配通过。
distCriteria, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*")
distTarget, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:9.0.1:*:*:*:*:*:*:*")
strictOpts := cpeskills.NewAdvancedMatchOptions()
strictOpts.MatchMode = "distance"
strictOpts.ScoreThreshold = 0.9
fmt.Printf("ScoreThreshold=0.9: %v\n",
cpeskills.AdvancedMatchCPE(distCriteria, distTarget, strictOpts))
looseOpts := cpeskills.NewAdvancedMatchOptions()
looseOpts.MatchMode = "distance"
looseOpts.ScoreThreshold = 0.5
fmt.Printf("ScoreThreshold=0.5: %v\n",
cpeskills.AdvancedMatchCPE(distCriteria, distTarget, looseOpts))
// 示例 6:版本比较(CompareVersions + 类型转换)
fmt.Println("\n6. 版本比较:")
// CPE 字段类型(Vendor/Product/Version)是 `type X string` 别名,
// 在传给 strings.Contains 或 CompareVersions 等通用 string 辅助函数前,
// 必须用 string() 进行转换。
v1 := targetCPE.Version
fmt.Printf("CompareVersions(%q, %q) = %d\n",
string(v1), "10", cpeskills.CompareVersions(string(v1), "10"))
fmt.Printf("IsVersionInRange(%q, %q, %q) = %v\n",
string(v1), "1.0", "99.0",
cpeskills.IsVersionInRange(string(v1), "1.0", "99.0"))
// 示例 7:通过 AdvancedMatchOptions 进行版本范围匹配
fmt.Println("\n7. 版本范围匹配:")
// VersionCompareMode 配合 VersionLower / VersionUpper,
// 在 AdvancedMatchCPE 内部执行基于范围的版本匹配。
rangeCriteria, _ := cpeskills.ParseCpe23("cpe:2.3:a:oracle:java:*:*:*:*:*:*:*:*")
rangeTarget, _ := cpeskills.ParseCpe23("cpe:2.3:a:oracle:java:8.0.291:*:*:*:*:*:*:*")
rangeOpts := cpeskills.NewAdvancedMatchOptions()
rangeOpts.MatchMode = "exact"
rangeOpts.VersionCompareMode = "range"
rangeOpts.VersionLower = "8.0.0"
rangeOpts.VersionUpper = "8.0.999"
fmt.Printf("Java 8.0.291 在 [8.0.0, 8.0.999] 内: %v\n",
cpeskills.AdvancedMatchCPE(rangeCriteria, rangeTarget, rangeOpts))
}// 示例1:模糊匹配
fmt.Println("\n1. 模糊匹配:")
// 创建目标CPE
targetCPE, _ := cpeskills.ParseCpe23("cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*")
// 创建候选CPE(包含一些变体)
candidateCPEs := []string{
"cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*", // 精确匹配
"cpe:2.3:a:microsoft:win:10:*:*:*:*:*:*:*", // 产品名缩写
"cpe:2.3:a:microsoft:windows:10.0:*:*:*:*:*:*:*", // 版本变体
"cpe:2.3:a:microsoft:windows_10:10:*:*:*:*:*:*:*", // 产品名变体
"cpe:2.3:a:ms:windows:10:*:*:*:*:*:*:*", // 供应商缩写
"cpe:2.3:a:oracle:java:11:*:*:*:*:*:*:*", // 完全不同
}
fmt.Printf("目标CPE: %s\n", targetCPE.GetURI())
fmt.Println("模糊匹配结果:")
for i, candidateStr := range candidateCPEs {
candidateCPE, _ := cpeskills.ParseCpe23(candidateStr)
// 计算相似度分数
similarity := calculateSimilarity(targetCPE, candidateCPE)
status := "❌"
if similarity >= 0.7 { // 70%阈值
status = "✅"
}
fmt.Printf(" %s %d. 相似度: %.2f - %s\n",
status, i+1, similarity, candidateStr)
}
// 示例2:版本范围匹配
fmt.Println("\n2. 版本范围匹配:")
// 定义漏洞影响的版本范围
vulnerableRanges := []struct {
product string
minVersion string
maxVersion string
cveID string
}{
{"tomcat", "8.5.0", "8.5.4", "CVE-2021-25122"},
{"java", "1.8.0", "1.8.0_291", "CVE-2021-2163"},
{"nginx", "1.0.0", "1.18.0", "CVE-2021-23017"},
}
// 测试系统中的软件
systemSoftware := []string{
"cpe:2.3:a:apache:tomcat:8.5.3:*:*:*:*:*:*:*",
"cpe:2.3:a:apache:tomcat:8.5.5:*:*:*:*:*:*:*",
"cpe:2.3:a:oracle:java:1.8.0_281:*:*:*:*:*:*:*",
"cpe:2.3:a:oracle:java:1.8.0_301:*:*:*:*:*:*:*",
"cpe:2.3:a:nginx:nginx:1.16.1:*:*:*:*:*:*:*",
"cpe:2.3:a:nginx:nginx:1.20.0:*:*:*:*:*:*:*",
}
fmt.Println("漏洞范围匹配:")
for _, softwareStr := range systemSoftware {
softwareCPE, _ := cpeskills.ParseCpe23(softwareStr)
fmt.Printf("\n检查: %s %s\n", softwareCPE.ProductName, softwareCPE.Version)
vulnerabilityFound := false
for _, vulnRange := range vulnerableRanges {
if softwareCPE.ProductName == vulnRange.product {
if isVersionInRange(softwareCPE.Version, vulnRange.minVersion, vulnRange.maxVersion) {
fmt.Printf(" ⚠️ 易受攻击: %s (版本 %s - %s)\n",
vulnRange.cveID, vulnRange.minVersion, vulnRange.maxVersion)
vulnerabilityFound = true
}
}
}
if !vulnerabilityFound {
fmt.Printf(" ✅ 未发现已知漏洞\n")
}
}
// 示例3:语义匹配
fmt.Println("\n3. 语义匹配:")
// 定义同义词映射
synonyms := map[string][]string{
"microsoft": {"ms", "msft"},
"windows": {"win", "windows_nt"},
"internet_explorer": {"ie", "iexplore"},
"apache": {"apache_software_foundation", "asf"},
"tomcat": {"apache_tomcat", "catalina"},
}
// 测试语义匹配
semanticTests := []struct {
pattern string
target string
}{
{"microsoft", "ms"},
{"windows", "win"},
{"internet_explorer", "ie"},
{"apache", "asf"},
{"tomcat", "catalina"},
}
fmt.Println("语义匹配测试:")
for i, test := range semanticTests {
matches := semanticMatch(test.pattern, test.target, synonyms)
status := "❌"
if matches {
status = "✅"
}
fmt.Printf(" %s %d. '%s' 匹配 '%s': %t\n",
status, i+1, test.pattern, test.target, matches)
}
// 示例4:复杂匹配条件
fmt.Println("\n4. 复杂匹配条件:")
// 定义复杂的匹配规则
type MatchRule struct {
Name string
Description string
Condition func(*cpeskills.CPE) bool
}
rules := []MatchRule{
{
"Web服务器",
"Apache HTTP Server或Nginx",
func(c *cpeskills.CPE) bool {
return (c.Vendor == "apache" && c.ProductName == "http_server") ||
(c.Vendor == "nginx" && c.ProductName == "nginx")
},
},
{
"Microsoft产品",
"任何Microsoft产品",
func(c *cpeskills.CPE) bool {
return c.Vendor == "microsoft"
},
},
{
"过时的Java",
"Java版本低于11",
func(c *cpeskills.CPE) bool {
if c.Vendor == "oracle" && c.ProductName == "java" {
return isVersionLessThan(c.Version, "11.0.0")
}
return false
},
},
{
"关键基础设施",
"操作系统或网络设备",
func(c *cpeskills.CPE) bool {
return c.Part.ShortName == "o" || c.Part.ShortName == "h"
},
},
}
// 测试软件清单
inventory := []string{
"cpe:2.3:a:apache:http_server:2.4.41:*:*:*:*:*:*:*",
"cpe:2.3:a:nginx:nginx:1.18.0:*:*:*:*:*:*:*",
"cpe:2.3:a:microsoft:office:2019:*:*:*:*:*:*:*",
"cpe:2.3:a:oracle:java:1.8.0_291:*:*:*:*:*:*:*",
"cpe:2.3:a:oracle:java:11.0.12:*:*:*:*:*:*:*",
"cpe:2.3:o:microsoft:windows:10:*:*:*:*:*:*:*",
"cpe:2.3:h:cisco:catalyst_2960:*:*:*:*:*:*:*:*",
}
fmt.Println("复杂匹配规则应用:")
for _, rule := range rules {
fmt.Printf("\n规则: %s (%s)\n", rule.Name, rule.Description)
matchCount := 0
for _, itemStr := range inventory {
itemCPE, _ := cpeskills.ParseCpe23(itemStr)
if rule.Condition(itemCPE) {
fmt.Printf(" ✅ %s %s %s\n",
itemCPE.Vendor, itemCPE.ProductName, itemCPE.Version)
matchCount++
}
}
fmt.Printf(" 匹配项: %d/%d\n", matchCount, len(inventory))
}
// 示例5:基于权重的匹配
fmt.Println("\n5. 基于权重的匹配:")
// 定义字段权重
fieldWeights := map[string]float64{
"vendor": 0.3,
"product": 0.4,
"version": 0.2,
"part": 0.1,
}
referenceCPE, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*")
testCPEs := []string{
"cpe:2.3:a:apache:tomcat:9.0.0:*:*:*:*:*:*:*", // 完全匹配
"cpe:2.3:a:apache:tomcat:9.0.1:*:*:*:*:*:*:*", // 版本不同
"cpe:2.3:a:apache:http_server:2.4.41:*:*:*:*:*:*:*", // 产品不同
"cpe:2.3:a:nginx:nginx:1.18.0:*:*:*:*:*:*:*", // 供应商和产品不同
"cpe:2.3:o:apache:tomcat:9.0.0:*:*:*:*:*:*:*", // 部件类型不同
}
fmt.Printf("参考CPE: %s\n", referenceCPE.GetURI())
fmt.Println("加权匹配分数:")
for i, testStr := range testCPEs {
testCPE, _ := cpeskills.ParseCpe23(testStr)
score := calculateWeightedScore(referenceCPE, testCPE, fieldWeights)
fmt.Printf(" %d. 分数: %.3f - %s\n", i+1, score, testStr)
}
// 示例6:上下文感知匹配
fmt.Println("\n6. 上下文感知匹配:")
// 定义不同的上下文
contexts := map[string]func(*cpeskills.CPE, *cpeskills.CPE) bool{
"安全扫描": func(pattern, target *cpeskills.CPE) bool {
// 在安全上下文中,版本必须精确匹配
return pattern.Vendor == target.Vendor &&
pattern.ProductName == target.ProductName &&
pattern.Version == target.Version
},
"资产清单": func(pattern, target *cpeskills.CPE) bool {
// 在清单上下文中,版本可以是通配符
return pattern.Vendor == target.Vendor &&
pattern.ProductName == target.ProductName &&
(pattern.Version == "*" || pattern.Version == target.Version)
},
"兼容性检查": func(pattern, target *cpeskills.CPE) bool {
// 在兼容性上下文中,允许次版本差异
if pattern.Vendor != target.Vendor || pattern.ProductName != target.ProductName {
return false
}
return isVersionCompatible(pattern.Version, target.Version)
},
}
patternCPE, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:9.*:*:*:*:*:*:*:*")
targetCPE, _ := cpeskills.ParseCpe23("cpe:2.3:a:apache:tomcat:9.0.1:*:*:*:*:*:*:*")
fmt.Printf("模式: %s\n", patternCPE.GetURI())
fmt.Printf("目标: %s\n", targetCPE.GetURI())
for contextName, matcher := range contexts {
matches := matcher(patternCPE, targetCPE)
status := "❌"
if matches {
status = "✅"
}
fmt.Printf(" %s %s上下文: %t\n", status, contextName, matches)
}
}
// 辅助函数:计算CPE相似度 func calculateSimilarity(cpe1, cpe2 *cpeskills.CPE) float64 { var score float64
// 供应商匹配 (权重: 30%)
if cpe1.Vendor == cpe2.Vendor {
score += 0.3
} else if strings.Contains(cpe1.Vendor, cpe2.Vendor) || strings.Contains(cpe2.Vendor, cpe1.Vendor) {
score += 0.15
}
// 产品匹配 (权重: 40%)
if cpe1.ProductName == cpe2.ProductName {
score += 0.4
} else if strings.Contains(cpe1.ProductName, cpe2.ProductName) || strings.Contains(cpe2.ProductName, cpe1.ProductName) {
score += 0.2
}
// 版本匹配 (权重: 20%)
if cpe1.Version == cpe2.Version {
score += 0.2
} else if strings.HasPrefix(cpe1.Version, cpe2.Version) || strings.HasPrefix(cpe2.Version, cpe1.Version) {
score += 0.1
}
// 部件匹配 (权重: 10%)
if cpe1.Part.ShortName == cpe2.Part.ShortName {
score += 0.1
}
return score
}
// 辅助函数:语义匹配 func semanticMatch(pattern, target string, synonyms map[string][]string) bool { if pattern == target { return true }
// 检查同义词
if syns, exists := synonyms[pattern]; exists {
for _, syn := range syns {
if syn == target {
return true
}
}
}
// 反向检查
if syns, exists := synonyms[target]; exists {
for _, syn := range syns {
if syn == pattern {
return true
}
}
}
return false
}
// 辅助函数:版本范围检查 func isVersionInRange(version, minVersion, maxVersion string) bool { return compareVersions(version, minVersion) >= 0 && compareVersions(version, maxVersion) <= 0 }
// 辅助函数:版本比较 func compareVersions(v1, v2 string) int { // 简化的版本比较实现 if v1 == v2 { return 0 } if v1 < v2 { return -1 } return 1 }
// 辅助函数:版本小于比较 func isVersionLessThan(version, threshold string) bool { return compareVersions(version, threshold) < 0 }
// 辅助函数:版本兼容性检查 func isVersionCompatible(required, available string) bool { // 简化的兼容性检查 return compareVersions(available, required) >= 0 }
// 辅助函数:计算加权分数 func calculateWeightedScore(ref, test *cpeskills.CPE, weights map[string]float64) float64 { var score float64
if ref.Vendor == test.Vendor {
score += weights["vendor"]
}
if ref.ProductName == test.ProductName {
score += weights["product"]
}
if ref.Version == test.Version {
score += weights["version"]
}
if ref.Part.ShortName == test.Part.ShortName {
score += weights["part"]
}
return score
}
## 关键概念
### 1. 匹配策略
- **精确匹配**: 所有字段必须完全相同
- **模糊匹配**: 基于相似度阈值
- **语义匹配**: 理解同义词和缩写
- **上下文匹配**: 根据使用场景调整规则
### 2. 相似度计算
- **字段权重**: 不同字段的重要性不同
- **字符串距离**: 使用编辑距离等算法
- **部分匹配**: 子字符串和前缀匹配
- **模式匹配**: 正则表达式和通配符
### 3. 版本处理
- **范围匹配**: 检查版本是否在范围内
- **兼容性**: 向后兼容性检查
- **语义版本**: 理解 major.minor.patch 结构
- **特殊格式**: 处理构建号和日期版本
## 最佳实践
1. **选择合适的匹配策略**: 根据用例选择精确或模糊匹配
2. **调整阈值**: 根据数据质量调整相似度阈值
3. **使用上下文**: 在不同场景中应用不同的匹配规则
4. **验证结果**: 始终验证匹配结果的准确性
5. **性能优化**: 对大数据集使用索引和缓存
## 性能考虑
1. **缓存**: 缓存开销较大的匹配计算结果
2. **索引**: 对大型数据集使用合适的数据结构
3. **并行处理**: 将匹配任务分布到多个 CPU 核心
4. **提前终止**: 当置信度足够时停止处理
## 下一步
- 学习[NVD 集成](./nvd-integration.md)获取实际漏洞数据
- 探索[CPE 集合](./sets.md)进行批量高级匹配
- 查看[存储](./storage.md)来持久化匹配结果