From bc02cdde7024c528cb3ccb05b88329850a3ba512 Mon Sep 17 00:00:00 2001 From: Paul Duncan Date: Thu, 3 Feb 2022 01:00:45 -0500 Subject: internal/cpe: add binding and tests --- internal/cpe/binding.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 internal/cpe/binding.go (limited to 'internal/cpe/binding.go') diff --git a/internal/cpe/binding.go b/internal/cpe/binding.go new file mode 100644 index 0000000..8c51d7d --- /dev/null +++ b/internal/cpe/binding.go @@ -0,0 +1,118 @@ +package cpe + +import ( + "encoding/json" + "errors" + "fmt" + "strings" +) + +// Common Platform Enumeration binding. +type Binding struct { + Part Part // Part attribute (NISTIR7695 5.3.3.1). + Vendor AvString // Vendor attribute (NISTIR7695 5.3.3.2). + Product AvString // Product attribute (NISTIR7695 5.3.3.3). + Version AvString // Version attribute (NISTIR7695 5.3.3.4). + Update AvString // Update attribute (NISTIR7695 5.3.3.5). + Edition AvString // Edition attribute (NISTIR7695 5.3.3.6). + SwEdition AvString // Software edition attribute (NISTIR7695 5.3.3.7). + TargetSw AvString // Target software attribute (NISTIR7695 5.3.3.8). + TargetHw AvString // Target hardware attribute (NISTIR7695 5.3.3.9). + Lang AvString // Language attribute (NISTIR7695 5.3.3.10). + Other AvString // Other attribute (NISTIR7695 5.3.3.11). +} + +// cpe 2.3 formatted string prefix +var cpe23Prefix = "cpe:2.3:" + +// missing prefix error +var missingPrefix = errors.New("missing CPE 2.3 prefix") + +// Create CPE 2.3 binding from formatted string. +func NewBinding(s string) (Binding, error) { + // check prefix + if s[0:len(cpe23Prefix)] != cpe23Prefix { + return Binding{}, missingPrefix + } + + // tokenize string, check for error + toks, err := tokenize([]byte(s[len(cpe23Prefix):])) + if err != nil { + return Binding{}, err + } + + // check token count + if len(toks) != 11 { + err = fmt.Errorf("invalid attribute count: %d != 11", len(toks)) + return Binding{}, err + } + + // create part + part, err := newPart(toks[0]) + if err != nil { + return Binding{}, err + } + + // parse tokens into strings + strs := make([]AvString, len(toks) - 1) + for i, t := range(toks[1:]) { + if strs[i], err = newAvString(t); err != nil { + return Binding{}, err + } + } + + // build and return result + return Binding { + Part: part, + Vendor: strs[0], + Product: strs[1], + Version: strs[2], + Update: strs[3], + Edition: strs[4], + Lang: strs[5], + SwEdition: strs[6], + TargetSw: strs[7], + TargetHw: strs[8], + Other: strs[9], + }, nil +} + +func (v Binding) String() string { + return cpe23Prefix + strings.Join([]string { + v.Part.String(), + v.Vendor.String(), + v.Product.String(), + v.Version.String(), + v.Update.String(), + v.Edition.String(), + v.Lang.String(), + v.SwEdition.String(), + v.TargetSw.String(), + v.TargetHw.String(), + v.Other.String(), + }, ":") +} + +// Unmarshal CPE formatted string from JSON string. +func (me *Binding) UnmarshalJSON(b []byte) error { + // decode json string + var s string + if err := json.Unmarshal(b, &s); err != nil { + return err + } + + // create binding + binding, err := NewBinding(s) + if err != nil { + return err + } + + // save result, return success + *me = binding + return nil +} + +// Marshal CPE formatted string as JSON string. +func (v Binding) MarshalJSON() ([]byte, error) { + return json.Marshal(v.String()) +} -- cgit v1.2.3