aboutsummaryrefslogtreecommitdiff
path: root/cisa
diff options
context:
space:
mode:
Diffstat (limited to 'cisa')
-rw-r--r--cisa/catalog.go5
-rw-r--r--cisa/catalog_test.go3
-rw-r--r--cisa/date.go164
-rw-r--r--cisa/date_test.go374
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)
- }
- })
- }
-}