@@ -7,21 +7,45 @@ import (
7
7
)
8
8
9
9
// WFN (Well-Formed Name) 表示CPE的规范化内部表示
10
+ // WFN是CPE的内部规范表示形式,用于存储CPE各个组成部分的值
11
+ // 一个完整的WFN包含以下11个属性:part, vendor, product, version, update, edition, language,
12
+ // softwareEdition, targetSoftware, targetHardware和other
10
13
type WFN struct {
11
- Part string
12
- Vendor string
13
- Product string
14
- Version string
15
- Update string
16
- Edition string
17
- Language string
18
- SoftwareEdition string
19
- TargetSoftware string
20
- TargetHardware string
21
- Other string
14
+ Part string // 组件类型: a(应用程序)、o(操作系统)、h(硬件设备)
15
+ Vendor string // 厂商名称
16
+ Product string // 产品名称
17
+ Version string // 版本号
18
+ Update string // 更新版本
19
+ Edition string // 版本
20
+ Language string // 语言
21
+ SoftwareEdition string // 软件版本
22
+ TargetSoftware string // 目标软件
23
+ TargetHardware string // 目标硬件
24
+ Other string // 其他信息
22
25
}
23
26
24
27
// FromCPE 从CPE结构体创建WFN
28
+ // 本方法将CPE结构体转换为规范化的WFN格式,用于内部处理和比较
29
+ //
30
+ // 参数:
31
+ // - cpe: CPE结构体指针,包含各个属性的值
32
+ //
33
+ // 返回值:
34
+ // - *WFN: 转换后的WFN结构体指针
35
+ //
36
+ // 示例:
37
+ //
38
+ // cpe := &CPE{
39
+ // Part: *PartApplication,
40
+ // Vendor: "microsoft",
41
+ // ProductName: "windows",
42
+ // Version: "10",
43
+ // }
44
+ // wfn := FromCPE(cpe)
45
+ // fmt.Println(wfn.Part) // 输出: "a"
46
+ // fmt.Println(wfn.Vendor) // 输出: "microsoft"
47
+ // fmt.Println(wfn.Product) // 输出: "windows"
48
+ // fmt.Println(wfn.Version) // 输出: "10"
25
49
func FromCPE (cpe * CPE ) * WFN {
26
50
return & WFN {
27
51
Part : cpe .Part .ShortName ,
@@ -39,6 +63,27 @@ func FromCPE(cpe *CPE) *WFN {
39
63
}
40
64
41
65
// ToCPE 转换WFN为CPE结构体
66
+ // 本方法将WFN转换回CPE结构体,用于外部使用和展示
67
+ //
68
+ // 返回值:
69
+ // - *CPE: 转换后的CPE结构体指针,包含从WFN提取的所有属性值
70
+ //
71
+ // 示例:
72
+ //
73
+ // wfn := &WFN{
74
+ // Part: "a",
75
+ // Vendor: "microsoft",
76
+ // Product: "windows",
77
+ // Version: "10",
78
+ // }
79
+ // cpe := wfn.ToCPE()
80
+ // fmt.Println(cpe.Part.Name) // 输出: "Application"
81
+ // fmt.Println(string(cpe.Vendor)) // 输出: "microsoft"
82
+ // fmt.Println(cpe.Cpe23) // 输出CPE 2.3格式字符串
83
+ //
84
+ // 注意:
85
+ // - 方法会自动设置CPE.Cpe23字段,生成CPE 2.3格式字符串
86
+ // - 如果WFN.Part不是有效值(a/h/o),默认为应用程序(a)
42
87
func (w * WFN ) ToCPE () * CPE {
43
88
cpe := & CPE {
44
89
Vendor : Vendor (w .Vendor ),
@@ -72,6 +117,32 @@ func (w *WFN) ToCPE() *CPE {
72
117
}
73
118
74
119
// FromCPE23String 从CPE 2.3格式字符串创建WFN
120
+ // 本方法解析CPE 2.3格式的字符串,将其转换为WFN结构体
121
+ //
122
+ // 参数:
123
+ // - cpe23: 符合CPE 2.3规范的字符串,例如"cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*"
124
+ //
125
+ // 返回值:
126
+ // - *WFN: 解析成功返回WFN结构体指针
127
+ // - error: 解析失败返回错误信息
128
+ //
129
+ // 示例:
130
+ //
131
+ // wfn, err := FromCPE23String("cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*")
132
+ // if err != nil {
133
+ // panic(err)
134
+ // }
135
+ // fmt.Println(wfn.Part) // 输出: "a"
136
+ // fmt.Println(wfn.Vendor) // 输出: "microsoft"
137
+ // fmt.Println(wfn.Product) // 输出: "windows"
138
+ // fmt.Println(wfn.Version) // 输出: "10"
139
+ //
140
+ // 错误情况:
141
+ // - 如果字符串不以"cpe:2.3:"开头,返回格式错误
142
+ // - 如果字符串不包含13个部分(以冒号分隔),返回格式错误
143
+ //
144
+ // 注意:
145
+ // - 方法会自动对每个字段进行反转义处理
75
146
func FromCPE23String (cpe23 string ) (* WFN , error ) {
76
147
// 移除cpe:2.3:前缀
77
148
if ! strings .HasPrefix (cpe23 , "cpe:2.3:" ) {
@@ -101,13 +172,59 @@ func FromCPE23String(cpe23 string) (*WFN, error) {
101
172
}
102
173
103
174
// FromCPE22String 从CPE 2.2格式字符串创建WFN
175
+ // 本方法解析CPE 2.2格式的字符串,将其转换为WFN结构体
176
+ // 内部实现是先将CPE 2.2转换为CPE 2.3格式,再调用FromCPE23String方法
177
+ //
178
+ // 参数:
179
+ // - cpe22: 符合CPE 2.2规范的字符串,例如"cpe:/a:microsoft:windows:10"
180
+ //
181
+ // 返回值:
182
+ // - *WFN: 解析成功返回WFN结构体指针
183
+ // - error: 解析失败返回错误信息
184
+ //
185
+ // 示例:
186
+ //
187
+ // wfn, err := FromCPE22String("cpe:/a:microsoft:windows:10")
188
+ // if err != nil {
189
+ // panic(err)
190
+ // }
191
+ // fmt.Println(wfn.Part) // 输出: "a"
192
+ // fmt.Println(wfn.Vendor) // 输出: "microsoft"
193
+ // fmt.Println(wfn.Product) // 输出: "windows"
194
+ // fmt.Println(wfn.Version) // 输出: "10"
195
+ //
196
+ // 错误情况:
197
+ // - 如果转换后的CPE 2.3格式字符串无效,会返回FromCPE23String传递的错误
198
+ //
199
+ // 注意:
200
+ // - 该方法依赖convertCpe22ToCpe23函数,该函数将CPE 2.2格式转换为CPE 2.3格式
104
201
func FromCPE22String (cpe22 string ) (* WFN , error ) {
105
202
// 转换成CPE 2.3格式,再解析
106
203
cpe23 := convertCpe22ToCpe23 (cpe22 )
107
204
return FromCPE23String (cpe23 )
108
205
}
109
206
110
207
// ToCPE23String 转换WFN为CPE 2.3格式字符串
208
+ // 本方法将WFN结构体转换为标准的CPE 2.3格式字符串
209
+ //
210
+ // 返回值:
211
+ // - string: 符合CPE 2.3规范的字符串,例如"cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*"
212
+ //
213
+ // 示例:
214
+ //
215
+ // wfn := &WFN{
216
+ // Part: "a",
217
+ // Vendor: "microsoft",
218
+ // Product: "windows",
219
+ // Version: "10",
220
+ // }
221
+ // cpe23 := wfn.ToCPE23String()
222
+ // fmt.Println(cpe23) // 输出: "cpe:2.3:a:microsoft:windows:10:*:*:*:*:*:*:*"
223
+ //
224
+ // 注意:
225
+ // - 方法会自动对每个字段进行转义处理,使用escapeValue函数
226
+ // - 所有字段之间用冒号(:)分隔,共有13部分
227
+ // - 返回的字符串始终以"cpe:2.3:"开头
111
228
func (w * WFN ) ToCPE23String () string {
112
229
parts := []string {
113
230
"cpe" , "2.3" ,
@@ -128,6 +245,31 @@ func (w *WFN) ToCPE23String() string {
128
245
}
129
246
130
247
// ToCPE22String 转换WFN为CPE 2.2格式字符串
248
+ // 本方法将WFN结构体转换为标准的CPE 2.2格式字符串
249
+ //
250
+ // 返回值:
251
+ // - string: 符合CPE 2.2规范的字符串,例如"cpe:/a:microsoft:windows:10"
252
+ //
253
+ // 示例:
254
+ //
255
+ // wfn := &WFN{
256
+ // Part: "a",
257
+ // Vendor: "microsoft",
258
+ // Product: "windows",
259
+ // Version: "10",
260
+ // Update: "sp1",
261
+ // Edition: "pro",
262
+ // Language: "zh-cn",
263
+ // }
264
+ // cpe22 := wfn.ToCPE22String()
265
+ // fmt.Println(cpe22) // 输出: "cpe:/a:microsoft:windows:10:sp1:pro~zh-cn"
266
+ //
267
+ // 注意:
268
+ // - CPE 2.2格式与CPE 2.3格式不完全兼容,部分字段可能无法完整表示
269
+ // - 主要部分(Part, Vendor, Product, Version, Update)用冒号分隔
270
+ // - 扩展属性(如Edition等)使用波浪线(~)分隔
271
+ // - 方法会自动移除末尾空值的扩展属性
272
+ // - 使用escapeValueForCpe22函数进行转义,转义规则与CPE 2.3不同
131
273
func (w * WFN ) ToCPE22String () string {
132
274
cpePrefix := "cpe:/"
133
275
mainParts := []string {
@@ -173,6 +315,31 @@ func (w *WFN) ToCPE22String() string {
173
315
}
174
316
175
317
// escapeValue 对CPE 2.3格式的值进行转义
318
+ // 本函数处理CPE 2.3格式中特殊字符的转义
319
+ //
320
+ // 参数:
321
+ // - value: 需要转义的原始字符串值
322
+ //
323
+ // 返回值:
324
+ // - string: 转义后的字符串
325
+ //
326
+ // 示例:
327
+ //
328
+ // escaped := escapeValue("windows.server")
329
+ // fmt.Println(escaped) // 输出: "windows\.server"
330
+ //
331
+ // // 版本号中的点号不进行转义
332
+ // escaped = escapeValue("2.0.1")
333
+ // fmt.Println(escaped) // 输出: "2.0.1"
334
+ //
335
+ // 转义规则:
336
+ // - 特殊值 "*"(ANY), "-"(NA) 和空字符串保持不变
337
+ // - 点号(.)会被转义为"\.",除非出现在符合版本格式的字符串中
338
+ // - 冒号(:)会被转义为"\:"
339
+ //
340
+ // 注意:
341
+ // - 函数会识别版本号格式(如1.2.3),在这种情况下点号不做转义
342
+ // - 这是为了保持版本号的可读性和一致性
176
343
func escapeValue (value string ) string {
177
344
// 如果是特殊值或空值,不需要转义
178
345
if value == "*" || value == "-" || value == "" {
@@ -202,6 +369,29 @@ func escapeValue(value string) string {
202
369
}
203
370
204
371
// unescapeValue 对CPE 2.3格式的值进行反转义
372
+ // 本函数处理CPE 2.3格式中特殊字符的反转义,是escapeValue的逆操作
373
+ //
374
+ // 参数:
375
+ // - value: 需要反转义的字符串
376
+ //
377
+ // 返回值:
378
+ // - string: 反转义后的原始字符串
379
+ //
380
+ // 示例:
381
+ //
382
+ // original := unescapeValue("windows\\.server")
383
+ // fmt.Println(original) // 输出: "windows.server"
384
+ //
385
+ // original = unescapeValue("2\\.0\\.1")
386
+ // fmt.Println(original) // 输出: "2.0.1"
387
+ //
388
+ // 反转义规则:
389
+ // - 特殊值 "*"(ANY), "-"(NA) 和空字符串保持不变
390
+ // - 所有形如"\x"的字符序列会被替换为"x",其中x可以是任何字符
391
+ //
392
+ // 注意:
393
+ // - 使用正则表达式识别和替换所有转义序列
394
+ // - 这个函数可以处理所有通过escapeValue函数转义的字符串
205
395
func unescapeValue (value string ) string {
206
396
if value == "*" || value == "-" || value == "" {
207
397
return value
@@ -213,6 +403,32 @@ func unescapeValue(value string) string {
213
403
}
214
404
215
405
// escapeValueForCpe22 对CPE 2.2格式的值进行转义
406
+ // 本函数处理CPE 2.2格式中特殊字符的转义,其规则与CPE 2.3不同
407
+ //
408
+ // 参数:
409
+ // - value: 需要转义的原始字符串值
410
+ //
411
+ // 返回值:
412
+ // - string: 转义后的字符串,符合CPE 2.2格式要求
413
+ //
414
+ // 示例:
415
+ //
416
+ // escaped := escapeValueForCpe22("windows/server")
417
+ // fmt.Println(escaped) // 输出: "windows%2fserver"
418
+ //
419
+ // escaped = escapeValueForCpe22("demo:test")
420
+ // fmt.Println(escaped) // 输出: "demo%3atest"
421
+ //
422
+ // 转义规则:
423
+ // - 特殊值 "*"(ANY), "-"(NA) 和空字符串保持不变
424
+ // - 反斜杠(\)转义为"\\"
425
+ // - 冒号(:)转义为"%3a"
426
+ // - 斜杠(/)转义为"%2f"
427
+ // - 波浪线(~)转义为"%7e"
428
+ //
429
+ // 注意:
430
+ // - CPE 2.2使用百分号编码(percent-encoding)来表示特殊字符,而不是反斜杠转义
431
+ // - 这种格式更接近URI编码,使CPE更容易嵌入到URL中
216
432
func escapeValueForCpe22 (value string ) string {
217
433
if value == "*" || value == "-" || value == "" {
218
434
return value
@@ -230,6 +446,39 @@ func escapeValueForCpe22(value string) string {
230
446
}
231
447
232
448
// Match 比较两个WFN是否匹配
449
+ // 本方法检查当前WFN与另一个WFN是否匹配,匹配规则遵循CPE规范
450
+ //
451
+ // 参数:
452
+ // - other: 另一个WFN结构体指针,用于与当前WFN比较
453
+ //
454
+ // 返回值:
455
+ // - bool: 如果匹配返回true,否则返回false
456
+ //
457
+ // 示例:
458
+ //
459
+ // wfn1 := &WFN{
460
+ // Part: "a",
461
+ // Vendor: "microsoft",
462
+ // Product: "windows",
463
+ // Version: "*", // 任意版本
464
+ // }
465
+ //
466
+ // wfn2 := &WFN{
467
+ // Part: "a",
468
+ // Vendor: "microsoft",
469
+ // Product: "windows",
470
+ // Version: "10",
471
+ // }
472
+ //
473
+ // fmt.Println(wfn1.Match(wfn2)) // 输出: true
474
+ // fmt.Println(wfn2.Match(wfn1)) // 输出: true
475
+ //
476
+ // 匹配规则:
477
+ // - 如果两个WFN的所有属性都匹配,则这两个WFN匹配
478
+ // - 单个属性的匹配规则通过matchWFNAttribute函数定义
479
+ // - 属性为"*"表示ANY,可以匹配任何值
480
+ // - 如果两个属性都是"-"(NA),则它们匹配
481
+ // - 其他情况要求精确匹配
233
482
func (w * WFN ) Match (other * WFN ) bool {
234
483
// 检查Part
235
484
if ! matchWFNAttribute (w .Part , other .Part ) {
@@ -250,6 +499,31 @@ func (w *WFN) Match(other *WFN) bool {
250
499
}
251
500
252
501
// matchWFNAttribute 匹配WFN的单个属性
502
+ // 本函数检查两个WFN属性值是否匹配,遵循CPE规范的匹配规则
503
+ //
504
+ // 参数:
505
+ // - a: 第一个属性值
506
+ // - b: 第二个属性值
507
+ //
508
+ // 返回值:
509
+ // - bool: 如果匹配返回true,否则返回false
510
+ //
511
+ // 示例:
512
+ //
513
+ // // ANY匹配任何值
514
+ // fmt.Println(matchWFNAttribute("*", "windows")) // 输出: true
515
+ //
516
+ // // NA匹配NA
517
+ // fmt.Println(matchWFNAttribute("-", "-")) // 输出: true
518
+ //
519
+ // // 精确匹配
520
+ // fmt.Println(matchWFNAttribute("windows", "windows")) // 输出: true
521
+ // fmt.Println(matchWFNAttribute("windows", "linux")) // 输出: false
522
+ //
523
+ // 匹配规则:
524
+ // - 如果任一属性为"*"(ANY),则匹配
525
+ // - 如果两个属性都是"-"(NA),则匹配
526
+ // - 其他情况要求精确匹配(区分大小写)
253
527
func matchWFNAttribute (a , b string ) bool {
254
528
// 如果有一个是ANY (*), 则匹配
255
529
if a == "*" || b == "*" {
0 commit comments