From a31ab0bced27eb9acd3889af8bdd027932159c03 Mon Sep 17 00:00:00 2001 From: "ritesh.noronha" Date: Fri, 16 Aug 2024 12:27:38 -0700 Subject: [PATCH 1/2] Add support for output spec version --- cmd/assemble.go | 18 +++++++++++++++++- pkg/assemble/cdx/interface.go | 15 +++++++++++++-- pkg/assemble/cdx/merge.go | 13 +++++++++++-- pkg/assemble/cdx/util.go | 11 +++++++++++ pkg/assemble/combiner.go | 2 ++ pkg/assemble/config.go | 33 ++++++++++++++++++++++++--------- pkg/assemble/interface.go | 3 +++ pkg/assemble/spdx/interface.go | 16 ++++++++++++++-- pkg/assemble/spdx/utils.go | 9 +++++++++ 9 files changed, 104 insertions(+), 16 deletions(-) diff --git a/cmd/assemble.go b/cmd/assemble.go index a68877b..0e2283d 100644 --- a/cmd/assemble.go +++ b/cmd/assemble.go @@ -59,7 +59,6 @@ Advanced Example: return err } - assembleParams.Ctx = &ctx return assemble.Assemble(assembleParams) }, @@ -80,6 +79,12 @@ func init() { assembleCmd.Flags().BoolP("assemblyMerge", "a", false, "assembly merge") assembleCmd.MarkFlagsMutuallyExclusive("flatMerge", "hierMerge", "assemblyMerge") + assembleCmd.Flags().BoolP("outputSpecCdx", "g", true, "output in cdx format") + assembleCmd.Flags().BoolP("outputSpecSpdx", "s", false, "output in spdx format") + assembleCmd.MarkFlagsMutuallyExclusive("outputSpecCdx", "outputSpecSpdx") + + assembleCmd.Flags().StringP("outputSpecVersion", "e", "", "spec version of the output sbom") + assembleCmd.Flags().BoolP("xml", "x", false, "output in xml format") assembleCmd.Flags().BoolP("json", "j", true, "output in json format") assembleCmd.MarkFlagsMutuallyExclusive("xml", "json") @@ -148,6 +153,17 @@ func extractArgs(cmd *cobra.Command, args []string) (*assemble.Params, error) { aParams.Json = false } + specVersion, _ := cmd.Flags().GetString("outputSpecVersion") + aParams.OutputSpecVersion = specVersion + + cdx, _ := cmd.Flags().GetBool("outputSpecCdx") + + if cdx { + aParams.OutputSpec = "cyclonedx" + } else { + aParams.OutputSpec = "spdx" + } + for _, arg := range args { if err := validatePath(arg); err != nil { return nil, err diff --git a/pkg/assemble/cdx/interface.go b/pkg/assemble/cdx/interface.go index d531aa6..1c77803 100644 --- a/pkg/assemble/cdx/interface.go +++ b/pkg/assemble/cdx/interface.go @@ -17,6 +17,7 @@ package cdx import ( "context" + "errors" "strings" cydx "github.com/CycloneDX/cyclonedx-go" @@ -97,8 +98,10 @@ type app struct { } type output struct { - FileFormat string - File string + FileFormat string + Spec string + SpecVersion string + File string } type input struct { @@ -128,6 +131,14 @@ func Merge(ms *MergeSettings) error { merger.loadBoms() merger.initOutBom() + if len(ms.Output.Spec) > 0 && ms.Output.Spec != "cyclonedx" { + return errors.New("invalid output spec") + } + + if len(ms.Output.SpecVersion) > 0 && !validSpecVersion(ms.Output.SpecVersion) { + return errors.New("invalid CycloneDX spec version") + } + if ms.Assemble.FlatMerge { return merger.flatMerge() } else if ms.Assemble.HierarchicalMerge { diff --git a/pkg/assemble/cdx/merge.go b/pkg/assemble/cdx/merge.go index 2eac96f..e2297c0 100644 --- a/pkg/assemble/cdx/merge.go +++ b/pkg/assemble/cdx/merge.go @@ -379,8 +379,17 @@ func (m *merge) writeSBOM() error { encoder.SetPretty(true) encoder.SetEscapeHTML(true) - if err := encoder.Encode(m.out); err != nil { - return err + + if m.settings.Output.SpecVersion == "" { + if err := encoder.Encode(m.out); err != nil { + return err + } + } else { + outputVersion := specVersionMap[m.settings.Output.SpecVersion] + + if err := encoder.EncodeVersion(m.out, outputVersion); err != nil { + return err + } } return nil diff --git a/pkg/assemble/cdx/util.go b/pkg/assemble/cdx/util.go index 56760f5..c52c750 100644 --- a/pkg/assemble/cdx/util.go +++ b/pkg/assemble/cdx/util.go @@ -31,6 +31,17 @@ import ( "sigs.k8s.io/release-utils/version" ) +var specVersionMap = map[string]cydx.SpecVersion{ + "1.4": cydx.SpecVersion1_4, + "1.5": cydx.SpecVersion1_5, + "1.6": cydx.SpecVersion1_6, +} + +func validSpecVersion(specVersion string) bool { + _, ok := specVersionMap[specVersion] + return ok +} + func newSerialNumber() string { u := uuid.New().String() diff --git a/pkg/assemble/combiner.go b/pkg/assemble/combiner.go index 104ea10..4bf070c 100644 --- a/pkg/assemble/combiner.go +++ b/pkg/assemble/combiner.go @@ -100,6 +100,8 @@ func toCDXMergerSettings(c *config) *cdx.MergeSettings { ms.Output.File = c.Output.file ms.Output.FileFormat = c.Output.FileFormat + ms.Output.Spec = c.Output.Spec + ms.Output.SpecVersion = c.Output.SpecVersion ms.App.Name = c.App.Name ms.App.Version = c.App.Version diff --git a/pkg/assemble/config.go b/pkg/assemble/config.go index 9b67912..7720ac4 100644 --- a/pkg/assemble/config.go +++ b/pkg/assemble/config.go @@ -32,6 +32,7 @@ import ( ) const DEFAULT_OUTPUT_SPEC = "cyclonedx" +const DEFAULT_OUTPUT_SPEC_VERSION = "1.6" const DEFAULT_OUTPUT_FILE_FORMAT = "json" const DEFAULT_OUTPUT_LICENSE = "CC0-1.0" @@ -71,9 +72,10 @@ type app struct { } type output struct { - Spec string `yaml:"spec"` - FileFormat string `yaml:"file_format"` - file string + Spec string `yaml:"spec"` + SpecVersion string `yaml:"spec_version"` + FileFormat string `yaml:"file_format"` + file string } type input struct { @@ -121,8 +123,9 @@ var defaultConfig = config{ Copyright: "[OPTIONAL]", }, Output: output{ - Spec: DEFAULT_OUTPUT_SPEC, - FileFormat: DEFAULT_OUTPUT_FILE_FORMAT, + Spec: DEFAULT_OUTPUT_SPEC, + SpecVersion: DEFAULT_OUTPUT_SPEC_VERSION, + FileFormat: DEFAULT_OUTPUT_FILE_FORMAT, }, Assemble: assemble{ FlatMerge: false, @@ -145,8 +148,9 @@ func DefaultConfig() { func newConfig() *config { return &config{ Output: output{ - Spec: DEFAULT_OUTPUT_SPEC, - FileFormat: DEFAULT_OUTPUT_FILE_FORMAT, + Spec: DEFAULT_OUTPUT_SPEC, + SpecVersion: DEFAULT_OUTPUT_SPEC_VERSION, + FileFormat: DEFAULT_OUTPUT_FILE_FORMAT, }, Assemble: assemble{ FlatMerge: false, @@ -197,6 +201,15 @@ func (c *config) readAndMerge(p *Params) error { if p.Xml { c.Output.FileFormat = "xml" } + + if p.OutputSpec != "" { + c.Output.Spec = strings.Trim(p.OutputSpec, " ") + } + + if p.OutputSpecVersion != "" { + c.Output.SpecVersion = strings.Trim(p.OutputSpecVersion, " ") + } + return nil } @@ -242,6 +255,7 @@ func (c *config) validate() error { c.App.CPE = sanitize(c.App.CPE) c.App.Copyright = sanitize(c.App.Copyright) c.Output.Spec = sanitize(c.Output.Spec) + c.Output.SpecVersion = sanitize(c.Output.SpecVersion) c.Output.FileFormat = sanitize(c.Output.FileFormat) for i := range c.App.Author { @@ -268,8 +282,9 @@ func (c *config) validate() error { } } - if c.Output.Spec == "" { - c.Output.Spec = DEFAULT_OUTPUT_SPEC + if c.Output.Spec == "" && c.Output.SpecVersion == "" { + c.Output.Spec = "" + c.Output.SpecVersion = "" } if c.Output.FileFormat == "" { diff --git a/pkg/assemble/interface.go b/pkg/assemble/interface.go index 34e638a..9382acc 100644 --- a/pkg/assemble/interface.go +++ b/pkg/assemble/interface.go @@ -36,6 +36,9 @@ type Params struct { Xml bool Json bool + + OutputSpec string + OutputSpecVersion string } func NewParams() *Params { diff --git a/pkg/assemble/spdx/interface.go b/pkg/assemble/spdx/interface.go index 6582baa..b92657e 100644 --- a/pkg/assemble/spdx/interface.go +++ b/pkg/assemble/spdx/interface.go @@ -18,6 +18,7 @@ package spdx import ( "context" + "errors" "github.com/spdx/tools-golang/spdx" ) @@ -88,8 +89,10 @@ type app struct { } type output struct { - FileFormat string - File string + FileFormat string + Spec string + SpecVersion string + File string } type input struct { @@ -114,6 +117,15 @@ type MergeSettings struct { } func Merge(ms *MergeSettings) error { + + if len(ms.Output.Spec) > 0 && ms.Output.Spec != "spdx" { + return errors.New("invalid output spec") + } + + if len(ms.Output.SpecVersion) > 0 && !validSpecVersion(ms.Output.SpecVersion) { + return errors.New("invalid CycloneDX spec version") + } + merger := newMerge(ms) merger.loadBoms() return merger.combinedMerge() diff --git a/pkg/assemble/spdx/utils.go b/pkg/assemble/spdx/utils.go index 43d949d..4d1c386 100644 --- a/pkg/assemble/spdx/utils.go +++ b/pkg/assemble/spdx/utils.go @@ -47,6 +47,15 @@ import ( const NOA = "NOASSERTION" +var specVersionMap = map[string]string{ + "2.3": v2_3.Version, +} + +func validSpecVersion(specVersion string) bool { + _, ok := specVersionMap[specVersion] + return ok +} + func loadBom(ctx context.Context, path string) (*v2_3.Document, error) { log := logger.FromContext(ctx) From 98a996a5a6a7eff1d3d811eb437022ebf8b37269 Mon Sep 17 00:00:00 2001 From: "ritesh.noronha" Date: Fri, 16 Aug 2024 12:32:20 -0700 Subject: [PATCH 2/2] update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b558440..1074dc7 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,10 @@ INTERLYNK_DISABLE_VERSION_CHECK=true sbomasm assemble -n "mega cdx app" -v "1.0. ```sh docker run -v .:/app/sboms/ ghcr.io/interlynk-io/sbomasm:v0.1.3 assemble -n "assemble cdx app" -v "v2.0.0" -t "application" -o /app/sboms/final-prod.cdx.json /app/sboms/one.cdx.json /app/sboms/two.cdx.json ``` +`CDX` assemble multiple SBOMs and limit output cyclonedx version +```sh +sbomasm assemble -n "mega cdx app" -v "1.0.0" -t "application" -e 1.4 -o final-product.cdx.json sbom1.json sbom2.json sbom3.json +``` # Features - SBOM format agnostic