diff options
Diffstat (limited to 'cisa')
-rw-r--r-- | cisa/catalog.go | 5 | ||||
-rw-r--r-- | cisa/catalog_test.go | 3 | ||||
-rw-r--r-- | cisa/date.go | 164 | ||||
-rw-r--r-- | cisa/date_test.go | 374 |
4 files changed, 5 insertions, 541 deletions
diff --git a/cisa/catalog.go b/cisa/catalog.go index 38116f6..4e9cc1a 100644 --- a/cisa/catalog.go +++ b/cisa/catalog.go @@ -3,6 +3,7 @@ package cisa import ( "time" "github.com/pablotron/cvez/feed" + "github.com/pablotron/cvez/rfc3339" ) // Vulnerability entry. @@ -20,7 +21,7 @@ type Vulnerability struct { Name string `json:"vulnerabilityName"` // Date vulnerability was added to catalog. - DateAdded Date `json:"dateAdded"` + DateAdded rfc3339.Date `json:"dateAdded"` // Short description of vulnerability. ShortDescription string `json:"shortDescription"` @@ -29,7 +30,7 @@ type Vulnerability struct { RequiredAction string `json:"requiredAction"` // Date that required action is due. - DueDate Date `json:"dueDate"` + DueDate rfc3339.Date `json:"dueDate"` } // Known exploited vulnerabilities catalog. diff --git a/cisa/catalog_test.go b/cisa/catalog_test.go index b86d21c..1e4ba7e 100644 --- a/cisa/catalog_test.go +++ b/cisa/catalog_test.go @@ -4,6 +4,7 @@ import ( "compress/gzip" "encoding/json" "github.com/pablotron/cvez/feed" + "github.com/pablotron/cvez/rfc3339" "os" "reflect" "testing" @@ -13,7 +14,7 @@ import ( // catalog test data type catalogTestData struct { CveIds map[string]feed.CveId `json:"cves"` - Dates map[string]Date `json:"dates"` + Dates map[string]rfc3339.Date `json:"dates"` Times map[string]time.Time `json:"times"` } diff --git a/cisa/date.go b/cisa/date.go deleted file mode 100644 index b3b6528..0000000 --- a/cisa/date.go +++ /dev/null @@ -1,164 +0,0 @@ -package cisa - -import ( - "encoding/json" - "fmt" - "regexp" - "strconv" -) - -// YYYY-MM-DD -type Date uint16 - -// Returns true if the year is a leap year, and false otherwise. -// -// Reference: -// https://www.timeanddate.com/date/leapyear.html -func isLeapYear(y uint16) bool { - // leap year rules: - // 1. year is evenly divisible by 4 and is not evenly divisible by 100 - // 2. year is evenly divisible by 4 and is evenly divisible by 400 - return (((y % 4) == 0) && ((y % 100) != 0)) || // rule 1 - (((y % 4) == 0) && ((y % 400) == 0)) // rule 2 -} - -// maximum month days -var maxMonthDays = []uint16 { - 31, // jan - 28, // feb (incorrect for leap years!) - 31, // mar - 30, // apr - 31, // may - 30, // jun - 31, // jul - 31, // aug - 30, // sep - 31, // oct - 30, // nov - 31, // dec -} - -// Get the maximum day for the given year and month. -func getMaxMonthDay(y, m uint16) uint16 { - if m == 2 && isLeapYear(y) { - return 29 - } else { - return maxMonthDays[m - 1] - } -} - -// Parse date component and check range. -func parseDateComponent(name string, s []byte, min, max uint16) (uint16, error) { - // parse value - vr, err := strconv.ParseUint(string(s), 10, 32) - if err != nil { - return 0, err - } - - v := uint16(vr) - - // check range - if v < min || v > max { - return 0, fmt.Errorf("%s component out of range [%d, %d]: %d", name, v, min, max) - } - - return uint16(v), nil -} - -var dateRe = regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`) - -// Create date from byte slice. -func NewDate(s []byte) (Date, error) { - var r Date - - if !dateRe.Match(s) { - return r, fmt.Errorf("invalid date: \"%s\"", s) - } - - // parse year - y, err := parseDateComponent("year", s[0:4], 1999, 2126) - if err != nil { - return r, err - } - - // parse month - m, err := parseDateComponent("month", s[5:7], 1, 12) - if err != nil { - return r, err - } - - // parse day - d, err := parseDateComponent("month", s[8:10], 1, getMaxMonthDay(y, m)) - if err != nil { - return r, err - } - - // encode return date - r = Date((((y - 1999) & 0x3f) << 9) | - (((m - 1) & 0xf) << 5) | - ((d - 1) & 0x1f)) - - // return result - return r, nil -} - -// Get year, month, and day components. -func (me Date) GetComponents() (uint16, uint16, uint16) { - // extract components - y := uint16((uint16(me) >> 9) + 1999) - m := uint16(((uint16(me) >> 5) & 0xf) + 1) - d := uint16((uint16(me) & 0x1f) + 1) - - return y, m, d -} - -// Get year component. -func (me Date) Year() uint16 { - y, _, _ := me.GetComponents() - return y -} - -// Get month component. -func (me Date) Month() uint16 { - _, m, _ := me.GetComponents() - return m -} - -// Get day component. -func (me Date) Day() uint16 { - _, _, d := me.GetComponents() - return d -} - -// Convert to string. -func (me Date) String() string { - // extract date components - y, m, d := me.GetComponents() - - // return string - return fmt.Sprintf("%04d-%02d-%02d", y, m, d) -} - -// Unmarshal date from JSON string. -func (me *Date) UnmarshalJSON(b []byte) error { - // decode json string - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - - // create date - d, err := NewDate([]byte(s)) - if err != nil { - return err - } - - // save result, return success - *me = d - return nil -} - -// Marshal Date as JSON string. -func (d Date) MarshalJSON() ([]byte, error) { - return json.Marshal(d.String()) -} diff --git a/cisa/date_test.go b/cisa/date_test.go deleted file mode 100644 index 47e152e..0000000 --- a/cisa/date_test.go +++ /dev/null @@ -1,374 +0,0 @@ -package cisa - -import ( - "encoding/json" - "fmt" - "reflect" - "testing" -) - -func TestIsLeapYear(t *testing.T) { - tests := []struct { - val uint16 - exp bool - } { - { 1800, false }, - { 1900, false }, - { 1996, true }, - { 1997, false }, - { 1998, false }, - { 1999, false }, - { 2000, true }, - { 2001, false }, - { 2002, false }, - { 2003, false }, - { 2004, true }, - { 2005, false }, - { 2006, false }, - { 2007, false }, - { 2008, true }, - { 2009, false }, - { 2010, false }, - { 2011, false }, - { 2012, true }, - { 2013, false }, - { 2014, false }, - { 2015, false }, - { 2016, true }, - { 2017, false }, - { 2018, false }, - { 2019, false }, - { 2020, true }, - { 2021, false }, - { 2022, false }, - { 2023, false }, - { 2024, true }, - { 2100, false }, - { 2400, true }, - } - - for _, test := range(tests) { - t.Run(fmt.Sprintf("%d", test.val), func(t *testing.T) { - got := isLeapYear(test.val) - if got != test.exp { - t.Errorf("got %v, exp %v", got, test.exp) - } - }) - } -} - -func TestGetMaxMonthDay(t *testing.T) { - tests := []struct { - y uint16 - m uint16 - exp uint16 - } { - { 2000, 2, 29 }, // test leap year - { 2001, 2, 28 }, // test non-leap year - { 2022, 1, 31 }, // test full non-leap year - { 2022, 2, 28 }, - { 2022, 3, 31 }, - { 2022, 4, 30 }, - { 2022, 5, 31 }, - { 2022, 6, 30 }, - { 2022, 7, 31 }, - { 2022, 8, 31 }, - { 2022, 9, 30 }, - { 2022, 10, 31 }, - { 2022, 11, 30 }, - { 2022, 12, 31 }, - } - - for _, test := range(tests) { - t.Run(fmt.Sprintf("%04d-%02d", test.y, test.m), func(t *testing.T) { - got := getMaxMonthDay(test.y, test.m) - if got != test.exp { - t.Errorf("got %v, exp %v", got, test.exp) - } - }) - } -} - -func TestParseDateComponent(t *testing.T) { - tests := []struct { - name string - val string - min uint16 - max uint16 - exp bool - } { - { "bad-uint", "abcd", 2000, 2001, false }, - { "y-pass", "2000", 2000, 2001, true }, - { "y-fail-lo", "1999", 2000, 2001, false }, - { "y-fail-hi", "2002", 2000, 2001, false }, - { "m-pass-1", "1", 1, 12, true }, - { "m-pass-2", "2", 1, 12, true }, - { "m-pass-3", "3", 1, 12, true }, - { "m-pass-4", "4", 1, 12, true }, - { "m-pass-5", "5", 1, 12, true }, - { "m-pass-6", "6", 1, 12, true }, - { "m-pass-7", "7", 1, 12, true }, - { "m-pass-8", "8", 1, 12, true }, - { "m-pass-9", "9", 1, 12, true }, - { "m-pass-10", "10", 1, 12, true }, - { "m-pass-11", "11", 1, 12, true }, - { "m-pass-12", "12", 1, 12, true }, - { "m-fail-lo", "0", 1, 12, false }, - { "m-fail-hi", "13", 1, 12, false }, - { "d-pass-1", "1", 1, 31, true }, - { "d-pass-10", "10", 1, 31, true }, - { "d-pass-30", "10", 1, 31, true }, - { "d-fail-lo", "0", 1, 31, false }, - { "d-fail-hi", "32", 1, 31, false }, - } - - for _, test := range(tests) { - t.Run(test.name, func(t *testing.T) { - got, err := parseDateComponent("a", []byte(test.val), test.min, test.max) - if err != nil && test.exp == true { - t.Error(err) - } else if err == nil && test.exp == false { - t.Errorf("got %v, exp error", got) - } - }) - } -} - -func TestNewDate(t *testing.T) { - tests := []struct { - name string - val string - exp bool - } { - { "pass", "2000-01-03", true }, - { "pass-lo", "1999-01-01", true }, - { "pass-hi", "2126-12-31", true }, - { "fail", "asdf", false }, - { "fail-y-lo", "1998-01-03", false }, - { "fail-y-hi", "2128-01-03", false }, - { "fail-m-lo", "2126-00-03", false }, - { "fail-m-hi", "2126-13-03", false }, - { "fail-d-lo", "2126-01-00", false }, - { "fail-d-hi", "2126-01-32", false }, - { "fail-d-hi", "2126-02-29", false }, - { "fail-d-hi", "2126-03-32", false }, - { "fail-d-hi", "2126-04-31", false }, - { "fail-d-hi", "2126-05-32", false }, - { "fail-d-hi", "2126-06-31", false }, - } - - for _, test := range(tests) { - t.Run(test.name, func(t *testing.T) { - if got, err := NewDate([]byte(test.val)); err != nil && test.exp { - t.Error(err) - } else if err == nil && !test.exp { - t.Errorf("got %v, exp error", got) - } - }) - } -} - -func TestGetComponents(t *testing.T) { - type date struct { - y uint16 - m uint16 - d uint16 - } - - tests := []struct { - val string - exp date - } { - { "2022-10-31", date { 2022, 10, 31 } }, - } - - for _, test := range(tests) { - t.Run(test.val, func(t *testing.T) { - // create date - dt, err := NewDate([]byte(test.val)) - if err != nil { - t.Error(err) - return - } - - // get components - y, m, d := dt.GetComponents() - got := date { y, m, d } - - // check components - if !reflect.DeepEqual(got, test.exp) { - t.Errorf("got %v, exp %v", got, test.exp) - } - }) - } -} - -func TestYear(t *testing.T) { - tests := []struct { - val string - exp uint16 - } { - { "2022-10-31", 2022 }, - } - - for _, test := range(tests) { - t.Run(test.val, func(t *testing.T) { - // create date - dt, err := NewDate([]byte(test.val)) - if err != nil { - t.Error(err) - return - } - - // get year - got := dt.Year() - - // check components - if got != test.exp { - t.Errorf("got year %d, exp %d", got, test.exp) - } - }) - } -} - -func TestMonth(t *testing.T) { - tests := []struct { - val string - exp uint16 - } { - { "2022-10-31", 10 }, - } - - for _, test := range(tests) { - t.Run(test.val, func(t *testing.T) { - // create date - dt, err := NewDate([]byte(test.val)) - if err != nil { - t.Error(err) - return - } - - // get month - got := dt.Month() - - // check components - if got != test.exp { - t.Errorf("got month %d, exp %d", got, test.exp) - } - }) - } -} - -func TestDay(t *testing.T) { - tests := []struct { - val string - exp uint16 - } { - { "2022-10-31", 31 }, - } - - for _, test := range(tests) { - t.Run(test.val, func(t *testing.T) { - // create date - dt, err := NewDate([]byte(test.val)) - if err != nil { - t.Error(err) - return - } - - // get day - got := dt.Day() - - // check components - if got != test.exp { - t.Errorf("got day %d, exp %d", got, test.exp) - } - }) - } -} - -func TestString(t *testing.T) { - tests := []string { - "2022-10-31", - } - - for _, test := range(tests) { - t.Run(test, func(t *testing.T) { - // create date - dt, err := NewDate([]byte(test)) - if err != nil { - t.Error(err) - return - } - - // get/check string - got := dt.String() - if got != test { - t.Errorf("got %s, exp %s", got, test) - } - }) - } -} - -func TestDateUnmarshalJSON(t *testing.T) { - passTests := []struct { - val string - exp string - } { - { `"2022-02-03"`, "2022-02-03" }, - } - - for _, test := range(passTests) { - var got Date - if err := json.Unmarshal([]byte(test.val), &got); err != nil { - t.Error(err) - } else if got.String() != test.exp { - t.Errorf("got \"%s\", exp \"%s\"", got.String(), test.exp) - } - } - - failTests := []struct { - name string - val string - } { - { "fail-str", "asdf" }, - { "fail-parse", "\"asdf\"" }, - } - - for _, test := range(failTests) { - t.Run(test.name, func(t *testing.T) { - var got Date - if err := got.UnmarshalJSON([]byte(test.val)); err == nil { - t.Errorf("got \"%v\" exp error", got) - } - }) - } - -} - -func TestDateMarshalJSON(t *testing.T) { - tests := []struct { - val string - exp string - } { - { "2022-10-31", `"2022-10-31"` }, - } - - for _, test := range(tests) { - t.Run(test.val, func(t *testing.T) { - // create date - dt, err := NewDate([]byte(test.val)) - if err != nil { - t.Error(err) - return - } - - // get/check string - if got, err := dt.MarshalJSON(); err != nil { - t.Error(err) - } else if string(got) != test.exp { - t.Errorf("got \"%s\", exp \"%s\"", got, test.exp) - } - }) - } -} |