(#495) Update documentNamespace uniqueness for spdx-json output (#528)

* add unique namespace identifier

Signed-off-by: Christopher Angelo Phillips <christopher.phillips@anchore.com>
This commit is contained in:
Christopher Angelo Phillips 2021-10-05 13:10:49 -04:00 committed by GitHub
parent 4b7217f052
commit f47a6a88b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 35 additions and 4 deletions

View File

@ -4,6 +4,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"path"
"strings"
"time" "time"
"github.com/anchore/syft/internal" "github.com/anchore/syft/internal"
@ -12,8 +14,11 @@ import (
"github.com/anchore/syft/internal/version" "github.com/anchore/syft/internal/version"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
"github.com/google/uuid"
) )
const anchoreNamespace = "https://anchore.com/syft"
// SPDXJsonPresenter is a SPDX presentation object for the syft results (see https://github.com/spdx/spdx-spec) // SPDXJsonPresenter is a SPDX presentation object for the syft results (see https://github.com/spdx/spdx-spec)
type SPDXJsonPresenter struct { type SPDXJsonPresenter struct {
catalog *pkg.Catalog catalog *pkg.Catalog
@ -41,14 +46,25 @@ func (pres *SPDXJsonPresenter) Present(output io.Writer) error {
// newSPDXJsonDocument creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results. // newSPDXJsonDocument creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
func newSPDXJsonDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) spdx22.Document { func newSPDXJsonDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) spdx22.Document {
var name string uniqueID := uuid.Must(uuid.NewRandom())
var name, input, identifier string
switch srcMetadata.Scheme { switch srcMetadata.Scheme {
case source.ImageScheme: case source.ImageScheme:
name = srcMetadata.ImageMetadata.UserInput name = cleanSPDXName(srcMetadata.ImageMetadata.UserInput)
input = "image"
case source.DirectoryScheme: case source.DirectoryScheme:
name = srcMetadata.Path name = cleanSPDXName(srcMetadata.Path)
input = "dir"
} }
if name != "." {
identifier = path.Join(input, fmt.Sprintf("%s-%s", name, uniqueID.String()))
} else {
identifier = path.Join(input, uniqueID.String())
}
namespace := path.Join(anchoreNamespace, identifier)
packages, files, relationships := newSPDXJsonElements(catalog) packages, files, relationships := newSPDXJsonElements(catalog)
return spdx22.Document{ return spdx22.Document{
@ -67,7 +83,7 @@ func newSPDXJsonDocument(catalog *pkg.Catalog, srcMetadata source.Metadata) spdx
LicenseListVersion: spdxlicense.Version, LicenseListVersion: spdxlicense.Version,
}, },
DataLicense: "CC0-1.0", DataLicense: "CC0-1.0",
DocumentNamespace: fmt.Sprintf("https://anchore.com/syft/image/%s", srcMetadata.ImageMetadata.UserInput), DocumentNamespace: namespace,
Packages: packages, Packages: packages,
Files: files, Files: files,
Relationships: relationships, Relationships: relationships,
@ -113,3 +129,14 @@ func newSPDXJsonElements(catalog *pkg.Catalog) ([]spdx22.Package, []spdx22.File,
return packages, files, relationships return packages, files, relationships
} }
func cleanSPDXName(name string) string {
// remove # according to specification
name = strings.Replace(name, "#", "-", -1)
// remove : for url construction
name = strings.Replace(name, ":", "-", -1)
// clean relative pathing
return path.Clean(name)
}

View File

@ -31,6 +31,10 @@ func TestSPDXJSONImagePresenter(t *testing.T) {
func spdxJsonRedactor(s []byte) []byte { func spdxJsonRedactor(s []byte) []byte {
// each SBOM reports the time it was generated, which is not useful during snapshot testing // each SBOM reports the time it was generated, which is not useful during snapshot testing
s = regexp.MustCompile(`"created": .*`).ReplaceAll(s, []byte("redacted")) s = regexp.MustCompile(`"created": .*`).ReplaceAll(s, []byte("redacted"))
// each SBOM reports a unique documentNamespace when generated, this is not useful for snapshot testing
s = regexp.MustCompile(`"documentNamespace": .*`).ReplaceAll(s, []byte("redacted"))
// the license list will be updated periodically, the value here should not be directly tested in snapshot tests // the license list will be updated periodically, the value here should not be directly tested in snapshot tests
return regexp.MustCompile(`"licenseListVersion": .*`).ReplaceAll(s, []byte("redacted")) return regexp.MustCompile(`"licenseListVersion": .*`).ReplaceAll(s, []byte("redacted"))
} }