aboutsummaryrefslogtreecommitdiff
path: root/internal/feed/cveid.go
diff options
context:
space:
mode:
authorPaul Duncan <pabs@pablotron.org>2022-02-01 17:21:30 -0500
committerPaul Duncan <pabs@pablotron.org>2022-02-01 17:21:30 -0500
commit639ebc03c5dd60e104f255d0b0f2fb45ec319799 (patch)
tree5423fb245f93d8bdf3eb3b39978e472fda566854 /internal/feed/cveid.go
parent3044aef20f4aecb7963e826b976c6d72a970cda1 (diff)
downloadcvez-639ebc03c5dd60e104f255d0b0f2fb45ec319799.tar.bz2
cvez-639ebc03c5dd60e104f255d0b0f2fb45ec319799.zip
add cveid, tests
Diffstat (limited to 'internal/feed/cveid.go')
-rw-r--r--internal/feed/cveid.go112
1 files changed, 112 insertions, 0 deletions
diff --git a/internal/feed/cveid.go b/internal/feed/cveid.go
new file mode 100644
index 0000000..8796029
--- /dev/null
+++ b/internal/feed/cveid.go
@@ -0,0 +1,112 @@
+package feed
+
+import (
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "strconv"
+)
+
+// CVE ID
+type CveId uint32
+
+var cveIdRe = regexp.MustCompile("\\ACVE-(\\d{4})-(\\d{1,8})\\z")
+
+// parse year component of CVE ID
+func parseCveIdYear(s string) (uint16, error) {
+ // parse year, check for error
+ year, err := strconv.ParseUint(s, 10, 16)
+ if err != nil {
+ return 0, err
+ }
+
+ // check bounds
+ if year < 2000 || year > 2127 {
+ return 0, fmt.Errorf("year out of bounds: %s", s)
+ }
+
+ // return value
+ return uint16(year), nil
+}
+
+// parse number component of CVE ID
+func parseCveIdNum(s string) (uint32, error) {
+ // parse number, check for error
+ num, err := strconv.ParseUint(s, 10, 32)
+ if err != nil {
+ return 0, err
+ }
+
+ // check bounds
+ if num > 0x01ffffff {
+ return 0, fmt.Errorf("number out of bounds: %d", num)
+ }
+
+ // return value
+ return uint32(num), nil
+}
+
+// Encode CVE ID as uint32.
+func encodeCveId(year uint16, num uint32) uint32 {
+ return uint32((uint32((year - 2000) & 0x7f) << 25) | (num & 0x01ffffff))
+}
+
+// Create CVE ID from string.
+func NewCveId(s string) (CveId, error) {
+ // match components, check for error
+ md := cveIdRe.FindStringSubmatch(s)
+ if len(md) != 3 {
+ return CveId(0), fmt.Errorf("invalid CVE ID: %s", s)
+ }
+
+ // parse year, check for error
+ year, err := parseCveIdYear(md[1])
+ if err != nil {
+ return CveId(0), err
+ }
+
+ // parse number, check for error
+ num, err := parseCveIdNum(md[2])
+ if err != nil {
+ return CveId(0), err
+ }
+
+ // encode and return result
+ return CveId(encodeCveId(year, num)), nil
+}
+
+// Unmarshal CVE ID from JSON.
+func (me *CveId) UnmarshalJSON(b []byte) error {
+ // decode string, check for error
+ var s string
+ if err := json.Unmarshal(b, &s); err != nil {
+ return err
+ }
+
+ // parse year, check for error
+ r, err := NewCveId(s)
+ if err != nil {
+ return err
+ }
+
+ // serialize ID
+ *me = r
+
+ // return success
+ return nil
+}
+
+// Get year component.
+func (me CveId) Year() uint16 {
+ return uint16((uint32(me) >> 25) & 0x7f) + 2000
+}
+
+// Get number component.
+func (me CveId) Number() uint32 {
+ return (uint32(me) & 0x01ffffff)
+}
+
+// Return string representation of CVE ID.
+func (me CveId) String() string {
+ return fmt.Sprintf("CVE-%04d-%04d", me.Year(), me.Number())
+}