Skip to content

Commit

Permalink
incorporate bom link; add test files for components merge
Browse files Browse the repository at this point in the history
Signed-off-by: nscuro <nscuro@protonmail.com>
  • Loading branch information
nscuro committed Feb 25, 2022
1 parent 0fb04d7 commit c52251a
Show file tree
Hide file tree
Showing 18 changed files with 1,174 additions and 18 deletions.
69 changes: 69 additions & 0 deletions cyclonedx.go
Expand Up @@ -23,6 +23,9 @@ import (
"errors"
"fmt"
"io"

"github.com/google/uuid"
"github.com/gowebpki/jcs"
)

const (
Expand Down Expand Up @@ -164,6 +167,37 @@ type Component struct {
ReleaseNotes *ReleaseNotes `json:"releaseNotes,omitempty" xml:"releaseNotes,omitempty"`
}

func (c Component) bomReference() string {
return c.BOMRef
}

func (c *Component) setBOMReference(ref string) {
c.BOMRef = ref
}

// TODO: Can we solve this more elegantly?
type componentRefSeed Component

func (c componentRefSeed) MarshalJSON() ([]byte, error) {
c.BOMRef = ""

componentJSON, err := json.Marshal(Component(c))
if err != nil {
return nil, err
}

return jcs.Transform(componentJSON)
}

func (c Component) generateBOMReference() (string, error) {
componentJSON, err := json.Marshal(componentRefSeed(c))
if err != nil {
return "", err
}

return uuid.NewSHA1(uuid.MustParse("369fac08-d4a0-452e-b4b1-de87a0f376c6"), componentJSON).String(), nil
}

type Composition struct {
Aggregate CompositionAggregate `json:"aggregate" xml:"aggregate"`
Assemblies *[]BOMReference `json:"assemblies,omitempty" xml:"assemblies>assembly,omitempty"`
Expand Down Expand Up @@ -520,6 +554,17 @@ type Property struct {
Value string `json:"value" xml:",innerxml"`
}

// referrer is an internal utility interface that is used
// to address bom elements that have a BOM reference.
type referrer interface {
bomReference() string
setBOMReference(ref string)

// generateBOMReference returns a new value intended to be used as BOM reference.
// Given the same state of the referrer, generateBOMReference must return the same result.
generateBOMReference() (string, error)
}

type ReleaseNotes struct {
Type string `json:"type" xml:"type"`
Title string `json:"title,omitempty" xml:"title,omitempty"`
Expand Down Expand Up @@ -570,6 +615,18 @@ type Service struct {
ReleaseNotes *ReleaseNotes `json:"releaseNotes,omitempty" xml:"releaseNotes,omitempty"`
}

func (s Service) bomReference() string {
return s.BOMRef
}

func (s *Service) setBOMReference(ref string) {
s.BOMRef = ref
}

func (s Service) generateBOMReference() (string, error) {
return "", nil
}

type Severity string

const (
Expand Down Expand Up @@ -625,6 +682,18 @@ type Vulnerability struct {
Affects *[]Affects `json:"affects,omitempty" xml:"affects>target,omitempty"`
}

func (v Vulnerability) bomReference() string {
return v.BOMRef
}

func (v *Vulnerability) setBOMReference(ref string) {
v.BOMRef = ref
}

func (v Vulnerability) generateBOMReference() (string, error) {
return "", nil
}

type VulnerabilityAnalysis struct {
State ImpactAnalysisState `json:"state,omitempty" xml:"state,omitempty"`
Justification ImpactAnalysisJustification `json:"justification,omitempty" xml:"justification,omitempty"`
Expand Down
21 changes: 21 additions & 0 deletions cyclonedx_test.go
Expand Up @@ -20,6 +20,8 @@ package cyclonedx
import (
"encoding/json"
"encoding/xml"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -217,3 +219,22 @@ func TestLicenses_UnmarshalXML(t *testing.T) {
err = xml.Unmarshal([]byte("<Licenses><somethingElse>expressionValue</somethingElse></Licenses>"), licenses)
assert.Error(t, err)
}

func readTestBOM(t *testing.T, filePath string) *BOM {
format := BOMFileFormatJSON
if filepath.Ext(filePath) == ".xml" {
format = BOMFileFormatXML
}

file, err := os.Open(filePath)
require.NoError(t, err)
defer func() {
_ = file.Close()
}()

var bom BOM
err = NewBOMDecoder(file, format).Decode(&bom)
require.NoError(t, err)

return &bom
}
4 changes: 4 additions & 0 deletions go.mod
Expand Up @@ -4,5 +4,9 @@ go 1.15

require (
github.com/bradleyjkemp/cupaloy/v2 v2.7.0
github.com/google/go-cmp v0.5.7
github.com/google/uuid v1.3.0
github.com/gowebpki/jcs v1.0.0
github.com/mitchellh/copystructure v1.2.0
github.com/stretchr/testify v1.7.0
)
12 changes: 12 additions & 0 deletions go.sum
Expand Up @@ -3,13 +3,25 @@ github.com/bradleyjkemp/cupaloy/v2 v2.7.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1l
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gowebpki/jcs v1.0.0 h1:0pZtOgGetfH/L7yXb4KWcJqIyZNA43WXFyMd7ftZACw=
github.com/gowebpki/jcs v1.0.0/go.mod h1:CID1cNZ+sHp1CCpAR8mPf6QRtagFBgPJE0FCUQ6+BrI=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
99 changes: 99 additions & 0 deletions link.go
@@ -0,0 +1,99 @@
package cyclonedx

import (
"fmt"
"net/url"
"regexp"
"strconv"

"github.com/google/uuid"
)

var bomLinkRegex = regexp.MustCompile(`^urn:cdx:(?P<serialNumber>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\/(?P<version>[0-9]+)(?:#(?P<bomRef>[0-9a-zA-Z\-._~%!$&'()*+,;=:@\/?]+))?$`)

// IsBOMLink TODO
func IsBOMLink(s string) bool {
return bomLinkRegex.MatchString(s)
}

// BOMLink TODO
type BOMLink struct {
SerialNumber uuid.UUID // Serial number of the linked BOM
Version int // Version of the linked BOM
Reference string // Reference of the linked element
}

// NewBOMLink TODO
func NewBOMLink(bom *BOM, elem referrer) (*BOMLink, error) {
if bom == nil {
return nil, fmt.Errorf("bom is nil")
}
if bom.SerialNumber == "" {
return nil, fmt.Errorf("missing serial number")
}
if bom.Version < 1 {
return nil, fmt.Errorf("versions below 1 are not allowed")
}

serial, err := uuid.Parse(bom.SerialNumber)
if err != nil {
return nil, fmt.Errorf("invalid serial number: %w", err)
}

if elem == nil {
return &BOMLink{
SerialNumber: serial,
Version: bom.Version,
}, nil
}

return &BOMLink{
SerialNumber: serial,
Version: bom.Version,
Reference: elem.bomReference(),
}, nil
}

// String TODO
func (b BOMLink) String() string {
if b.Reference == "" {
return fmt.Sprintf("urn:cdx:%s/%d", b.SerialNumber, b.Version)
}

return fmt.Sprintf("urn:cdx:%s/%d#%s", b.SerialNumber, b.Version, url.QueryEscape(b.Reference))
}

// ParseBOMLink TODO
func ParseBOMLink(s string) (*BOMLink, error) {
matches := bomLinkRegex.FindStringSubmatch(s)
if len(matches) < 3 || len(matches) > 4 {
return nil, fmt.Errorf("")
}

serial, err := uuid.Parse(matches[1])
if err != nil {
return nil, fmt.Errorf("invalid serial number: %w", err)
}
version, err := strconv.Atoi(matches[2])
if err != nil {
return nil, fmt.Errorf("invalid version: %w", err)
}

if len(matches) == 4 {
bomRef, err := url.QueryUnescape(matches[3])
if err != nil {
return nil, fmt.Errorf("invalid reference: %w", err)
}

return &BOMLink{
SerialNumber: serial,
Version: version,
Reference: bomRef,
}, nil
}

return &BOMLink{
SerialNumber: serial,
Version: version,
}, nil
}
15 changes: 15 additions & 0 deletions link_test.go
@@ -0,0 +1,15 @@
package cyclonedx

import "testing"

func TestIsBOMLink(t *testing.T) {
// TODO
}

func TestNewBOMLink(t *testing.T) {
// TODO
}

func TestParseBOMLink(t *testing.T) {
// TODO
}

0 comments on commit c52251a

Please sign in to comment.