diff --git a/config/example_config.json b/config/example_config.json
index 908fb07..b26e48d 100644
--- a/config/example_config.json
+++ b/config/example_config.json
@@ -1,4 +1,5 @@
{
+ "spdxVersion": "SPDX-2.2",
"documentName": "composed-1.0",
"packageName": "top-level-artifact",
"spdxID": "SPDXRef-composed-sbom-product",
@@ -12,5 +13,8 @@
"packageLicenseDeclared": "license, licenseRef or NOASSERTION",
"packageCopyrightText": "text or NOASSERTION",
"packageSupplier": "Organization or recognized author of product. Optional",
- "packageComment": "Any relevant comment. Optional"
+ "packageComment": "Any relevant comment. Optional",
+ "namespacePrefix": "https://spdx.org/spdxdocs/",
+ "creator": "sbom-composer-v0.1",
+ "creatorType": "Tool"
}
\ No newline at end of file
diff --git a/config/example_config.yaml b/config/example_config.yaml
index fc3bf17..a41dbda 100644
--- a/config/example_config.yaml
+++ b/config/example_config.yaml
@@ -1,6 +1,8 @@
# Copyright (c) 2022 VMware, Inc. All Rights Reserved.
# SPDX-License-Identifier: BSD-2-Clause
+# SPDX version the composed doc is output to
+spdxVersion: "SPDX-2.2"
# SBOM-Composer report for
documentName: "composed-1.0"
# Top Level Composed SBOM product
@@ -24,3 +26,11 @@ packageCopyrightText: ""
packageSupplier: "Example supplier"
# Any relevant comment. Optional
packageComment: "Example comment"
+
+# The following fields belong to SPDX config reference
+# Prefix used for DocumentNamespace
+NamespacePrefix: "https://spdx.org/spdxdocs/"
+# Composed SPDX SBOM creator
+Creator: "sbom-composer-v0.1"
+# Should be always set to "Tool"
+CreatorType: "Tool"
diff --git a/parser/build.go b/parser/build.go
index fb8d313..d2bcfc4 100644
--- a/parser/build.go
+++ b/parser/build.go
@@ -9,20 +9,26 @@ import (
"github.com/spdx/tools-golang/builder"
)
-func Build(spdxVersion string, dirRoot string, conf *Config) (*Document, error) {
- spdxDocRef := BuildVersion(spdxVersion, dirRoot, conf)
+func Build(dirRoot string, conf *Config) (*Document, error) {
+ spdxDocRef := BuildVersion(dirRoot, conf)
- UpdatePackages(SPDX_VERSION, &spdxDocRef, conf)
+ UpdatePackages(&spdxDocRef, conf)
doc := CreateDocument(&spdxDocRef, conf)
return doc, nil
}
-func BuildVersion(spdxVersion string, dirRoot string, conf *Config) SPDXDocRef {
+func BuildVersion(dirRoot string, conf *Config) SPDXDocRef {
res := SPDXDocRef{}
- switch spdxVersion {
- case "2.2":
+ switch conf.SPDXVersion {
+ case "SPDX-2.2":
var err error
- res.Doc2_2, err = builder.Build2_2(conf.PackageName, dirRoot, conf.SPDXConfigRef)
+ res.Doc2_2, err = builder.Build2_2(conf.PackageName, dirRoot, conf.SPDXConfigRef.Conf2_2)
+ if err != nil {
+ fmt.Printf("error while building spdx document reference for path %v with config %v, %v: %v\n", dirRoot, conf.PackageName, conf.SPDXConfigRef, err)
+ }
+ default:
+ var err error
+ res.Doc2_2, err = builder.Build2_2(conf.PackageName, dirRoot, conf.SPDXConfigRef.Conf2_2)
if err != nil {
fmt.Printf("error while building spdx document reference for path %v with config %v, %v: %v\n", dirRoot, conf.PackageName, conf.SPDXConfigRef, err)
}
@@ -33,7 +39,7 @@ func BuildVersion(spdxVersion string, dirRoot string, conf *Config) SPDXDocRef {
func GenerateComposedDoc(dirRoot string, output string, outFormat string, confFile string) error {
conf := LoadConfig(confFile)
- doc, err := Build(SPDX_VERSION, dirRoot, conf)
+ doc, err := Build(dirRoot, conf)
if err != nil {
return err
}
diff --git a/parser/build_test.go b/parser/build_test.go
index 799c103..732573c 100644
--- a/parser/build_test.go
+++ b/parser/build_test.go
@@ -7,6 +7,7 @@ import (
"fmt"
"testing"
+ "github.com/spdx/tools-golang/builder"
"github.com/spdx/tools-golang/spdx"
"github.com/stretchr/testify/assert"
)
@@ -14,7 +15,8 @@ import (
func TestGenerateComposedDoc(t *testing.T) {
fmt.Println("Testing Unmarshal JSON Config")
t.Run("Unmarshal Config:", func(t *testing.T) {
- input := []byte(`documentName: "composed-1.0"
+ input := []byte(`spdxVersion: "SPDX-2.2"
+documentName: "composed-1.0"
packageName: "top-level-artifact"
spdxID: "SPDXRef-DOCUMENT"
packageVersion: "1.0"
@@ -26,19 +28,28 @@ packageLicenseConcluded: "BSD-3-Clause"
packageLicenseDeclared: "BSD-3-Clause"
packageCopyrightText: ""
packageSupplier: "somesupplier"
-packageComment: "somecomment"`)
+packageComment: "somecomment"
+namespacePrefix: "https://spdx.org/spdxdocs/"
+creator: "sbom-composer-v0.1"
+creatorType: "Tool"`)
want := Document{
SPDXDocRef: &SPDXDocRef{
Doc2_2: &spdx.Document2_2{
- SPDXVersion: "SPDX-2.2",
DataLicense: "CC0-1.0",
SPDXIdentifier: "DOCUMENT",
DocumentName: "top-level-artifact",
DocumentNamespace: "https://spdx.org/spdxdocs/top-level-artifact-",
}},
ConfigDataRef: &Config{
- SPDXConfigRef: SPDXConfigReference,
+ SPDXVersion: "SPDX-2.2",
+ SPDXConfigRef: SPDXConfigRef{
+ Conf2_2: &builder.Config2_2{
+ NamespacePrefix: "https://spdx.org/spdxdocs/",
+ CreatorType: "Tool",
+ Creator: "sbom-composer-1.0",
+ },
+ },
PackageName: "top-level-artifact",
DocumentName: "composed-1.0",
SPDXID: "SPDXRef-DOCUMENT",
@@ -57,10 +68,10 @@ packageComment: "somecomment"`)
}
loadedConfig := createConfig(input)
- doc, err := Build(SPDX_VERSION, "../example_data/micro_sboms/tag_value", loadedConfig)
+ doc, err := Build("../example_data/micro_sboms/tag_value", loadedConfig)
assert.Equal(t, nil, err)
- assert.Equal(t, want.SPDXDocRef.Doc2_2.SPDXVersion, doc.SPDXDocRef.Doc2_2.SPDXVersion)
+ assert.Equal(t, want.ConfigDataRef.SPDXVersion, doc.SPDXDocRef.Doc2_2.SPDXVersion)
assert.Equal(t, want.SPDXDocRef.Doc2_2.DataLicense, doc.SPDXDocRef.Doc2_2.DataLicense)
assert.Equal(t, want.SPDXDocRef.Doc2_2.SPDXIdentifier, doc.SPDXDocRef.Doc2_2.SPDXIdentifier)
assert.Equal(t, want.SPDXDocRef.Doc2_2.DocumentName, doc.SPDXDocRef.Doc2_2.DocumentName)
diff --git a/parser/config.go b/parser/config.go
index 893b01a..a8f41d5 100644
--- a/parser/config.go
+++ b/parser/config.go
@@ -15,23 +15,26 @@ import (
)
// PackageChecksum is a unique identifier used to verify if all files
-// in the orginal package are unchanged, exluding SPDX documents.
+// in the original package are unchanged, excluding SPDX documents.
// Should be generated with SHA256.
type PackageChecksum struct {
SHA256 string
}
var NOASSERTION string = "NOASSERTION"
-var SPDXConfigReference *builder.Config2_2 = &builder.Config2_2{
- NamespacePrefix: "https://spdx.org/spdxdocs/", // TODO: move this to config
- CreatorType: "Tool",
- Creator: "sbom-composer-1.0", // TODO: automate taking the version
+
+type SPDXConfigRef struct {
+ Conf2_2 *builder.Config2_2
}
// Config is a collection of configuration settings for builder
// to create a composed document with.
type Config struct {
- SPDXConfigRef *builder.Config2_2
+ SPDXConfigRef SPDXConfigRef
+
+ // SPDXVersion is a configuration for the
+ // version that should be used as an output
+ SPDXVersion string `json:"spdxVersion"`
// DocumentName is an SBOM-Composer report
// for
@@ -73,6 +76,15 @@ type Config struct {
// PackageComment is any relevant comment in a section
PackageComment string `json:"packageComment,omitempty"`
+
+ // NamespacePrefix is a prefix used for DocumentNamespace
+ NamespacePrefix string `json:"namespacePrefix"`
+
+ // Creator is the composed SPDX SBOM creator
+ Creator string `json:"creator"`
+
+ // CreatorType refers to SPDX CreatorType field
+ CreatorType string `json:"creatorType"`
}
func UnmarshalJSONConfig(jsonData []byte) (*Config, error) {
@@ -111,9 +123,7 @@ func readConfFile(file string) []byte {
}
func createConfig(loadedData []byte) *Config {
- conf := Config{}
-
- conf.SPDXConfigRef = SPDXConfigReference
+ conf := &Config{}
mapData := make(map[string]interface{})
@@ -125,6 +135,8 @@ func createConfig(loadedData []byte) *Config {
dataByte, _ := json.Marshal(mapData)
_ = json.Unmarshal(dataByte, &conf)
+ conf = setSPDXConfigRef(conf)
+
if len(conf.PackageDownloadLocation) == 0 {
conf.PackageDownloadLocation = NOASSERTION
}
@@ -139,5 +151,27 @@ func createConfig(loadedData []byte) *Config {
}
conf.PackageChecksum.SHA256 = strings.Trim(conf.PackageChecksum.SHA256, "\"{}")
- return &conf
+ return conf
+}
+
+func setSPDXConfigRef(conf *Config) *Config {
+ switch conf.SPDXVersion {
+ case "SPDX-2.2":
+ conf.SPDXConfigRef = SPDXConfigRef{
+ Conf2_2: &builder.Config2_2{
+ NamespacePrefix: conf.NamespacePrefix,
+ CreatorType: conf.CreatorType,
+ Creator: conf.Creator,
+ },
+ }
+ default:
+ conf.SPDXConfigRef = SPDXConfigRef{
+ Conf2_2: &builder.Config2_2{
+ NamespacePrefix: conf.NamespacePrefix,
+ CreatorType: conf.CreatorType,
+ Creator: conf.Creator,
+ },
+ }
+ }
+ return conf
}
diff --git a/parser/config_test.go b/parser/config_test.go
index 2fecedd..f48f807 100644
--- a/parser/config_test.go
+++ b/parser/config_test.go
@@ -12,11 +12,11 @@ import (
"github.com/stretchr/testify/assert"
)
-func generateJSONTConfigExample(documentName string, packageName string, spdxID string, packageVersion string,
+func generateJSONTConfigExample(spdxVersion string, documentName string, packageName string, spdxID string, packageVersion string,
packageDownloadLocation string, packageChecksum PackageChecksum, filesAnalyzed bool, packageLicenseConcluded string,
packageLicenseDeclared string, packageCopyrightText string, packageSupplier string, packageComment string) []byte {
- jsonStr := fmt.Sprintf("{\"documentName\":\"%s\",\"packageName\":\"%s\",\"spdxID\":\"%s\",\"packageVersion\":\"%s\",\"packageDownloadLocation\":\"%s\",\"filesAnalyzed\":\"%t\",\"packageChecksum\":\"%s\", \"packageLicenseConcluded\":\"%s\", \"packageLicenseDeclared\":\"%s\", \"packageCopyrightText\":\"%s\", \"packageSupplier\":\"%s\", \"packageComment\":\"%s\"}",
- documentName, packageName, spdxID, packageVersion, packageDownloadLocation, filesAnalyzed, packageChecksum,
+ jsonStr := fmt.Sprintf("{\"spdxVersion\":\"%s\",\"documentName\":\"%s\",\"packageName\":\"%s\",\"spdxID\":\"%s\",\"packageVersion\":\"%s\",\"packageDownloadLocation\":\"%s\",\"filesAnalyzed\":\"%t\",\"packageChecksum\":\"%s\", \"packageLicenseConcluded\":\"%s\", \"packageLicenseDeclared\":\"%s\", \"packageCopyrightText\":\"%s\", \"packageSupplier\":\"%s\", \"packageComment\":\"%s\"}",
+ spdxVersion, documentName, packageName, spdxID, packageVersion, packageDownloadLocation, filesAnalyzed, packageChecksum,
packageLicenseConcluded, packageLicenseDeclared, packageCopyrightText, packageSupplier, packageComment)
return []byte(jsonStr)
@@ -37,11 +37,12 @@ func TestUnmarshalJSONConfig(t *testing.T) {
pch := PackageChecksum{SHA256: checksum}
- jsonData := generateJSONTConfigExample("composed-1.0", "top-level-artifact", "SPDXRef-DOCUMENT", "1.0", NOASSERTION, pch,
+ jsonData := generateJSONTConfigExample("SPDX-2.2", "composed-1.0", "top-level-artifact", "SPDXRef-DOCUMENT", "1.0", NOASSERTION, pch,
false, "BSD-3-Clause", "BSD-3-Clause", NOASSERTION,
"somesupplier", "somecomment")
conf, _ := UnmarshalJSONConfig(jsonData)
+ assert.Equal(t, "SPDX-2.2", conf.SPDXVersion)
assert.Equal(t, "composed-1.0", conf.DocumentName)
assert.Equal(t, "top-level-artifact", conf.PackageName)
assert.Equal(t, "SPDXRef-DOCUMENT", conf.SPDXID)
diff --git a/parser/document.go b/parser/document.go
index 7155136..1af3b4c 100644
--- a/parser/document.go
+++ b/parser/document.go
@@ -29,9 +29,16 @@ func CreateDocumentWithSPDXRef() *Document {
return doc
}
-func UpdatePackages(spdxVersion string, spdxDocRef *SPDXDocRef, conf *Config) {
- switch spdxVersion {
- case "2.2":
+func UpdatePackages(spdxDocRef *SPDXDocRef, conf *Config) {
+ switch conf.SPDXVersion {
+ case "SPDX-2.2":
+ for i := range spdxDocRef.Doc2_2.Packages {
+ if spdxDocRef.Doc2_2.Packages[i].PackageName == conf.PackageName &&
+ len(spdxDocRef.Doc2_2.Packages[i].PackageVersion) == 0 {
+ spdxDocRef.Doc2_2.Packages[i].PackageVersion = conf.PackageVersion
+ }
+ }
+ default:
for i := range spdxDocRef.Doc2_2.Packages {
if spdxDocRef.Doc2_2.Packages[i].PackageName == conf.PackageName &&
len(spdxDocRef.Doc2_2.Packages[i].PackageVersion) == 0 {
diff --git a/parser/read_config_test.go b/parser/read_config_test.go
index 4addff9..e816307 100644
--- a/parser/read_config_test.go
+++ b/parser/read_config_test.go
@@ -15,7 +15,8 @@ func TestLoadConfigYAML(t *testing.T) {
t.Run("Loading YAML Config:", func(t *testing.T) {
conf := LoadConfig("../config/example_config.yaml")
- fmt.Println(conf)
+
+ assert.Equal(t, "SPDX-2.2", conf.SPDXVersion)
assert.Equal(t, "composed-1.0", conf.DocumentName)
assert.Equal(t, "top-level-artifact", conf.PackageName)
assert.Equal(t, "SPDXRef-DOCUMENT", conf.SPDXID)
diff --git a/parser/save.go b/parser/save.go
index a9884d2..9ca1289 100644
--- a/parser/save.go
+++ b/parser/save.go
@@ -13,9 +13,6 @@ import (
"github.com/spdx/tools-golang/tvsaver"
)
-// TODO: Make configurable
-var SPDX_VERSION = "2.2"
-
func Save(doc *Document, composableDocs []*Document, output string, outFormat string) error {
output = updateFileExtension(output, outFormat)
@@ -29,17 +26,17 @@ func Save(doc *Document, composableDocs []*Document, output string, outFormat st
// It's not necessary for the composed doc to
// contain all merged documents as Files
- doc = cleanDocumentFileData(SPDX_VERSION, doc)
+ doc = cleanDocumentFileData(doc)
- updateRelationships(SPDX_VERSION, doc, composableDocs)
+ updateRelationships(doc, composableDocs)
for _, cdoc := range composableDocs {
if cdoc != nil {
- AppendComposableDocument(SPDX_VERSION, doc, cdoc, w, outFormat)
+ AppendComposableDocument(doc, cdoc, w, outFormat)
}
}
- err = SaveVersion(SPDX_VERSION, outFormat, doc, w)
+ err = SaveVersion(outFormat, doc, w)
if err != nil {
fmt.Printf("error while saving %v: %v\n", output, err)
return err
@@ -47,19 +44,19 @@ func Save(doc *Document, composableDocs []*Document, output string, outFormat st
return nil
}
-func SaveVersion(version string, format string, doc *Document, w *os.File) error {
+func SaveVersion(format string, doc *Document, w *os.File) error {
switch format {
case "tv":
- if version == "2.2" {
+ if doc.ConfigDataRef.SPDXVersion == "SPDX-2.2" {
return tvsaver.Save2_2(doc.SPDXDocRef.Doc2_2, w)
}
case "json":
- if version == "2.2" {
+ if doc.ConfigDataRef.SPDXVersion == "SPDX-2.2" {
return spdx_json.Save2_2(doc.SPDXDocRef.Doc2_2, w)
}
default:
fmt.Printf("warn: %s is not proper output format; saving to default\n", format)
- if version == "2.2" {
+ if doc.ConfigDataRef.SPDXVersion == "SPDX-2.2" {
return tvsaver.Save2_2(doc.SPDXDocRef.Doc2_2, w)
}
}
@@ -69,10 +66,10 @@ func SaveVersion(version string, format string, doc *Document, w *os.File) error
// RenderComposableDocument processes a composable document
// and renders it to the composed document
-func AppendComposableDocument(spdxVersion string, res *Document, cdoc *Document, w io.Writer, outFormat string) {
+func AppendComposableDocument(res *Document, cdoc *Document, w io.Writer, outFormat string) {
- switch spdxVersion {
- case "2.2":
+ switch res.ConfigDataRef.SPDXVersion {
+ case "SPDX-2.2":
res.SPDXDocRef.Doc2_2.Annotations = append(res.SPDXDocRef.Doc2_2.Annotations, cdoc.SPDXDocRef.Doc2_2.Annotations...)
res.SPDXDocRef.Doc2_2.ExternalDocumentReferences = append(res.SPDXDocRef.Doc2_2.ExternalDocumentReferences, cdoc.SPDXDocRef.Doc2_2.ExternalDocumentReferences...)
res.SPDXDocRef.Doc2_2.Files = append(res.SPDXDocRef.Doc2_2.Files, cdoc.SPDXDocRef.Doc2_2.Files...)
@@ -84,9 +81,15 @@ func AppendComposableDocument(spdxVersion string, res *Document, cdoc *Document,
}
}
-func cleanDocumentFileData(spdxVersion string, doc *Document) *Document {
- switch spdxVersion {
- case "2.2":
+func cleanDocumentFileData(doc *Document) *Document {
+ switch doc.ConfigDataRef.SPDXVersion {
+ case "SPDX-2.2":
+ doc.SPDXDocRef.Doc2_2.Files = []*spdx.File2_2{}
+
+ for i := range doc.SPDXDocRef.Doc2_2.Packages {
+ doc.SPDXDocRef.Doc2_2.Packages[i].Files = []*spdx.File2_2{}
+ }
+ default:
doc.SPDXDocRef.Doc2_2.Files = []*spdx.File2_2{}
for i := range doc.SPDXDocRef.Doc2_2.Packages {
@@ -97,12 +100,26 @@ func cleanDocumentFileData(spdxVersion string, doc *Document) *Document {
return doc
}
-func updateRelationships(spdxVersion string, doc *Document, composableDocs []*Document) (*Document, []*Document) {
+func updateRelationships(doc *Document, composableDocs []*Document) (*Document, []*Document) {
- rootDocElID := setDocElID(spdxVersion, doc)
+ rootDocElID := setDocElID(doc)
for _, cdoc := range composableDocs {
- switch spdxVersion {
- case "2.2":
+ switch cdoc.ConfigDataRef.SPDXVersion {
+ case "SPDX-2.2":
+ if cdoc != nil && len(cdoc.SPDXDocRef.Doc2_2.Packages) > 0 {
+ elId := spdx.MakeDocElementID("",
+ fmt.Sprintf("%s-%s", cdoc.SPDXDocRef.Doc2_2.Packages[0].PackageName, cdoc.SPDXDocRef.Doc2_2.Packages[0].PackageVersion))
+ newRelationship := &spdx.Relationship2_2{
+ RefA: rootDocElID,
+ RefB: elId,
+ Relationship: "DESCRIBES",
+ }
+ doc.SPDXDocRef.Doc2_2.Relationships = append(doc.SPDXDocRef.Doc2_2.Relationships, newRelationship)
+ }
+ if cdoc != nil && len(cdoc.SPDXDocRef.Doc2_2.Relationships) > 0 {
+ cdoc.SPDXDocRef.Doc2_2.Relationships = cdoc.SPDXDocRef.Doc2_2.Relationships[1:]
+ }
+ default:
if cdoc != nil && len(cdoc.SPDXDocRef.Doc2_2.Packages) > 0 {
elId := spdx.MakeDocElementID("",
fmt.Sprintf("%s-%s", cdoc.SPDXDocRef.Doc2_2.Packages[0].PackageName, cdoc.SPDXDocRef.Doc2_2.Packages[0].PackageVersion))
@@ -122,10 +139,10 @@ func updateRelationships(spdxVersion string, doc *Document, composableDocs []*Do
return doc, composableDocs
}
-func setDocElID(spdxVersion string, doc *Document) spdx.DocElementID {
+func setDocElID(doc *Document) spdx.DocElementID {
rootDocElID := spdx.DocElementID{}
- switch spdxVersion {
- case "2.2":
+ switch doc.ConfigDataRef.SPDXVersion {
+ case "SPDX-2.2":
if len(doc.SPDXDocRef.Doc2_2.Packages) > 0 {
rootDocElID = spdx.MakeDocElementID("",
fmt.Sprintf("%s-%s", doc.SPDXDocRef.Doc2_2.Packages[0].PackageName, doc.SPDXDocRef.Doc2_2.Packages[0].PackageVersion))