Skip to content

Commit f0d8695

Browse files
committed
oval: add+use custom Date type
Signed-off-by: Hank Donnay <[email protected]>
1 parent cf64532 commit f0d8695

File tree

6 files changed

+934407
-11
lines changed

6 files changed

+934407
-11
lines changed

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/quay/goval-parser
22

33
go 1.13
44

5-
require golang.org/x/tools v0.0.0-20190924052046-3ac2a5bbd98a
5+
require (
6+
github.com/google/go-cmp v0.4.0
7+
golang.org/x/tools v0.0.0-20190924052046-3ac2a5bbd98a
8+
)

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
2+
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
13
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
24
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
35
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -6,3 +8,4 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
68
golang.org/x/tools v0.0.0-20190924052046-3ac2a5bbd98a h1:DJzZ1GRmbjp7ihxzAN6UTVpVMi6k4CXZEr7A3wi2kRA=
79
golang.org/x/tools v0.0.0-20190924052046-3ac2a5bbd98a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
810
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
11+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

oval/date_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package oval
2+
3+
import (
4+
"encoding/xml"
5+
"os"
6+
"testing"
7+
"time"
8+
9+
"github.com/google/go-cmp/cmp"
10+
)
11+
12+
func TestAdvisoryDates(t *testing.T) {
13+
var tt = []struct{ Updated, Issued time.Time }{
14+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
15+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
16+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
17+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
18+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
19+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
20+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
21+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
22+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
23+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
24+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
25+
{time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 7, 0, 0, 0, 0, time.UTC)},
26+
//
27+
{time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC)},
28+
{time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC)},
29+
{time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC)},
30+
{time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC)},
31+
{time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC)},
32+
{time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 13, 0, 0, 0, 0, time.UTC)},
33+
//
34+
{time.Date(2019, time.Month(5), 14, 0, 0, 0, 0, time.UTC), time.Date(2019, time.Month(5), 14, 0, 0, 0, 0, time.UTC)},
35+
}
36+
37+
f, err := os.Open("../testdata/com.redhat.rhsa-RHEL8.xml")
38+
if err != nil {
39+
t.Fatal(err)
40+
}
41+
defer f.Close()
42+
43+
var root Root
44+
if err := xml.NewDecoder(f).Decode(&root); err != nil {
45+
t.Fatal(err)
46+
}
47+
for i, tc := range tt {
48+
def := &root.Definitions.Definitions[i]
49+
a := def.Advisory
50+
if got, want := a.Updated.Date, tc.Updated; !cmp.Equal(got, want) {
51+
t.Errorf("def #%d: %s: Updated\n%v", i, def.Title, cmp.Diff(got, want))
52+
}
53+
if got, want := a.Issued.Date, tc.Issued; !cmp.Equal(got, want) {
54+
t.Errorf("def #%d: %s: Issued\n%v", i, def.Title, cmp.Diff(got, want))
55+
}
56+
}
57+
}
58+
59+
func TestDebianDates(t *testing.T) {
60+
var tt = []time.Time{
61+
time.Date(2004, time.Month(10), 29, 0, 0, 0, 0, time.UTC),
62+
time.Date(2003, time.Month(6), 6, 0, 0, 0, 0, time.UTC),
63+
time.Date(2005, time.Month(2), 2, 0, 0, 0, 0, time.UTC),
64+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
65+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
66+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
67+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
68+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
69+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
70+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
71+
time.Date(2020, time.Month(4), 24, 0, 0, 0, 0, time.UTC),
72+
time.Date(2001, time.Month(7), 11, 0, 0, 0, 0, time.UTC),
73+
time.Date(2001, time.Month(8), 9, 0, 0, 0, 0, time.UTC),
74+
}
75+
76+
f, err := os.Open("../testdata/oval-definitions-buster.xml")
77+
if err != nil {
78+
t.Fatal(err)
79+
}
80+
defer f.Close()
81+
82+
var root Root
83+
if err := xml.NewDecoder(f).Decode(&root); err != nil {
84+
t.Fatal(err)
85+
}
86+
for i, tc := range tt {
87+
def := &root.Definitions.Definitions[i]
88+
a := def.Debian
89+
if got, want := a.Date.Date, tc; !cmp.Equal(got, want) {
90+
t.Errorf("def #%d: %s:\n%v", i, def.Title, cmp.Diff(got, want))
91+
}
92+
}
93+
}
94+
95+
func TestUbuntuDates(t *testing.T) {
96+
var tt = []time.Time{
97+
time.Time{}, // First entry has no date attached.
98+
time.Date(2015, time.Month(2), 23, 0, 0, 0, 0, time.UTC),
99+
time.Date(2007, time.Month(1), 16, 23, 28, 0, 0, time.UTC),
100+
time.Date(2007, time.Month(9), 26, 23, 17, 0, 0, time.UTC),
101+
time.Date(2008, time.Month(11), 18, 16, 0, 0, 0, time.UTC),
102+
time.Date(2008, time.Month(11), 18, 16, 0, 0, 0, time.UTC),
103+
time.Date(2008, time.Month(11), 18, 16, 0, 0, 0, time.UTC),
104+
time.Date(2008, time.Month(11), 18, 16, 0, 0, 0, time.UTC),
105+
time.Date(2018, time.Month(11), 18, 19, 29, 0, 0, time.UTC),
106+
time.Date(2009, time.Month(4), 23, 19, 30, 0, 0, time.UTC),
107+
}
108+
109+
f, err := os.Open("../testdata/com.ubuntu.bionic.cve.oval.xml")
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
defer f.Close()
114+
115+
var root Root
116+
if err := xml.NewDecoder(f).Decode(&root); err != nil {
117+
t.Fatal(err)
118+
}
119+
for i, tc := range tt {
120+
def := &root.Definitions.Definitions[i]
121+
a := def.Advisory
122+
if got, want := a.PublicDate.Date, tc; !cmp.Equal(got, want) {
123+
t.Errorf("def #%d: %s:\n%v", i, def.Title, cmp.Diff(got, want))
124+
}
125+
}
126+
}

oval/types.go

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/xml"
55
"fmt"
66
"sync"
7+
"time"
78
)
89

910
// ErrNotFound is returned by Lookup methods when the specified identifier is
@@ -92,15 +93,75 @@ type Advisory struct {
9293
Cves []Cve `xml:"cve"`
9394
Bugzillas []Bugzilla `xml:"bugzilla"`
9495
AffectedCPEList []string `xml:"affected_cpe_list>cpe"`
95-
Refs []Ref `xml:"ref"` // Ubuntu Only
96-
Bugs []Bug `xml:"bug"` // Ubuntu Only
97-
PublicDate string `xml:"public_date"` // Ubuntu Only
98-
Issued struct {
99-
Date string `xml:"date,attr"`
100-
} `xml:"issued"`
101-
Updated struct {
102-
Date string `xml:"date,attr"`
103-
} `xml:"updated"`
96+
Refs []Ref `xml:"ref"` // Ubuntu Only
97+
Bugs []Bug `xml:"bug"` // Ubuntu Only
98+
PublicDate Date `xml:"public_date"`
99+
Issued Date `xml:"issued"`
100+
Updated Date `xml:"updated"`
101+
}
102+
103+
// Date is a wrapper type for decoding a range of date, datestamp, and timestamp
104+
// strings seen in the wild.
105+
//
106+
// Currently, it will only examine attributes with the key "date".
107+
type Date struct {
108+
Date time.Time
109+
}
110+
111+
var (
112+
_ xml.Unmarshaler = (*Date)(nil)
113+
_ xml.UnmarshalerAttr = (*Date)(nil)
114+
)
115+
116+
// UnmarshalXML implements xml.Unmarshaler.
117+
func (d *Date) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {
118+
// Attempt attrs first:
119+
for _, a := range start.Attr {
120+
if err := d.UnmarshalXMLAttr(a); err == nil {
121+
break
122+
}
123+
}
124+
// Attempt the inner node:
125+
var s string
126+
if err := dec.DecodeElement(&s, &start); err != nil {
127+
return err
128+
}
129+
// If the date is set but an empty string is the inner element, then the
130+
// date was set by an attr.
131+
if s == "" && !d.Date.IsZero() {
132+
return nil
133+
}
134+
var err error
135+
// Try a variety of formats, because everything is terrible.
136+
for _, f := range []string{
137+
"2006-01-02", // Debian-style YYYY-MM-DD
138+
"2006-01-02 15:04:05 MST", // Ubuntu style YYYY-MM-DD time zone
139+
time.RFC1123, // The rest of these seem like someone might use them.
140+
time.RFC1123Z,
141+
time.RFC3339,
142+
time.RFC3339Nano,
143+
} {
144+
d.Date, err = time.Parse(f, s)
145+
if err == nil {
146+
return nil
147+
}
148+
}
149+
return fmt.Errorf("unable to decode string as datestamp: %q", s)
150+
}
151+
152+
// UnmarshalXMLAttr implements xml.UnmarshalerAttr.
153+
func (d *Date) UnmarshalXMLAttr(attr xml.Attr) error {
154+
const dsfmt = `2006-01-02`
155+
var name = xml.Name{Local: `date`}
156+
if attr.Name.Local != name.Local {
157+
return xml.UnmarshalError(fmt.Sprintf("unexpected attr : %v", attr))
158+
}
159+
var err error
160+
d.Date, err = time.Parse(dsfmt, attr.Value)
161+
if err != nil {
162+
return err
163+
}
164+
return nil
104165
}
105166

106167
// Ref : >definitions>definition>metadata>advisory>ref
@@ -143,7 +204,7 @@ type Bugzilla struct {
143204
type Debian struct {
144205
XMLName xml.Name `xml:"debian"`
145206
MoreInfo string `xml:"moreinfo"`
146-
Date string `xml:"date"`
207+
Date Date `xml:"date"`
147208
}
148209

149210
// Tests : >tests

0 commit comments

Comments
 (0)