├── .travis.yml ├── LICENSE ├── README.md ├── attribute.go ├── attribute_test.go ├── encoder.go ├── matching.go ├── matching_test.go ├── naming.go └── naming_test.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 umisama 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # umisama/go-cpe [![Build Status](https://travis-ci.org/umisama/go-cpe.svg)](https://travis-ci.org/umisama/go-cpe) 2 | A Common Platform Enumeration 2.3 implementation for golang. 3 | 4 | ## installation 5 | ``` 6 | go get github.com/umisama/go-cpe 7 | ``` 8 | 9 | ## usage 10 | Simple example is here: 11 | ```go 12 | // Create new item with Setter functions. 13 | item1 := cpe.NewItem( 14 | item1.SetPart(cpe.Application) 15 | item1.SetVendor(cpe.NewStringAttr("microsoft")) 16 | item1.SetProduct(cpe.NewStringAttr("internet_explorer")) 17 | 18 | // create new item with WFN. You can use also other formats(url, formatted string and WFN). 19 | item2, err := cpe.NewItemFromWfn(`wfn:[part="a",vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta"]`) 20 | if err != nil { 21 | panic(err) 22 | } 23 | fmt.Println("Vendor :", item2.Vendor() 24 | 25 | // Compare functions 26 | fmt.Println("is relation superset between item1 and item2? : ", cpe.CheckSuperset(item1, item2)) 27 | fmt.Println("is relation equal between item1 and item2? : ", cpe.CheckEqual(item1, item2)) 28 | )) 29 | ``` 30 | 31 | ## document 32 | [godoc.org](http://godoc.org/github.com/umisama/go-cpe) 33 | 34 | 35 | ## reference 36 | * [NIST IR 7695 — Common Platform Enumeration: Naming Specification Version 2.3](http://csrc.nist.gov/publications/nistir/ir7695/NISTIR-7695-CPE-Naming.pdf) 37 | * [NIST IR 7696 — Common Platform Enumeration: Name Matching Specification Version 2.3](http://csrc.nist.gov/publications/nistir/ir7696/NISTIR-7696-CPE-Matching.pdf) 38 | 39 | ## license 40 | under the MIT License. 41 | -------------------------------------------------------------------------------- /attribute.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | // Attribute groups PartAttr and StringAttr. 9 | type Attribute interface { 10 | wfnEncoded() string 11 | urlEncoded() string 12 | fmtString() string 13 | String() string 14 | IsEmpty() bool 15 | IsValid() bool 16 | Comparison(Attribute) Relation 17 | } 18 | 19 | // PartAttr reprecents part attribute of cpe item. 20 | type PartAttr rune 21 | 22 | // StringAttr reprecents other than part attribute of cpe item. 23 | type StringAttr struct { 24 | raw string 25 | isNa bool 26 | } 27 | 28 | var ( 29 | Application = PartAttr('a') 30 | OperationgSystem = PartAttr('o') 31 | Hardware = PartAttr('h') 32 | PartNotSet = PartAttr(0x00) 33 | Any = StringAttr{} 34 | Na = StringAttr{isNa: true} 35 | ) 36 | 37 | func newPartAttrFromWfnEncoded(str string) PartAttr { 38 | if len(str) != 3 { 39 | return PartNotSet 40 | } 41 | 42 | switch PartAttr(str[1]) { 43 | case Application: 44 | return Application 45 | case OperationgSystem: 46 | return OperationgSystem 47 | case Hardware: 48 | return Hardware 49 | } 50 | return PartNotSet 51 | } 52 | 53 | func newPartAttrFromUriEncoded(str string) PartAttr { 54 | if len(str) != 1 { 55 | return PartNotSet 56 | } 57 | 58 | switch PartAttr(str[0]) { 59 | case Application: 60 | return Application 61 | case OperationgSystem: 62 | return OperationgSystem 63 | case Hardware: 64 | return Hardware 65 | } 66 | return PartNotSet 67 | } 68 | 69 | func newPartAttrFromFmtEncoded(str string) PartAttr { 70 | if len(str) != 1 { 71 | return PartNotSet 72 | } 73 | 74 | switch PartAttr(str[0]) { 75 | case Application: 76 | return Application 77 | case OperationgSystem: 78 | return OperationgSystem 79 | case Hardware: 80 | return Hardware 81 | } 82 | return PartNotSet 83 | } 84 | 85 | func (m PartAttr) String() string { 86 | if m.IsValid() { 87 | return string(m) 88 | } else { 89 | panic("\"%v\" is not valid as part attribute.") 90 | } 91 | } 92 | 93 | func (m PartAttr) wfnEncoded() string { 94 | return "\"" + m.String() + "\"" 95 | } 96 | 97 | func (m PartAttr) fmtString() string { 98 | return m.String() 99 | } 100 | 101 | func (m PartAttr) urlEncoded() string { 102 | return m.String() 103 | } 104 | 105 | func (m PartAttr) IsValid() bool { 106 | switch m { 107 | case Application, OperationgSystem, Hardware: 108 | return true 109 | default: 110 | return false 111 | } 112 | } 113 | 114 | func (m PartAttr) IsEmpty() bool { 115 | return m == PartNotSet 116 | } 117 | 118 | func (src PartAttr) Comparison(trg Attribute) Relation { 119 | trg_part, ok := trg.(PartAttr) 120 | if !ok { 121 | return Undefined 122 | } 123 | 124 | if !src.IsValid() || !trg_part.IsValid() { 125 | return Undefined 126 | } 127 | 128 | if src == trg_part { 129 | return Equal 130 | } 131 | 132 | return Disjoint 133 | } 134 | 135 | // NewStringAttr returns attribute of item with str. 136 | func NewStringAttr(str string) StringAttr { 137 | return StringAttr{ 138 | raw: str, 139 | } 140 | } 141 | 142 | func newStringAttrFromWfnEncoded(str string) StringAttr { 143 | if str == "NA" { 144 | return Na 145 | } else if str == "ANY" { 146 | return Any 147 | } 148 | return StringAttr{ 149 | raw: wfn_encoder.Decode(strings.TrimPrefix(strings.TrimSuffix(str, "\""), "\"")), 150 | } 151 | } 152 | 153 | func newStringAttrFromUriEncoded(str string) StringAttr { 154 | if str == "-" { 155 | return Na 156 | } else if str == "" || str == "*" { 157 | return Any 158 | } 159 | return StringAttr{ 160 | raw: url_encoder.Decode(str), 161 | } 162 | } 163 | 164 | func newStringAttrFromFmtEncoded(str string) StringAttr { 165 | if str == "-" { 166 | return Na 167 | } else if str == "*" { 168 | return Any 169 | } 170 | return StringAttr{ 171 | raw: fmt_encoder.Decode(str), 172 | } 173 | } 174 | 175 | func (s StringAttr) String() string { 176 | if s.isNa { 177 | return "-" 178 | } else if len(s.raw) == 0 { 179 | return "*" 180 | } 181 | 182 | return s.raw 183 | } 184 | 185 | func (s StringAttr) wfnEncoded() string { 186 | if s.isNa { 187 | return "NA" 188 | } else if len(s.raw) == 0 { 189 | return "ANY" 190 | } 191 | 192 | return "\"" + wfn_encoder.Encode(s.raw) + "\"" 193 | } 194 | 195 | func (s StringAttr) fmtString() string { 196 | if s.isNa { 197 | return "-" 198 | } else if len(s.raw) == 0 { 199 | return "*" 200 | } 201 | 202 | return fmt_encoder.Encode(s.raw) 203 | } 204 | 205 | func (s StringAttr) urlEncoded() string { 206 | if s.IsEmpty() { 207 | return "" // * 208 | } else if s.isNa { 209 | return "-" 210 | } 211 | return url_encoder.Encode(s.raw) 212 | } 213 | 214 | // Empty StringAttr means ANY. 215 | func (s StringAttr) IsEmpty() bool { 216 | return s.raw == "" && !s.isNa 217 | } 218 | 219 | var stringAttrIsValidRegExp = regexp.MustCompile("\\A(\\*|\\?+){0,1}[a-zA-Z0-9\\-_!\"#$%&'()+,./:;<=>@\\[\\]^`{}\\|~\\\\]+(\\*|\\?+){0,1}$") 220 | 221 | func (s StringAttr) IsValid() bool { 222 | if s.isNa && len(s.raw) != 0 { 223 | return false 224 | } 225 | 226 | if stringAttrIsValidRegExp.FindString(s.raw) != s.raw { 227 | return false 228 | } 229 | 230 | return true 231 | } 232 | 233 | func (src StringAttr) Comparison(trg Attribute) Relation { 234 | trg_str, ok := trg.(StringAttr) 235 | if !ok { 236 | return Undefined 237 | } 238 | 239 | if !src.IsValid() || !trg_str.IsValid() { 240 | return Undefined 241 | } 242 | 243 | if src == Any { 244 | if trg_str == Any { 245 | return Equal 246 | } else if trg_str == Na { 247 | return Superset 248 | } else if !trg_str.withWildCard() { 249 | return Superset 250 | } 251 | return Undefined 252 | } 253 | 254 | if src == Na { 255 | if trg_str == Any { 256 | return Subset 257 | } else if trg_str == Na { 258 | return Equal 259 | } else if !trg_str.withWildCard() { 260 | return Disjoint 261 | } 262 | return Undefined 263 | } 264 | 265 | if src.withWildCard() { 266 | if trg_str == Any { 267 | return Subset 268 | } else if trg_str == Na { 269 | return Disjoint 270 | } else if trg_str.withWildCard() { 271 | return Undefined 272 | } else if match_wildcard(src.raw, trg_str.raw) { 273 | return Superset 274 | } 275 | return Disjoint 276 | } else { 277 | if trg_str == Any { 278 | return Subset 279 | } else if trg_str == Na { 280 | return Disjoint 281 | } else if trg_str.withWildCard() { 282 | return Undefined 283 | } else if trg_str.raw == src.raw { 284 | return Equal 285 | } 286 | return Disjoint 287 | } 288 | 289 | return Undefined 290 | } 291 | 292 | func (m StringAttr) withWildCard() bool { 293 | prefix, suffix := m.raw[0], m.raw[len(m.raw)-1] 294 | return prefix == '*' || prefix == '?' || suffix == '*' || suffix == '?' 295 | } 296 | 297 | func match_wildcard(src, trg string) bool { 298 | sufw, sufq, prew, preq := 0, 0, 0, 0 299 | if strings.HasPrefix(src, "?") { 300 | before := len(src) 301 | src = strings.TrimLeft(src, "?") 302 | preq = before - len(src) 303 | } 304 | if strings.HasPrefix(src, "*") { 305 | src = strings.TrimPrefix(src, "*") 306 | prew = 1 307 | } 308 | if strings.HasSuffix(src, "?") { 309 | before := len(src) 310 | src = strings.TrimRight(src, "?") 311 | sufq = before - len(src) 312 | } 313 | if strings.HasSuffix(src, "*") { 314 | src = strings.TrimSuffix(src, "*") 315 | sufw = 1 316 | } 317 | 318 | i := strings.Index(trg, src) 319 | if prew != 0 { 320 | if i != len(trg)-len(src)-sufq && sufw == 0 { 321 | return false 322 | } 323 | } 324 | if sufw != 0 { 325 | if i != preq && prew == 0 { 326 | return false 327 | } 328 | } 329 | if preq != 0 { 330 | if i != preq || (i != len(trg)-len(src)-sufq && sufw == 0) { 331 | return false 332 | } 333 | } 334 | if sufq != 0 { 335 | if i != len(trg)-sufq-len(src) || (i != preq && prew == 0) { 336 | return false 337 | } 338 | } 339 | 340 | return true 341 | } 342 | -------------------------------------------------------------------------------- /attribute_test.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestNewStringAttr(t *testing.T) { 9 | type testcase struct { 10 | input string 11 | valid bool 12 | } 13 | var cases = []testcase{ 14 | {"microsoft", true}, 15 | {"microsoft&google", true}, 16 | {"??crosoft", true}, 17 | {"microso??", true}, 18 | {"マイクロソフト", false}, 19 | {"microsoft&グーグル", false}, 20 | {"**crosoft", false}, 21 | {"microso**", false}, 22 | {"mic**roso", false}, 23 | // {"-microsoft", false}, // FIXME:this case must check to invalid 24 | } 25 | 26 | for i, c := range cases { 27 | sa := NewStringAttr(c.input) 28 | assert.Equal(t, c.valid, sa.IsValid(), "%d", i) 29 | } 30 | } 31 | 32 | func TestWFNEncoded(t *testing.T) { 33 | type testcase struct { 34 | input string 35 | expect string 36 | } 37 | var cases = []testcase{ 38 | {"foo-bar", "\"foo\\-bar\""}, 39 | {"Acrobat_Reader", "\"Acrobat_Reader\""}, 40 | {"\"oh_my!\"", "\"\\\"oh_my\\!\\\"\""}, 41 | {"g++", "\"g\\+\\+\""}, 42 | {"g.?", "\"g\\.?\""}, 43 | {"sr*", "\"sr*\""}, 44 | {"big$money", "\"big\\$money\""}, 45 | {"foo:bar", "\"foo\\:bar\""}, 46 | {"with_quoted~tilde", "\"with_quoted\\~tilde\""}, 47 | {"*SOFT*", "\"*SOFT*\""}, 48 | {"8.??", "\"8\\.??\""}, 49 | {"*8.??", "\"*8\\.??\""}, 50 | } 51 | 52 | for i, c := range cases { 53 | sa := NewStringAttr(c.input) 54 | assert.Equal(t, c.expect, sa.wfnEncoded(), "%d", i) 55 | } 56 | } 57 | 58 | func TestNewStringAttrFromWcnEncoded(t *testing.T) { 59 | type testcase struct { 60 | input string 61 | expect string 62 | } 63 | var cases = []testcase{ 64 | {"\"foo\\-bar\"", "foo-bar"}, 65 | {"\"Acrobat_Reader\"", "Acrobat_Reader"}, 66 | {"\"\\\"oh_my\\!\\\"\"", "\"oh_my!\""}, 67 | {"\"g\\+\\+\"", "g++"}, 68 | {"\"g\\.?\"", "g.?"}, 69 | {"\"sr*\"", "sr*"}, 70 | {"\"big\\$money\"", "big$money"}, 71 | {"\"foo\\:bar\"", "foo:bar"}, 72 | {"\"with_quoted\\~tilde\"", "with_quoted~tilde"}, 73 | {"\"*SOFT*\"", "*SOFT*"}, 74 | {"\"8\\.??\"", "8.??"}, 75 | {"\"*8\\.??\"", "*8.??"}, 76 | } 77 | 78 | for i, c := range cases { 79 | sa := newStringAttrFromWfnEncoded(c.input) 80 | assert.Equal(t, c.expect, sa.raw, "%d", i) 81 | } 82 | } 83 | 84 | func TestNewPartAttrFromWcnEncoded(t *testing.T) { 85 | type testcase struct { 86 | input string 87 | expect PartAttr 88 | } 89 | var cases = []testcase{ 90 | {`"a"`, Application}, 91 | {`"o"`, OperationgSystem}, 92 | {`"h"`, Hardware}, 93 | {`"z"`, PartNotSet}, 94 | } 95 | 96 | for i, c := range cases { 97 | pa := newPartAttrFromWfnEncoded(c.input) 98 | assert.Equal(t, c.expect, pa, "%d", i) 99 | } 100 | } 101 | 102 | func TestMatchWildcard(t *testing.T) { 103 | type testcase struct { 104 | input string 105 | value string 106 | expect bool 107 | } 108 | var cases = []testcase{ 109 | {"*123", "11123", true}, 110 | {"*123", "11123a", false}, 111 | {"123*", "12311", true}, 112 | {"123*", "112311", false}, 113 | {"??123", "11123", true}, 114 | {"??123", "1123", false}, 115 | {"??123", "11123a", false}, 116 | {"123??", "12311", true}, 117 | {"123??", "123111", false}, 118 | {"123??", "112311", false}, 119 | {"*123?", "111233", true}, 120 | {"*123*", "11123111", true}, 121 | {"?123?", "11231", true}, 122 | {"?123*", "112333", true}, 123 | {"??123*", "1112333", true}, 124 | {"*123??", "1112335", true}, 125 | {"*123??", "11123355", false}, 126 | {"??123*", "18112333", false}, 127 | } 128 | 129 | for i, c := range cases { 130 | assert.Equal(t, c.expect, match_wildcard(c.input, c.value), "%d", i) 131 | } 132 | } 133 | 134 | func testComparition(t *testing.T) { 135 | type testcase struct { 136 | input Attribute 137 | value Attribute 138 | expect Relation 139 | } 140 | var cases = []testcase{ 141 | {Application, Application, Equal}, 142 | {NewStringAttr("Adobe"), Any, Subset}, 143 | {Any, NewStringAttr("Reader"), Superset}, 144 | {NewStringAttr("9.*"), NewStringAttr("9.3.2"), Superset}, 145 | {Any, Na, Superset}, 146 | {NewStringAttr("PalmOS"), Na, Disjoint}, 147 | } 148 | 149 | for i, c := range cases { 150 | assert.Equal(t, c.expect, c.input.Comparison(c.value), "%d", i) 151 | } 152 | } 153 | 154 | func BenchmarkStringAttrComparison(b *testing.B) { 155 | sa1 := NewStringAttr("hellohellohellohello") 156 | sa2 := NewStringAttr("worldworldworldworld") 157 | for i := 0; i < b.N; i++ { 158 | sa1.Comparison(sa2) 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /encoder.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | type charEncoder []encodeTable 8 | type encodeTable struct { 9 | raw string 10 | encoded string 11 | } 12 | 13 | var ( 14 | url_encoder = charEncoder{ 15 | {"%", "%25"}, // must first 16 | {"!", "%21"}, 17 | {"\"", "%22"}, 18 | {"#", "%23"}, 19 | {"$", "%24"}, 20 | {"&", "%26"}, 21 | {"'", "%27"}, 22 | {"(", "%28"}, 23 | {")", "%29"}, 24 | {"+", "%2b"}, 25 | {",", "%2c"}, 26 | {"-", "-"}, 27 | {".", "."}, 28 | {"/", "%2f"}, 29 | {":", "%3a"}, 30 | {";", "%3b"}, 31 | {"<", "%3c"}, 32 | {"=", "%3d"}, 33 | {">", "%3e"}, 34 | {"@", "%40"}, 35 | {"[", "%5b"}, 36 | {"\\", "%5c"}, 37 | {"]", "%5d"}, 38 | {"^", "%5e"}, 39 | {"`", "%60"}, 40 | {"{", "%7b"}, 41 | {"|", "%7c"}, 42 | {"}", "%7d"}, 43 | {"~", "%7e"}, 44 | {"?", "%01"}, 45 | {"*", "%02"}, 46 | } 47 | 48 | wfn_encoder = charEncoder{ 49 | {"\\", "\\\\"}, 50 | {"-", "\\-"}, 51 | {"#", "\\#"}, 52 | {"$", "\\$"}, 53 | {"%", "\\%"}, 54 | {"&", "\\&"}, 55 | {"'", "\\'"}, 56 | {"(", "\\("}, 57 | {")", "\\)"}, 58 | {"+", "\\+"}, 59 | {",", "\\,"}, 60 | {".", "\\."}, 61 | {"/", "\\/"}, 62 | {":", "\\:"}, 63 | {";", "\\;"}, 64 | {"<", "\\<"}, 65 | {"=", "\\="}, 66 | {">", "\\>"}, 67 | {"@", "\\@"}, 68 | {"!", "\\!"}, 69 | {"\"", "\\\""}, 70 | {"[", "\\["}, 71 | {"]", "\\]"}, 72 | {"^", "\\^"}, 73 | {"`", "\\`"}, 74 | {"{", "\\{"}, 75 | {"}", "\\}"}, 76 | {"|", "\\|"}, 77 | {"~", "\\~"}, 78 | } 79 | 80 | fmt_encoder= charEncoder{ 81 | {"\\", "\\\\"}, 82 | {"#", "\\#"}, 83 | {"$", "\\$"}, 84 | {"%", "\\%"}, 85 | {"&", "\\&"}, 86 | {"'", "\\'"}, 87 | {"(", "\\("}, 88 | {")", "\\)"}, 89 | {"+", "\\+"}, 90 | {",", "\\,"}, 91 | {"/", "\\/"}, 92 | {":", "\\:"}, 93 | {";", "\\;"}, 94 | {"<", "\\<"}, 95 | {"=", "\\="}, 96 | {">", "\\>"}, 97 | {"@", "\\@"}, 98 | {"!", "\\!"}, 99 | {"\"", "\\\""}, 100 | {"[", "\\["}, 101 | {"]", "\\]"}, 102 | {"^", "\\^"}, 103 | {"`", "\\`"}, 104 | {"{", "\\{"}, 105 | {"}", "\\}"}, 106 | {"|", "\\|"}, 107 | {"~", "\\~"}, 108 | } 109 | ) 110 | 111 | func (t charEncoder) Encode(str string) string { 112 | for _, it := range t { 113 | str = it.Encode(str) 114 | } 115 | return str 116 | } 117 | 118 | func (t charEncoder) Decode(str string) string { 119 | for _, it := range t { 120 | str = it.Decode(str) 121 | } 122 | return str 123 | } 124 | 125 | func (t encodeTable) Encode(str string) string { 126 | return strings.Replace(str, t.raw, t.encoded, -1) 127 | } 128 | 129 | func (t encodeTable) Decode(str string) string { 130 | return strings.Replace(str, t.encoded, t.raw, -1) 131 | } 132 | -------------------------------------------------------------------------------- /matching.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | type Relation int 4 | 5 | const ( 6 | Disjoint = Relation(iota) 7 | Equal 8 | Subset 9 | Superset 10 | Undefined 11 | ) 12 | 13 | // CheckDisjoint implements CPE_DISJOINT. Returns true if the set-theoretic reration between the names is DISJOINT. 14 | func CheckDisjoint(src, trg *Item) bool { 15 | type obj struct { 16 | src Attribute 17 | trg Attribute 18 | } 19 | for _, v := range []obj{ 20 | {src.part, trg.part}, 21 | {src.vendor, trg.vendor}, 22 | {src.product, trg.product}, 23 | {src.version, trg.version}, 24 | {src.update, trg.update}, 25 | {src.edition, trg.edition}, 26 | {src.language, trg.language}, 27 | {src.sw_edition, trg.sw_edition}, 28 | {src.target_sw, trg.target_sw}, 29 | {src.target_hw, trg.target_hw}, 30 | {src.other, trg.other}, 31 | } { 32 | switch v.src.Comparison(v.trg) { 33 | case Disjoint: 34 | return true 35 | } 36 | } 37 | return false 38 | } 39 | 40 | // CheckEqual implements CPE_EQUAL. Returns true if the set-theoretic relation between src and trg is EQUAL. 41 | func CheckEqual(src, trg *Item) bool { 42 | type obj struct { 43 | src Attribute 44 | trg Attribute 45 | } 46 | for _, v := range []obj{ 47 | {src.part, trg.part}, 48 | {src.vendor, trg.vendor}, 49 | {src.product, trg.product}, 50 | {src.version, trg.version}, 51 | {src.update, trg.update}, 52 | {src.edition, trg.edition}, 53 | {src.language, trg.language}, 54 | {src.sw_edition, trg.sw_edition}, 55 | {src.target_sw, trg.target_sw}, 56 | {src.target_hw, trg.target_hw}, 57 | {src.other, trg.other}, 58 | } { 59 | switch v.src.Comparison(v.trg) { 60 | case Equal: 61 | default: 62 | return false 63 | } 64 | } 65 | return true 66 | } 67 | 68 | // CheckSubset implements CPE_SUBSET. Returns true if the set-theoretic relation between src and trg is SUBSET. 69 | func CheckSubset(src, trg *Item) bool { 70 | type obj struct { 71 | src Attribute 72 | trg Attribute 73 | } 74 | for _, v := range []obj{ 75 | {src.part, trg.part}, 76 | {src.vendor, trg.vendor}, 77 | {src.product, trg.product}, 78 | {src.version, trg.version}, 79 | {src.update, trg.update}, 80 | {src.edition, trg.edition}, 81 | {src.language, trg.language}, 82 | {src.sw_edition, trg.sw_edition}, 83 | {src.target_sw, trg.target_sw}, 84 | {src.target_hw, trg.target_hw}, 85 | {src.other, trg.other}, 86 | } { 87 | switch v.src.Comparison(v.trg) { 88 | case Subset, Equal: 89 | default: 90 | return false 91 | } 92 | } 93 | return true 94 | } 95 | 96 | // CheckSuperset implements CPE_SUPERSET. Returns true if the set-theoretic relation between src and trg is SUPERSET. 97 | func CheckSuperset(src, trg *Item) bool { 98 | type obj struct { 99 | src Attribute 100 | trg Attribute 101 | } 102 | for _, v := range []obj{ 103 | {src.part, trg.part}, 104 | {src.vendor, trg.vendor}, 105 | {src.product, trg.product}, 106 | {src.version, trg.version}, 107 | {src.update, trg.update}, 108 | {src.edition, trg.edition}, 109 | {src.language, trg.language}, 110 | {src.sw_edition, trg.sw_edition}, 111 | {src.target_sw, trg.target_sw}, 112 | {src.target_hw, trg.target_hw}, 113 | {src.other, trg.other}, 114 | } { 115 | switch v.src.Comparison(v.trg) { 116 | case Superset, Equal: 117 | default: 118 | return false 119 | } 120 | } 121 | return true 122 | } 123 | -------------------------------------------------------------------------------- /matching_test.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestCheckDisjoint(t *testing.T) { 9 | i1, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 10 | assert.Nil(t, err) 11 | i2, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 12 | assert.Nil(t, err) 13 | assert.Equal(t, false, CheckDisjoint(i1, i2)) 14 | 15 | i1, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_95"]`) 16 | assert.Nil(t, err) 17 | i2, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 18 | assert.Nil(t, err) 19 | assert.Equal(t, true, CheckDisjoint(i1, i2)) 20 | return 21 | } 22 | 23 | func TestCheckEqual(t *testing.T) { 24 | i1, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 25 | assert.Nil(t, err) 26 | i2, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 27 | assert.Nil(t, err) 28 | assert.Equal(t, true, CheckEqual(i1, i2)) 29 | 30 | i1, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_200?"]`) 31 | assert.Nil(t, err) 32 | i2, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 33 | assert.Nil(t, err) 34 | assert.Equal(t, false, CheckEqual(i1, i2)) 35 | return 36 | } 37 | 38 | func TestCheckSubset(t *testing.T) { 39 | i1, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000",update="sp3"]`) 40 | assert.Nil(t, err) 41 | i2, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 42 | assert.Nil(t, err) 43 | assert.Equal(t, true, CheckSubset(i1, i2)) 44 | 45 | i1, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_95"]`) 46 | assert.Nil(t, err) 47 | i2, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 48 | assert.Nil(t, err) 49 | assert.Equal(t, false, CheckSubset(i1, i2)) 50 | 51 | return 52 | } 53 | 54 | func TestCheckSuperset(t *testing.T) { 55 | i1, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 56 | assert.Nil(t, err) 57 | i2, err := NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000",update="sp3",edition="pro"]`) 58 | assert.Nil(t, err) 59 | assert.Equal(t, true, CheckSuperset(i1, i2)) 60 | 61 | i1, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_200*"]`) 62 | assert.Nil(t, err) 63 | i2, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000"]`) 64 | assert.Nil(t, err) 65 | assert.Equal(t, true, CheckSuperset(i1, i2)) 66 | 67 | i1, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_95"]`) 68 | assert.Nil(t, err) 69 | i2, err = NewItemFromWfn(`wfn:[part="o",vendor="microsoft",product="windows_2000",update="sp3",edition="pro"]`) 70 | assert.Nil(t, err) 71 | assert.Equal(t, false, CheckSuperset(i1, i2)) 72 | } 73 | 74 | func BenchmarkSuperset(b *testing.B) { 75 | item1, _ := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 76 | item2, _ := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 77 | b.ResetTimer() 78 | for i:=0; i< b.N; i++ { 79 | CheckSuperset(item1, item2) 80 | } 81 | } 82 | 83 | func BenchmarkSubset(b *testing.B) { 84 | item1, _ := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 85 | item2, _ := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 86 | b.ResetTimer() 87 | for i:=0; i< b.N; i++ { 88 | CheckSubset(item1, item2) 89 | } 90 | } 91 | 92 | func BenchmarkEqual(b *testing.B) { 93 | item1, _ := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 94 | item2, _ := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 95 | b.ResetTimer() 96 | for i:=0; i< b.N; i++ { 97 | CheckEqual(item1, item2) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /naming.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Item reprecents a CPE item. 9 | type Item struct { 10 | part PartAttr 11 | vendor StringAttr 12 | product StringAttr 13 | version StringAttr 14 | update StringAttr 15 | edition StringAttr 16 | language StringAttr 17 | sw_edition StringAttr 18 | target_sw StringAttr 19 | target_hw StringAttr 20 | other StringAttr 21 | } 22 | 23 | // NewItem returns empty Item. 24 | func NewItem() *Item { 25 | return &Item{ 26 | part: PartNotSet, 27 | vendor: Any, 28 | product: Any, 29 | version: Any, 30 | update: Any, 31 | edition: Any, 32 | language: Any, 33 | sw_edition: Any, 34 | target_sw: Any, 35 | target_hw: Any, 36 | other: Any, 37 | } 38 | } 39 | 40 | var goCpeOriginalDelim = "[goCpeOriginalDelim]" 41 | 42 | func NewItemFromWfn(wfn string) (*Item, error) { 43 | if strings.HasPrefix(wfn, "wfn:[") { 44 | wfn = strings.TrimPrefix(wfn, "wfn:[") 45 | } else { 46 | return nil, cpeerr{reason: err_invalid_wfn} 47 | } 48 | 49 | if strings.HasSuffix(wfn, "]") { 50 | wfn = strings.TrimSuffix(wfn, "]") 51 | } else { 52 | return nil, cpeerr{reason: err_invalid_wfn} 53 | } 54 | 55 | item := NewItem() 56 | for _, attr := range strings.Split(wfn, ",") { 57 | sepattr := strings.Split(attr, "=") 58 | if len(sepattr) != 2 { 59 | return nil, cpeerr{reason: err_invalid_wfn} 60 | } 61 | 62 | n, v := sepattr[0], sepattr[1] 63 | switch n { 64 | case "part": 65 | item.part = newPartAttrFromWfnEncoded(v) 66 | case "vendor": 67 | item.vendor = newStringAttrFromWfnEncoded(v) 68 | case "product": 69 | item.product = newStringAttrFromWfnEncoded(v) 70 | case "version": 71 | item.version = newStringAttrFromWfnEncoded(v) 72 | case "update": 73 | item.update = newStringAttrFromWfnEncoded(v) 74 | case "edition": 75 | item.edition = newStringAttrFromWfnEncoded(v) 76 | case "language": 77 | item.language = newStringAttrFromWfnEncoded(v) 78 | case "sw_edition": 79 | item.sw_edition = newStringAttrFromWfnEncoded(v) 80 | case "target_sw": 81 | item.target_sw = newStringAttrFromWfnEncoded(v) 82 | case "target_hw": 83 | item.target_hw = newStringAttrFromWfnEncoded(v) 84 | case "other": 85 | item.other = newStringAttrFromWfnEncoded(v) 86 | } 87 | } 88 | 89 | return item, nil 90 | } 91 | 92 | func NewItemFromUri(uri string) (*Item, error) { 93 | if strings.HasPrefix(uri, "cpe:/") { 94 | uri = strings.TrimPrefix(uri, "cpe:/") 95 | } else { 96 | return nil, cpeerr{reason: err_invalid_wfn} 97 | } 98 | 99 | item := NewItem() 100 | for i, attr := range strings.Split(uri, ":") { 101 | switch i { 102 | case 0: 103 | item.part = newPartAttrFromUriEncoded(attr) 104 | case 1: 105 | item.vendor = newStringAttrFromUriEncoded(attr) 106 | case 2: 107 | item.product = newStringAttrFromUriEncoded(attr) 108 | case 3: 109 | item.version = newStringAttrFromUriEncoded(attr) 110 | case 4: 111 | item.update = newStringAttrFromUriEncoded(attr) 112 | case 5: 113 | editions := strings.Split(attr, "~") 114 | if len(editions) == 1 { 115 | item.edition = newStringAttrFromUriEncoded(editions[0]) 116 | } else if len(editions) == 6 { 117 | item.edition = newStringAttrFromUriEncoded(editions[1]) 118 | item.sw_edition = newStringAttrFromUriEncoded(editions[2]) 119 | item.target_sw = newStringAttrFromUriEncoded(editions[3]) 120 | item.target_hw = newStringAttrFromUriEncoded(editions[4]) 121 | item.other = newStringAttrFromUriEncoded(editions[5]) 122 | } else { 123 | return nil, cpeerr{reason: err_invalid_wfn} 124 | } 125 | } 126 | } 127 | return item, nil 128 | } 129 | 130 | func NewItemFromFormattedString(str string) (*Item, error) { 131 | if strings.HasPrefix(str, "cpe:2.3:") { 132 | str = replaceToDelim(strings.TrimPrefix(str, "cpe:2.3:")) 133 | } else { 134 | return nil, cpeerr{reason: err_invalid_wfn} 135 | } 136 | 137 | attrs := strings.Split(str, ":") 138 | if len(attrs) != 11 { 139 | return nil, cpeerr{reason: err_invalid_wfn} 140 | } 141 | 142 | item := NewItem() 143 | for i, attr := range attrs { 144 | switch i { 145 | case 0: 146 | item.part = newPartAttrFromFmtEncoded(replaceFromDelim(attr)) 147 | case 1: 148 | item.vendor = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 149 | case 2: 150 | item.product = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 151 | case 3: 152 | item.version = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 153 | case 4: 154 | item.update = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 155 | case 5: 156 | item.edition = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 157 | case 6: 158 | item.language = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 159 | case 7: 160 | item.sw_edition = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 161 | case 8: 162 | item.target_sw = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 163 | case 9: 164 | item.target_hw = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 165 | case 10: 166 | item.other = newStringAttrFromFmtEncoded(replaceFromDelim(attr)) 167 | } 168 | } 169 | 170 | return item, nil 171 | } 172 | 173 | // Wfn returns a string of Well-Formed string data model. 174 | func (m *Item) Wfn() string { 175 | wfn := "wfn:[" 176 | first := true 177 | 178 | for _, it := range []struct { 179 | name string 180 | attr Attribute 181 | }{ 182 | {"part", m.part}, 183 | {"vendor", m.vendor}, 184 | {"product", m.product}, 185 | {"version", m.version}, 186 | {"update", m.update}, 187 | {"edition", m.edition}, 188 | {"language", m.language}, 189 | {"sw_edition", m.sw_edition}, 190 | {"target_sw", m.target_sw}, 191 | {"target_hw", m.target_hw}, 192 | {"other", m.other}, 193 | } { 194 | if !it.attr.IsEmpty() { 195 | if first { 196 | first = false 197 | } else { 198 | wfn += "," 199 | } 200 | wfn += it.name + "=" + it.attr.wfnEncoded() 201 | } 202 | } 203 | wfn += "]" 204 | 205 | return wfn 206 | } 207 | 208 | // Wfn returns a string of uri binding. 209 | func (m *Item) Uri() string { 210 | uri := "cpe:/" 211 | 212 | l := []struct { 213 | name string 214 | attr Attribute 215 | }{ 216 | {"part", m.part}, 217 | {"vendor", m.vendor}, 218 | {"product", m.product}, 219 | {"version", m.version}, 220 | {"update", m.update}, 221 | } 222 | 223 | for c, it := range l { 224 | if !it.attr.IsEmpty() { 225 | uri += it.attr.urlEncoded() 226 | } 227 | if c+1 != len(l) { 228 | uri += ":" 229 | } 230 | } 231 | 232 | if m.target_hw.urlEncoded() != "" || 233 | m.target_sw.urlEncoded() != "" || 234 | m.sw_edition.urlEncoded() != "" || 235 | m.other.urlEncoded() != "" { 236 | uri += ":~" + m.edition.urlEncoded() 237 | uri += "~" + m.sw_edition.urlEncoded() 238 | uri += "~" + m.target_sw.urlEncoded() 239 | uri += "~" + m.target_hw.urlEncoded() 240 | uri += "~" + m.other.urlEncoded() 241 | } else { 242 | uri += ":" + m.edition.urlEncoded() 243 | } 244 | 245 | uri += ":" + m.language.urlEncoded() 246 | return strings.TrimRight(uri, ":*") 247 | } 248 | 249 | // Wfn returns a formatted string binding. 250 | func (m *Item) Formatted() string { 251 | fmted := "cpe:2.3" 252 | 253 | for _, it := range []Attribute{ 254 | m.part, m.vendor, m.product, m.version, m.update, m.edition, m.language, m.sw_edition, m.target_sw, m.target_hw, m.other, 255 | } { 256 | if !it.IsEmpty() { 257 | fmted += ":" + it.fmtString() 258 | } else { 259 | fmted += ":*" 260 | 261 | } 262 | } 263 | return fmted 264 | } 265 | 266 | // SetPart sets part of item. returns error if p is invalid. 267 | func (i *Item) SetPart(p PartAttr) error { 268 | if !p.IsValid() { 269 | return cpeerr{reason: err_invalid_type, attr: []interface{}{p, "part"}} 270 | } 271 | 272 | i.part = p 273 | return nil 274 | } 275 | 276 | // Part returns part of item. 277 | func (i *Item) Part() PartAttr { 278 | return i.part 279 | } 280 | 281 | // SetVendor sets vendor of item. returns error if s is invalid. 282 | func (i *Item) SetVendor(s StringAttr) error { 283 | if !s.IsValid() { 284 | return cpeerr{reason: err_invalid_attribute_str} 285 | } 286 | 287 | i.vendor = s 288 | return nil 289 | } 290 | 291 | // Vendor returns vendor of item. 292 | func (i *Item) Vendor() StringAttr { 293 | return i.vendor 294 | } 295 | 296 | // SetProduct sets vendor of item. returns error if s is invalid. 297 | func (i *Item) SetProduct(s StringAttr) error { 298 | if !s.IsValid() { 299 | return cpeerr{reason: err_invalid_attribute_str} 300 | } 301 | 302 | i.product = s 303 | return nil 304 | } 305 | 306 | // Vendor returns product of item. 307 | func (i *Item) Product() StringAttr { 308 | return i.product 309 | } 310 | 311 | // SetVersion sets version of item. returns error if s is invalid. 312 | func (i *Item) SetVersion(s StringAttr) error { 313 | if !s.IsValid() { 314 | return cpeerr{reason: err_invalid_attribute_str} 315 | } 316 | 317 | i.version = s 318 | return nil 319 | } 320 | 321 | // Version returns version of item. 322 | func (i *Item) Version() StringAttr { 323 | return i.version 324 | } 325 | 326 | // SetUpdate sets update of item. returns error if s is invalid. 327 | func (i *Item) SetUpdate(s StringAttr) error { 328 | if !s.IsValid() { 329 | return cpeerr{reason: err_invalid_attribute_str} 330 | } 331 | 332 | i.update = s 333 | return nil 334 | } 335 | 336 | // Update returns update of item. 337 | func (i *Item) Update() StringAttr { 338 | return i.update 339 | } 340 | 341 | // SetEdition sets edition of item. returns error if s is invalid. 342 | func (i *Item) SetEdition(s StringAttr) error { 343 | if !s.IsValid() { 344 | return cpeerr{reason: err_invalid_attribute_str} 345 | } 346 | 347 | i.edition = s 348 | return nil 349 | } 350 | 351 | // Edition returns edition of item. 352 | func (i *Item) Edition() StringAttr { 353 | return i.edition 354 | } 355 | 356 | // SetLanguage sets language of item. returns error if s is invalid. 357 | func (i *Item) SetLanguage(s StringAttr) error { 358 | if !s.IsValid() { 359 | return cpeerr{reason: err_invalid_attribute_str} 360 | } 361 | 362 | i.language = s 363 | return nil 364 | } 365 | 366 | // Language returns language of item. 367 | func (i *Item) Language() StringAttr { 368 | return i.language 369 | } 370 | 371 | // SetSwEdition sets sw_edition of item. returns error if s is invalid. 372 | func (i *Item) SetSwEdition(s StringAttr) error { 373 | if !s.IsValid() { 374 | return cpeerr{reason: err_invalid_attribute_str} 375 | } 376 | 377 | i.sw_edition = s 378 | return nil 379 | } 380 | 381 | // SwEdition returns sw_edition of item. 382 | func (i *Item) SwEdition() StringAttr { 383 | return i.sw_edition 384 | } 385 | 386 | // SetTargetSw sets target_sw of item. returns error if s is invalid. 387 | func (i *Item) SetTargetSw(s StringAttr) error { 388 | if !s.IsValid() { 389 | return cpeerr{reason: err_invalid_attribute_str} 390 | } 391 | 392 | i.target_sw = s 393 | return nil 394 | } 395 | 396 | // TargetSw returns target_sw of item. 397 | func (i *Item) TargetSw() StringAttr { 398 | return i.target_sw 399 | } 400 | 401 | // SetTargetHw sets target_hw of item. returns error if s is invalid. 402 | func (i *Item) SetTargetHw(s StringAttr) error { 403 | if !s.IsValid() { 404 | return cpeerr{reason: err_invalid_attribute_str} 405 | } 406 | 407 | i.target_hw = s 408 | return nil 409 | } 410 | 411 | // TargetHw returns target_hw of item. 412 | func (i *Item) TargetHw() StringAttr { 413 | return i.target_hw 414 | } 415 | 416 | // SetOther sets other of item. returns error if s is invalid. 417 | func (i *Item) SetOther(s StringAttr) error { 418 | if !s.IsValid() { 419 | return cpeerr{reason: err_invalid_attribute_str} 420 | } 421 | 422 | i.other = s 423 | return nil 424 | } 425 | 426 | // Other returns other of item. 427 | func (i *Item) Other() StringAttr { 428 | return i.other 429 | } 430 | 431 | func replaceToDelim(str string) string { 432 | return strings.Replace(str, "\\:", goCpeOriginalDelim, -1) 433 | } 434 | 435 | func replaceFromDelim(str string) string { 436 | return strings.Replace(str, goCpeOriginalDelim, "\\:", -1) 437 | } 438 | 439 | type cpeerr struct { 440 | reason string 441 | attr []interface{} 442 | } 443 | 444 | var ( 445 | err_invalid_type = "\"%#v\" is not valid as %v attribute." 446 | err_invalid_attribute_str = "invalid attribute string." 447 | err_invalid_wfn = "invalid wfn string." 448 | ) 449 | 450 | func (e cpeerr) Error() string { 451 | return fmt.Sprintf("cpe:"+e.reason, e.attr...) 452 | } 453 | -------------------------------------------------------------------------------- /naming_test.go: -------------------------------------------------------------------------------- 1 | package cpe 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | // from 5.5 WFN Example @ NISTIR-7695-CPE-Naming 10 | func TestWfnExamples(t *testing.T) { 11 | // Example 1 12 | item := NewItem() 13 | item.SetPart(Application) 14 | item.SetVendor(NewStringAttr("microsoft")) 15 | item.SetProduct(NewStringAttr("internet_explorer")) 16 | item.SetVersion(NewStringAttr("8.0.6001")) 17 | item.SetUpdate(NewStringAttr("beta")) 18 | item.SetEdition(Na) 19 | assert.Equal(t, `wfn:[part="a",vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta",edition=NA]`, item.Wfn()) 20 | 21 | // Example 2 22 | item = NewItem() 23 | item.SetPart(Application) 24 | item.SetVendor(NewStringAttr("microsoft")) 25 | item.SetProduct(NewStringAttr("internet_explorer")) 26 | item.SetVersion(NewStringAttr("8.*")) 27 | item.SetUpdate(NewStringAttr("sp?")) 28 | item.SetEdition(Na) 29 | item.SetLanguage(Any) 30 | assert.Equal(t, `wfn:[part="a",vendor="microsoft",product="internet_explorer",version="8\.*",update="sp?",edition=NA]`, item.Wfn()) 31 | 32 | // Example 3 33 | item = NewItem() 34 | item.SetPart(Application) 35 | item.SetVendor(NewStringAttr("hp")) 36 | item.SetProduct(NewStringAttr("insight_diagnostics")) 37 | item.SetVersion(NewStringAttr("7.4.0.1570")) 38 | item.SetSwEdition(NewStringAttr("online")) 39 | item.SetTargetSw(NewStringAttr("windows_2003")) 40 | item.SetTargetHw(NewStringAttr("x64")) 41 | assert.Equal(t, `wfn:[part="a",vendor="hp",product="insight_diagnostics",version="7\.4\.0\.1570",sw_edition="online",target_sw="windows_2003",target_hw="x64"]`, item.Wfn()) 42 | 43 | // Example 4 44 | item = NewItem() 45 | item.SetPart(Application) 46 | item.SetVendor(NewStringAttr("hp")) 47 | item.SetProduct(NewStringAttr("openview_network_manager")) 48 | item.SetVersion(NewStringAttr("7.51")) 49 | item.SetUpdate(Na) 50 | item.SetTargetSw(NewStringAttr("linux")) 51 | assert.Equal(t, `wfn:[part="a",vendor="hp",product="openview_network_manager",version="7\.51",update=NA,target_sw="linux"]`, item.Wfn()) 52 | 53 | // Example 5 54 | item = NewItem() 55 | item.SetPart(Application) 56 | item.SetVendor(NewStringAttr("foo\\bar")) 57 | item.SetProduct(NewStringAttr("big$money_2010")) 58 | item.SetSwEdition(NewStringAttr("special")) 59 | item.SetTargetSw(NewStringAttr("ipod_touch")) 60 | assert.Equal(t, `wfn:[part="a",vendor="foo\\bar",product="big\$money_2010",sw_edition="special",target_sw="ipod_touch"]`, item.Wfn()) 61 | } 62 | 63 | // from 6.1.2.4 Examples of binding a WFN to a URI @ NISTIR-7695-CPE-Naming 64 | func TestUrlExamples(t *testing.T) { 65 | // Example 1 66 | item := NewItem() 67 | item.SetPart(Application) 68 | item.SetVendor(NewStringAttr("microsoft")) 69 | item.SetProduct(NewStringAttr("internet_explorer")) 70 | item.SetVersion(NewStringAttr("8.0.6001")) 71 | item.SetUpdate(NewStringAttr("beta")) 72 | item.SetEdition(Any) 73 | assert.Equal(t, `cpe:/a:microsoft:internet_explorer:8.0.6001:beta`, item.Uri()) 74 | 75 | // Example 2 76 | item = NewItem() 77 | item.SetPart(Application) 78 | item.SetVendor(NewStringAttr("microsoft")) 79 | item.SetProduct(NewStringAttr("internet_explorer")) 80 | item.SetVersion(NewStringAttr("8.*")) 81 | item.SetUpdate(NewStringAttr("sp?")) 82 | assert.Equal(t, `cpe:/a:microsoft:internet_explorer:8.%02:sp%01`, item.Uri()) 83 | 84 | // Example3 85 | item = NewItem() 86 | item.SetPart(Application) 87 | item.SetVendor(NewStringAttr("hp")) 88 | item.SetProduct(NewStringAttr("insight_diagnostics")) 89 | item.SetVersion(NewStringAttr("7.4.0.1570")) 90 | item.SetUpdate(Na) 91 | item.SetSwEdition(NewStringAttr("online")) 92 | item.SetTargetSw(NewStringAttr("win2003")) 93 | item.SetTargetHw(NewStringAttr("x64")) 94 | assert.Equal(t, `cpe:/a:hp:insight_diagnostics:7.4.0.1570:-:~~online~win2003~x64~`, item.Uri()) 95 | 96 | // Example 4 97 | item = NewItem() 98 | item.SetPart(Application) 99 | item.SetVendor(NewStringAttr("hp")) 100 | item.SetProduct(NewStringAttr("openview_network_manager")) 101 | item.SetVersion(NewStringAttr("7.51")) 102 | item.SetTargetSw(NewStringAttr("linux")) 103 | assert.Equal(t, `cpe:/a:hp:openview_network_manager:7.51::~~~linux~~`, item.Uri()) 104 | 105 | // Example 5 106 | item = NewItem() 107 | item.SetPart(Application) 108 | item.SetVendor(NewStringAttr("foo\\bar")) 109 | item.SetProduct(NewStringAttr("big$money_manager_2010")) 110 | item.SetSwEdition(NewStringAttr("special")) 111 | item.SetTargetSw(NewStringAttr("ipod_touch")) 112 | item.SetTargetHw(NewStringAttr("80gb")) 113 | assert.Equal(t, `cpe:/a:foo%5cbar:big%24money_manager_2010:::~~special~ipod_touch~80gb~`, item.Uri()) 114 | } 115 | 116 | func TestFormattedString(t *testing.T) { 117 | // Example 1 118 | item := NewItem() 119 | item.SetPart(Application) 120 | item.SetVendor(NewStringAttr("microsoft")) 121 | item.SetProduct(NewStringAttr("internet_explorer")) 122 | item.SetVersion(NewStringAttr("8.0.6001")) 123 | item.SetUpdate(NewStringAttr("beta")) 124 | assert.Equal(t, `cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*`, item.Formatted()) 125 | 126 | // Example 2 127 | item = NewItem() 128 | item.SetPart(Application) 129 | item.SetVendor(NewStringAttr("microsoft")) 130 | item.SetProduct(NewStringAttr("internet_explorer")) 131 | item.SetVersion(NewStringAttr("8.*")) 132 | item.SetUpdate(NewStringAttr("sp?")) 133 | item.SetLanguage(Any) 134 | assert.Equal(t, `cpe:2.3:a:microsoft:internet_explorer:8.*:sp?:*:*:*:*:*:*`, item.Formatted()) 135 | 136 | // Example 3 137 | item = NewItem() 138 | item.SetPart(Application) 139 | item.SetVendor(NewStringAttr("hp")) 140 | item.SetProduct(NewStringAttr("insight_diagnostics")) 141 | item.SetVersion(NewStringAttr("7.4.0.1570")) 142 | item.SetUpdate(Na) 143 | item.SetSwEdition(NewStringAttr("online")) 144 | item.SetTargetSw(NewStringAttr("win2003")) 145 | item.SetTargetHw(NewStringAttr("x64")) 146 | assert.Equal(t, `cpe:2.3:a:hp:insight_diagnostics:7.4.0.1570:-:*:*:online:win2003:x64:*`, item.Formatted()) 147 | 148 | // Example 4 149 | item = NewItem() 150 | item.SetPart(Application) 151 | item.SetVendor(NewStringAttr("hp")) 152 | item.SetProduct(NewStringAttr("openview_network_manager")) 153 | item.SetVersion(NewStringAttr("7.51")) 154 | item.SetTargetSw(NewStringAttr("linux")) 155 | assert.Equal(t, `cpe:2.3:a:hp:openview_network_manager:7.51:*:*:*:*:linux:*:*`, item.Formatted()) 156 | 157 | // Example 5 158 | item = NewItem() 159 | item.SetPart(Application) 160 | item.SetVendor(NewStringAttr("foo\\bar")) 161 | item.SetProduct(NewStringAttr("big$money_2010")) 162 | item.SetSwEdition(NewStringAttr("special")) 163 | item.SetTargetSw(NewStringAttr("ipod_touch")) 164 | item.SetTargetHw(NewStringAttr("80gb")) 165 | assert.Equal(t, `cpe:2.3:a:foo\\bar:big\$money_2010:*:*:*:*:special:ipod_touch:80gb:*`, item.Formatted()) 166 | } 167 | 168 | func TestNewItemFromWfn(t *testing.T) { 169 | // Example 1 170 | item, err := NewItemFromWfn(`wfn:[part="a",vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta",edition=NA]`) 171 | assert.Nil(t, err) 172 | 173 | if item != nil { 174 | assert.Equal(t, item.Part(), Application) 175 | assert.Equal(t, item.Vendor(), NewStringAttr("microsoft")) 176 | assert.Equal(t, item.Product(), NewStringAttr("internet_explorer")) 177 | assert.Equal(t, item.Version(), NewStringAttr("8.0.6001")) 178 | assert.Equal(t, item.Update(), NewStringAttr("beta")) 179 | assert.Equal(t, item.Edition(), Na) 180 | } 181 | 182 | // Example 5 183 | item, err = NewItemFromWfn(`wfn:[part="a",vendor="foo\\bar",product="big\$money_2010",sw_edition="special",target_sw="ipod_touch"]`) 184 | assert.Nil(t, err) 185 | 186 | if item != nil { 187 | assert.Equal(t, item.Part(), Application) 188 | assert.Equal(t, item.Vendor(), NewStringAttr("foo\\bar")) 189 | assert.Equal(t, item.Product(), NewStringAttr("big$money_2010")) 190 | assert.Equal(t, item.SwEdition(), NewStringAttr("special")) 191 | assert.Equal(t, item.TargetSw(), NewStringAttr("ipod_touch")) 192 | } 193 | 194 | // Example 1' 195 | _, err = NewItemFromWfn(`wfn:[part="a",vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta",edition=NA`) 196 | assert.Error(t, err) 197 | 198 | // Example 1'' 199 | _, err = NewItemFromWfn(`part="a",vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta",edition=NA]`) 200 | assert.Error(t, err) 201 | 202 | // Example 1''' 203 | _, err = NewItemFromWfn(`wfn:[part="a"vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta",edition=NA]`) 204 | assert.Error(t, err) 205 | } 206 | 207 | func TestNewItemFromUri(t *testing.T) { 208 | // Example1 209 | item, err := NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 210 | assert.Nil(t, err) 211 | if item != nil { 212 | assert.Equal(t, item.Part(), Application) 213 | assert.Equal(t, item.Vendor(), NewStringAttr("microsoft")) 214 | assert.Equal(t, item.Product(), NewStringAttr("internet_explorer")) 215 | assert.Equal(t, item.Version(), NewStringAttr("8.0.6001")) 216 | assert.Equal(t, item.Update(), NewStringAttr("beta")) 217 | } 218 | 219 | // Example3 220 | item, err = NewItemFromUri(`cpe:/a:hp:insight_diagnostics:7.4.0.1570:-:~~online~win2003~x64~`) 221 | assert.Nil(t, err) 222 | if item != nil { 223 | assert.Equal(t, item.Part(), Application) 224 | assert.Equal(t, item.Vendor(), NewStringAttr("hp")) 225 | assert.Equal(t, item.Product(), NewStringAttr("insight_diagnostics")) 226 | assert.Equal(t, item.Version(), NewStringAttr("7.4.0.1570")) 227 | assert.Equal(t, item.Update(), Na) 228 | assert.Equal(t, item.SwEdition(), NewStringAttr("online")) 229 | assert.Equal(t, item.TargetSw(), NewStringAttr("win2003")) 230 | assert.Equal(t, item.TargetHw(), NewStringAttr("x64")) 231 | } 232 | 233 | // Example1' 234 | item, err = NewItemFromUri("a:microsoft:internet_explorer:8.0.6001:beta") 235 | assert.Error(t, err) 236 | } 237 | 238 | func TestNewItemFromFmt(t *testing.T) { 239 | // Example1 240 | item, err := NewItemFromFormattedString(`cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*`) 241 | assert.Nil(t, err) 242 | if item != nil { 243 | assert.Equal(t, item.Part(), Application) 244 | assert.Equal(t, item.Vendor(), NewStringAttr("microsoft")) 245 | assert.Equal(t, item.Product(), NewStringAttr("internet_explorer")) 246 | assert.Equal(t, item.Version(), NewStringAttr("8.0.6001")) 247 | assert.Equal(t, item.Update(), NewStringAttr("beta")) 248 | } 249 | 250 | // Example5 251 | item, err = NewItemFromFormattedString(`cpe:2.3:a:foo\\bar:big\$money_2010:*:*:*:*:special:ipod_touch:80gb:*`) 252 | assert.Nil(t, err) 253 | if item != nil { 254 | assert.Equal(t, item.Part(), Application) 255 | assert.Equal(t, item.Vendor(), NewStringAttr("foo\\bar")) 256 | assert.Equal(t, item.Product(), NewStringAttr("big$money_2010")) 257 | assert.Equal(t, item.SwEdition(), NewStringAttr("special")) 258 | assert.Equal(t, item.TargetSw(), NewStringAttr("ipod_touch")) 259 | assert.Equal(t, item.TargetHw(), NewStringAttr("80gb")) 260 | } 261 | 262 | item, err = NewItemFromFormattedString(`cpe:2.3:a:xt-commerce:xt\\:commerce:*:*:*:*:*:*:*:*`) 263 | assert.Nil(t, err) 264 | if item != nil { 265 | assert.Equal(t, item.Part(), Application) 266 | assert.Equal(t, item.Vendor(), NewStringAttr("xt-commerce")) 267 | assert.Equal(t, item.Product(), NewStringAttr("xt:commerce")) 268 | assert.Equal(t, item.SwEdition(), NewStringAttr("")) 269 | assert.Equal(t, item.TargetSw(), NewStringAttr("")) 270 | assert.Equal(t, item.TargetHw(), NewStringAttr("")) 271 | } 272 | 273 | // Example1' 274 | item, err = NewItemFromFormattedString(`a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*`) 275 | assert.Error(t, err) 276 | 277 | // Example1'' 278 | item, err = NewItemFromFormattedString(`cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*`) 279 | assert.Error(t, err) 280 | 281 | } 282 | 283 | func BenchmarkNewItemFromUri(b *testing.B) { 284 | for i := 0; i < b.N; i++ { 285 | NewItemFromUri("cpe:/a:microsoft:internet_explorer:8.0.6001:beta") 286 | } 287 | } 288 | 289 | func BenchmarkNewItemFromWfn(b *testing.B) { 290 | for i := 0; i < b.N; i++ { 291 | NewItemFromWfn(`wfn:[part="a",vendor="microsoft",product="internet_explorer",version="8\.0\.6001",update="beta",edition=NA]`) 292 | } 293 | } 294 | 295 | func BenchmarkNewItemFromFormattedString(b *testing.B) { 296 | for i := 0; i < b.N; i++ { 297 | NewItemFromFormattedString(`cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*`) 298 | } 299 | } 300 | --------------------------------------------------------------------------------