aboutsummaryrefslogtreecommitdiff
path: root/cpedict
diff options
context:
space:
mode:
Diffstat (limited to 'cpedict')
-rw-r--r--cpedict/cpedict.go46
-rw-r--r--cpedict/cpedict_test.go136
-rw-r--r--cpedict/testdata/test-0.xml.gzbin0 -> 1359 bytes
3 files changed, 182 insertions, 0 deletions
diff --git a/cpedict/cpedict.go b/cpedict/cpedict.go
new file mode 100644
index 0000000..0b5f77b
--- /dev/null
+++ b/cpedict/cpedict.go
@@ -0,0 +1,46 @@
+// CPE 2.3 dictionary parser.
+//
+// The official NVD CPE dictionary is available here:
+// https://nvd.nist.gov/products/cpe
+package cpedict
+
+import "time"
+
+// Dictionary generator information.
+type Generator struct {
+ ProductName string `xml:"product_name"` // Product name.
+ ProductVersion string `xml:"product_version"` // Product version.
+ SchemaVersion string `xml:"schema_version"` // Schema version.
+ Timestamp time.Time `xml:"timestamp"` // Generation timestamp.
+}
+
+// Dictionary item title.
+type Title struct {
+ Lang string `xml:"lang,attr"` // language code
+ Text string `xml:",chardata"` // value
+}
+
+// Dictionary item reference.
+type Reference struct {
+ Href string `xml:"href,attr"` // Link
+ Text string `xml:",chardata"` // Text
+}
+
+// CPE 2.3 item attributes.
+type Cpe23Item struct {
+ Name string `xml:"name,attr"` // CPE 2.3 formatting string.
+}
+
+// Dictionary item.
+type Item struct {
+ CpeUri string `xml:"name,attr"` // CPE URI.
+ Cpe23Item Cpe23Item `xml:"cpe23-item"` // CPE 2.3 formatting string.
+ Titles []Title `xml:"title"` // Item titles.
+ References []Reference `xml:"references>reference"` // References.
+}
+
+// CPE dictionary.
+type Dictionary struct {
+ Generator Generator `xml:"generator"` // Dictionary generator.
+ Items []Item `xml:"cpe-item"` // Dictionary items.
+}
diff --git a/cpedict/cpedict_test.go b/cpedict/cpedict_test.go
new file mode 100644
index 0000000..eca0aeb
--- /dev/null
+++ b/cpedict/cpedict_test.go
@@ -0,0 +1,136 @@
+package cpedict
+
+import (
+ "compress/gzip"
+ "encoding/xml"
+ "os"
+ "testing"
+ "time"
+ "reflect"
+)
+
+func TestDictionaryXMLUnmarshal(t *testing.T) {
+ // open test data
+ f, err := os.Open("testdata/test-0.xml.gz")
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer f.Close()
+
+ // create gzip reader
+ gz, err := gzip.NewReader(f)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ defer gz.Close()
+
+ // create xml decoder, decode xml
+ d := xml.NewDecoder(gz)
+ var dict Dictionary
+
+ if err := d.Decode(&dict); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // build expected time
+ var expTime time.Time
+ if err := expTime.UnmarshalText([]byte("2022-02-02T04:51:00.437Z")); err != nil {
+ t.Error(err)
+ return
+ }
+
+ // expected generator
+ expGenerator := Generator {
+ ProductName: "National Vulnerability Database (NVD)",
+ ProductVersion: "4.9",
+ SchemaVersion: "2.3",
+ Timestamp: expTime,
+ }
+
+ // compare generator
+ if !reflect.DeepEqual(dict.Generator, expGenerator) {
+ t.Errorf("got \"%v\", exp \"%v\"", dict.Generator, expGenerator)
+ return
+ }
+
+ // check item count
+ gotNumItems := len(dict.Items)
+ expNumItems := 19
+ if gotNumItems != expNumItems {
+ t.Errorf("item count: got %d, exp %d", gotNumItems, expNumItems)
+ return
+ }
+
+ // build expected item
+ expItems := []Item {
+ // first item in the list
+ Item {
+ CpeUri: "cpe:/a:%240.99_kindle_books_project:%240.99_kindle_books:6::~~~android~~",
+
+ Cpe23Item: Cpe23Item {
+ Name: "cpe:2.3:a:\\$0.99_kindle_books_project:\\$0.99_kindle_books:6:*:*:*:*:android:*:*",
+ },
+
+ Titles: []Title {
+ Title {
+ Lang: "en-US",
+ Text: "$0.99 Kindle Books project $0.99 Kindle Books (aka com.kindle.books.for99) for android 6.0",
+ },
+ },
+
+ References: []Reference {
+ Reference {
+ Href: "https://play.google.com/store/apps/details?id=com.kindle.books.for99",
+ Text: "Product information",
+ },
+
+ Reference {
+ Href: "https://docs.google.com/spreadsheets/d/1t5GXwjw82SyunALVJb2w0zi3FoLRIkfGPc7AMjRF0r4/edit?pli=1#gid=1053404143",
+ Text: "Government Advisory",
+ },
+ },
+ },
+
+ // last item in the list
+ Item {
+ CpeUri: "cpe:/a:3com:3c16116-us:2.0",
+
+ Cpe23Item: Cpe23Item {
+ Name: "cpe:2.3:a:3com:3c16116-us:2.0:*:*:*:*:*:*:*",
+ },
+
+ Titles: []Title {
+ Title {
+ Lang: "ja-JP",
+ Text: "スリーコム WebCache 3000 2.0",
+ },
+
+ Title {
+ Lang: "en-US",
+ Text: "3Com WebCache 3000 2.0",
+ },
+ },
+ },
+ }
+
+ // build item comparisons
+ compares := []struct {
+ name string
+ exp Item
+ got Item
+ } {
+ { "head", expItems[0], dict.Items[0] },
+ { "tail", expItems[1], dict.Items[len(dict.Items) - 1] },
+ }
+
+ for _, row := range(compares) {
+ t.Run(row.name, func(t *testing.T) {
+ if !reflect.DeepEqual(row.got, row.exp) {
+ t.Errorf("got \"%v\", exp \"%v\"", row.got, row.exp)
+ }
+ })
+ }
+}
diff --git a/cpedict/testdata/test-0.xml.gz b/cpedict/testdata/test-0.xml.gz
new file mode 100644
index 0000000..110e965
--- /dev/null
+++ b/cpedict/testdata/test-0.xml.gz
Binary files differ