mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Suport SPDX SBOM decoding (#738)
This commit is contained in:
parent
ca081ae5e0
commit
76f8205936
2
go.mod
2
go.mod
@ -35,7 +35,7 @@ require (
|
|||||||
github.com/scylladb/go-set v1.0.2
|
github.com/scylladb/go-set v1.0.2
|
||||||
github.com/sergi/go-diff v1.1.0
|
github.com/sergi/go-diff v1.1.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spdx/tools-golang v0.1.0
|
github.com/spdx/tools-golang v0.2.0
|
||||||
github.com/spf13/afero v1.6.0
|
github.com/spf13/afero v1.6.0
|
||||||
github.com/spf13/cobra v1.2.1
|
github.com/spf13/cobra v1.2.1
|
||||||
github.com/spf13/pflag v1.0.5
|
github.com/spf13/pflag v1.0.5
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -759,8 +759,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
|
|||||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
|
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
|
||||||
github.com/spdx/tools-golang v0.1.0 h1:iDMNEPqQk6CdiDj6eWDIDw85j0wQ3IR3pH9p0X05TSQ=
|
github.com/spdx/tools-golang v0.2.0 h1:KBNcw7xvVycRWeCWZK/5xQJA+plymW1+rTCs8ekJDro=
|
||||||
github.com/spdx/tools-golang v0.1.0/go.mod h1:RO4Y3IFROJnz+43JKm1YOrbtgQNljW4gAPpA/sY2eqo=
|
github.com/spdx/tools-golang v0.2.0/go.mod h1:RO4Y3IFROJnz+43JKm1YOrbtgQNljW4gAPpA/sY2eqo=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
|
||||||
|
|||||||
@ -2,6 +2,9 @@ package spdxhelpers
|
|||||||
|
|
||||||
import "github.com/anchore/syft/syft/pkg"
|
import "github.com/anchore/syft/syft/pkg"
|
||||||
|
|
||||||
|
const NONE = "NONE"
|
||||||
|
const NOASSERTION = "NOASSERTION"
|
||||||
|
|
||||||
func DownloadLocation(p pkg.Package) string {
|
func DownloadLocation(p pkg.Package) string {
|
||||||
// 3.7: Package Download Location
|
// 3.7: Package Download Location
|
||||||
// Cardinality: mandatory, one
|
// Cardinality: mandatory, one
|
||||||
@ -19,5 +22,5 @@ func DownloadLocation(p pkg.Package) string {
|
|||||||
return NoneIfEmpty(metadata.URL)
|
return NoneIfEmpty(metadata.URL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "NOASSERTION"
|
return NOASSERTION
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ func Test_DownloadLocation(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "no metadata",
|
name: "no metadata",
|
||||||
input: pkg.Package{},
|
input: pkg.Package{},
|
||||||
expected: "NOASSERTION",
|
expected: NOASSERTION,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "from apk",
|
name: "from apk",
|
||||||
@ -43,7 +43,7 @@ func Test_DownloadLocation(t *testing.T) {
|
|||||||
URL: "",
|
URL: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: "NONE",
|
expected: NONE,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package model
|
package spdxhelpers
|
||||||
|
|
||||||
type ReferenceCategory string
|
type ReferenceCategory string
|
||||||
|
|
||||||
@ -1,51 +1,26 @@
|
|||||||
package spdxhelpers
|
package spdxhelpers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExternalRefs(p pkg.Package) (externalRefs []model.ExternalRef) {
|
func ExternalRefs(p pkg.Package) (externalRefs []ExternalRef) {
|
||||||
externalRefs = make([]model.ExternalRef, 0)
|
externalRefs = make([]ExternalRef, 0)
|
||||||
|
|
||||||
for _, c := range p.CPEs {
|
for _, c := range p.CPEs {
|
||||||
externalRefs = append(externalRefs, model.ExternalRef{
|
externalRefs = append(externalRefs, ExternalRef{
|
||||||
ReferenceCategory: model.SecurityReferenceCategory,
|
ReferenceCategory: SecurityReferenceCategory,
|
||||||
ReferenceLocator: pkg.CPEString(c),
|
ReferenceLocator: pkg.CPEString(c),
|
||||||
ReferenceType: model.Cpe23ExternalRefType,
|
ReferenceType: Cpe23ExternalRefType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.PURL != "" {
|
if p.PURL != "" {
|
||||||
externalRefs = append(externalRefs, model.ExternalRef{
|
externalRefs = append(externalRefs, ExternalRef{
|
||||||
ReferenceCategory: model.PackageManagerReferenceCategory,
|
ReferenceCategory: PackageManagerReferenceCategory,
|
||||||
ReferenceLocator: p.PURL,
|
ReferenceLocator: p.PURL,
|
||||||
ReferenceType: model.PurlExternalRefType,
|
ReferenceType: PurlExternalRefType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return externalRefs
|
return externalRefs
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractPURL(refs []model.ExternalRef) string {
|
|
||||||
for _, r := range refs {
|
|
||||||
if r.ReferenceType == model.PurlExternalRefType {
|
|
||||||
return r.ReferenceLocator
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractCPEs(refs []model.ExternalRef) (cpes []pkg.CPE) {
|
|
||||||
for _, r := range refs {
|
|
||||||
if r.ReferenceType == model.Cpe23ExternalRefType {
|
|
||||||
cpe, err := pkg.NewCPE(r.ReferenceLocator)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("unable to extract SPDX CPE=%q: %+v", r.ReferenceLocator, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cpes = append(cpes, cpe)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cpes
|
|
||||||
}
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package spdxhelpers
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -13,7 +12,7 @@ func Test_ExternalRefs(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input pkg.Package
|
input pkg.Package
|
||||||
expected []model.ExternalRef
|
expected []ExternalRef
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "cpe + purl",
|
name: "cpe + purl",
|
||||||
@ -23,16 +22,16 @@ func Test_ExternalRefs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
PURL: "a-purl",
|
PURL: "a-purl",
|
||||||
},
|
},
|
||||||
expected: []model.ExternalRef{
|
expected: []ExternalRef{
|
||||||
{
|
{
|
||||||
ReferenceCategory: model.SecurityReferenceCategory,
|
ReferenceCategory: SecurityReferenceCategory,
|
||||||
ReferenceLocator: pkg.CPEString(testCPE),
|
ReferenceLocator: pkg.CPEString(testCPE),
|
||||||
ReferenceType: model.Cpe23ExternalRefType,
|
ReferenceType: Cpe23ExternalRefType,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ReferenceCategory: model.PackageManagerReferenceCategory,
|
ReferenceCategory: PackageManagerReferenceCategory,
|
||||||
ReferenceLocator: "a-purl",
|
ReferenceLocator: "a-purl",
|
||||||
ReferenceType: model.PurlExternalRefType,
|
ReferenceType: PurlExternalRefType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
17
internal/formats/common/spdxhelpers/file_type.go
Normal file
17
internal/formats/common/spdxhelpers/file_type.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package spdxhelpers
|
||||||
|
|
||||||
|
type FileType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
DocumentationFileType FileType = "DOCUMENTATION" // if the file serves as documentation
|
||||||
|
ImageFileType FileType = "IMAGE" // if the file is associated with a picture image file (MIME type of image/*, e.g., .jpg, .gif)
|
||||||
|
VideoFileType FileType = "VIDEO" // if the file is associated with a video file type (MIME type of video/*)
|
||||||
|
ArchiveFileType FileType = "ARCHIVE" // if the file represents an archive (.tar, .jar, etc.)
|
||||||
|
SpdxFileType FileType = "SPDX" // if the file is an SPDX document
|
||||||
|
ApplicationFileType FileType = "APPLICATION" // if the file is associated with a specific application type (MIME type of application/*)
|
||||||
|
SourceFileType FileType = "SOURCE" // if the file is human readable source code (.c, .html, etc.)
|
||||||
|
BinaryFileType FileType = "BINARY" // if the file is a compiled object, target image or binary executable (.o, .a, etc.)
|
||||||
|
TextFileType FileType = "TEXT" // if the file is human readable text file (MIME type of text/*)
|
||||||
|
AudioFileType FileType = "AUDIO" // if the file is associated with an audio file (MIME type of audio/* , e.g. .mp3)
|
||||||
|
OtherFileType FileType = "OTHER" // if the file doesn't fit into the above categories (generated artifacts, data files, etc.)
|
||||||
|
)
|
||||||
@ -18,7 +18,7 @@ func License(p pkg.Package) string {
|
|||||||
// (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so).
|
// (iii) the SPDX file creator has intentionally provided no information (no meaning should be implied by doing so).
|
||||||
|
|
||||||
if len(p.Licenses) == 0 {
|
if len(p.Licenses) == 0 {
|
||||||
return "NONE"
|
return NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
// take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/
|
// take all licenses and assume an AND expression; for information about license expressions see https://spdx.github.io/spdx-spec/appendix-IV-SPDX-license-expressions/
|
||||||
@ -30,7 +30,7 @@ func License(p pkg.Package) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(parsedLicenses) == 0 {
|
if len(parsedLicenses) == 0 {
|
||||||
return "NOASSERTION"
|
return NOASSERTION
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(parsedLicenses, " AND ")
|
return strings.Join(parsedLicenses, " AND ")
|
||||||
|
|||||||
@ -16,7 +16,7 @@ func Test_License(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "no licenses",
|
name: "no licenses",
|
||||||
input: pkg.Package{},
|
input: pkg.Package{},
|
||||||
expected: "NONE",
|
expected: NONE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no SPDX licenses",
|
name: "no SPDX licenses",
|
||||||
@ -25,7 +25,7 @@ func Test_License(t *testing.T) {
|
|||||||
"made-up",
|
"made-up",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: "NOASSERTION",
|
expected: NOASSERTION,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with SPDX license",
|
name: "with SPDX license",
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
func NoneIfEmpty(value string) string {
|
func NoneIfEmpty(value string) string {
|
||||||
if strings.TrimSpace(value) == "" {
|
if strings.TrimSpace(value) == "" {
|
||||||
return "NONE"
|
return NONE
|
||||||
}
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,17 +20,17 @@ func Test_noneIfEmpty(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
value: "",
|
value: "",
|
||||||
expected: "NONE",
|
expected: NONE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "space",
|
name: "space",
|
||||||
value: " ",
|
value: " ",
|
||||||
expected: "NONE",
|
expected: NONE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "tab",
|
name: "tab",
|
||||||
value: "\t",
|
value: "\t",
|
||||||
expected: "NONE",
|
expected: NONE,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|||||||
173
internal/formats/common/spdxhelpers/relationship_type.go
Normal file
173
internal/formats/common/spdxhelpers/relationship_type.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package spdxhelpers
|
||||||
|
|
||||||
|
// source: https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/
|
||||||
|
type RelationshipType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DescribedByRelationship is to be used when SPDXRef-A is described by SPDXREF-Document.
|
||||||
|
// Example: The package 'WildFly' is described by SPDX document WildFly.spdx.
|
||||||
|
DescribedByRelationship RelationshipType = "DESCRIBED_BY"
|
||||||
|
|
||||||
|
// ContainsRelationship is to be used when SPDXRef-A contains SPDXRef-B.
|
||||||
|
// Example: An ARCHIVE file bar.tgz contains a SOURCE file foo.c.
|
||||||
|
ContainsRelationship RelationshipType = "CONTAINS"
|
||||||
|
|
||||||
|
// ContainedByRelationship is to be used when SPDXRef-A is contained by SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.c is contained by ARCHIVE file bar.tgz
|
||||||
|
ContainedByRelationship RelationshipType = "CONTAINED_BY"
|
||||||
|
|
||||||
|
// DependsOnRelationship is to be used when SPDXRef-A depends on SPDXRef-B.
|
||||||
|
// Example: Package A depends on the presence of package B in order to build and run
|
||||||
|
DependsOnRelationship RelationshipType = "DEPENDS_ON"
|
||||||
|
|
||||||
|
// DependencyOfRelationship is to be used when SPDXRef-A is dependency of SPDXRef-B.
|
||||||
|
// Example: A is explicitly stated as a dependency of B in a machine-readable file. Use when a package manager does not define scopes.
|
||||||
|
DependencyOfRelationship RelationshipType = "DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// DependencyManifestOfRelationship is to be used when SPDXRef-A is a manifest file that lists a set of dependencies for SPDXRef-B.
|
||||||
|
// Example: A file package.json is the dependency manifest of a package foo. Note that only one manifest should be used to define the same dependency graph.
|
||||||
|
DependencyManifestOfRelationship RelationshipType = "DEPENDENCY_MANIFEST_OF"
|
||||||
|
|
||||||
|
// BuildDependencyOfRelationship is to be used when SPDXRef-A is a build dependency of SPDXRef-B.
|
||||||
|
// Example: A is in the compile scope of B in a Maven project.
|
||||||
|
BuildDependencyOfRelationship RelationshipType = "BUILD_DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// DevDependencyOfRelationship is to be used when SPDXRef-A is a development dependency of SPDXRef-B.
|
||||||
|
// Example: A is in the devDependencies scope of B in a Maven project.
|
||||||
|
DevDependencyOfRelationship RelationshipType = "DEV_DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// OptionalDependencyOfRelationship is to be used when SPDXRef-A is an optional dependency of SPDXRef-B.
|
||||||
|
// Example: Use when building the code will proceed even if a dependency cannot be found, fails to install, or is only installed on a specific platform. For example, A is in the optionalDependencies scope of npm project B.
|
||||||
|
OptionalDependencyOfRelationship RelationshipType = "OPTIONAL_DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// ProvidedDependencyOfRelationship is to be used when SPDXRef-A is a to be provided dependency of SPDXRef-B.
|
||||||
|
// Example: A is in the provided scope of B in a Maven project, indicating that the project expects it to be provided, for instance, by the container or JDK.
|
||||||
|
ProvidedDependencyOfRelationship RelationshipType = "PROVIDED_DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// TestDependencyOfRelationship is to be used when SPDXRef-A is a test dependency of SPDXRef-B.
|
||||||
|
// Example: A is in the test scope of B in a Maven project.
|
||||||
|
TestDependencyOfRelationship RelationshipType = "TEST_DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// RuntimeDependencyOfRelationship is to be used when SPDXRef-A is a dependency required for the execution of SPDXRef-B.
|
||||||
|
// Example: A is in the runtime scope of B in a Maven project.
|
||||||
|
RuntimeDependencyOfRelationship RelationshipType = "RUNTIME_DEPENDENCY_OF"
|
||||||
|
|
||||||
|
// ExampleOfRelationship is to be used when SPDXRef-A is an example of SPDXRef-B.
|
||||||
|
// Example: The file or snippet that illustrates how to use an application or library.
|
||||||
|
ExampleOfRelationship RelationshipType = "EXAMPLE_OF"
|
||||||
|
|
||||||
|
// GeneratesRelationship is to be used when SPDXRef-A generates SPDXRef-B.
|
||||||
|
// Example: A SOURCE file makefile.mk generates a BINARY file a.out
|
||||||
|
GeneratesRelationship RelationshipType = "GENERATES"
|
||||||
|
|
||||||
|
// GeneratedFromRelationship is to be used when SPDXRef-A was generated from SPDXRef-B.
|
||||||
|
// Example: A BINARY file a.out has been generated from a SOURCE file makefile.mk. A BINARY file foolib.a is generated from a SOURCE file bar.c.
|
||||||
|
GeneratedFromRelationship RelationshipType = "GENERATED_FROM"
|
||||||
|
|
||||||
|
// AncestorOfRelationship is to be used when SPDXRef-A is an ancestor (same lineage but pre-dates) SPDXRef-B.
|
||||||
|
// Example: A SOURCE file makefile.mk is a version of the original ancestor SOURCE file 'makefile2.mk'
|
||||||
|
AncestorOfRelationship RelationshipType = "ANCESTOR_OF"
|
||||||
|
|
||||||
|
// DescendantOfRelationship is to be used when SPDXRef-A is a descendant of (same lineage but postdates) SPDXRef-B.
|
||||||
|
// Example: A SOURCE file makefile2.mk is a descendant of the original SOURCE file 'makefile.mk'
|
||||||
|
DescendantOfRelationship RelationshipType = "DESCENDANT_OF"
|
||||||
|
|
||||||
|
// VariantOfRelationship is to be used when SPDXRef-A is a variant of (same lineage but not clear which came first) SPDXRef-B.
|
||||||
|
// Example: A SOURCE file makefile2.mk is a variant of SOURCE file makefile.mk if they differ by some edit, but there is no way to tell which came first (no reliable date information).
|
||||||
|
VariantOfRelationship RelationshipType = "VARIANT_OF"
|
||||||
|
|
||||||
|
// DistributionArtifactRelationship is to be used when distributing SPDXRef-A requires that SPDXRef-B also be distributed.
|
||||||
|
// Example: A BINARY file foo.o requires that the ARCHIVE file bar-sources.tgz be made available on distribution.
|
||||||
|
DistributionArtifactRelationship RelationshipType = "DISTRIBUTION_ARTIFACT"
|
||||||
|
|
||||||
|
// PatchForRelationship is to be used when SPDXRef-A is a patch file for (to be applied to) SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.diff is a patch file for SOURCE file foo.c.
|
||||||
|
PatchForRelationship RelationshipType = "PATCH_FOR"
|
||||||
|
|
||||||
|
// PatchAppliedRelationship is to be used when SPDXRef-A is a patch file that has been applied to SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.diff is a patch file that has been applied to SOURCE file 'foo-patched.c'.
|
||||||
|
PatchAppliedRelationship RelationshipType = "PATCH_APPLIED"
|
||||||
|
|
||||||
|
// CopyOfRelationship is to be used when SPDXRef-A is an exact copy of SPDXRef-B.
|
||||||
|
// Example: A BINARY file alib.a is an exact copy of BINARY file a2lib.a.
|
||||||
|
CopyOfRelationship RelationshipType = "COPY_OF"
|
||||||
|
|
||||||
|
// FileAddedRelationship is to be used when SPDXRef-A is a file that was added to SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.c has been added to package ARCHIVE bar.tgz.
|
||||||
|
FileAddedRelationship RelationshipType = "FILE_ADDED"
|
||||||
|
|
||||||
|
// FileDeletedRelationship is to be used when SPDXRef-A is a file that was deleted from SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.diff has been deleted from package ARCHIVE bar.tgz.
|
||||||
|
FileDeletedRelationship RelationshipType = "FILE_DELETED"
|
||||||
|
|
||||||
|
// FileModifiedRelationship is to be used when SPDXRef-A is a file that was modified from SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.c has been modified from SOURCE file foo.orig.c.
|
||||||
|
FileModifiedRelationship RelationshipType = "FILE_MODIFIED"
|
||||||
|
|
||||||
|
// ExpandedFromArchiveRelationship is to be used when SPDXRef-A is expanded from the archive SPDXRef-B.
|
||||||
|
// Example: A SOURCE file foo.c, has been expanded from the archive ARCHIVE file xyz.tgz.
|
||||||
|
ExpandedFromArchiveRelationship RelationshipType = "EXPANDED_FROM_ARCHIVE"
|
||||||
|
|
||||||
|
// DynamicLinkRelationship is to be used when SPDXRef-A dynamically links to SPDXRef-B.
|
||||||
|
// Example: An APPLICATION file 'myapp' dynamically links to BINARY file zlib.so.
|
||||||
|
DynamicLinkRelationship RelationshipType = "DYNAMIC_LINK"
|
||||||
|
|
||||||
|
// StaticLinkRelationship is to be used when SPDXRef-A statically links to SPDXRef-B.
|
||||||
|
// Example: An APPLICATION file 'myapp' statically links to BINARY zlib.a.
|
||||||
|
StaticLinkRelationship RelationshipType = "STATIC_LINK"
|
||||||
|
|
||||||
|
// DataFileOfRelationship is to be used when SPDXRef-A is a data file used in SPDXRef-B.
|
||||||
|
// Example: An IMAGE file 'kitty.jpg' is a data file of an APPLICATION 'hellokitty'.
|
||||||
|
DataFileOfRelationship RelationshipType = "DATA_FILE_OF"
|
||||||
|
|
||||||
|
// TestCaseOfRelationship is to be used when SPDXRef-A is a test case used in testing SPDXRef-B.
|
||||||
|
// Example: A SOURCE file testMyCode.java is a unit test file used to test an APPLICATION MyPackage.
|
||||||
|
TestCaseOfRelationship RelationshipType = "TEST_CASE_OF"
|
||||||
|
|
||||||
|
// BuildToolOfRelationship is to be used when SPDXRef-A is used to build SPDXRef-B.
|
||||||
|
// Example: A SOURCE file makefile.mk is used to build an APPLICATION 'zlib'.
|
||||||
|
BuildToolOfRelationship RelationshipType = "BUILD_TOOL_OF"
|
||||||
|
|
||||||
|
// DevToolOfRelationship is to be used when SPDXRef-A is used as a development tool for SPDXRef-B.
|
||||||
|
// Example: Any tool used for development such as a code debugger.
|
||||||
|
DevToolOfRelationship RelationshipType = "DEV_TOOL_OF"
|
||||||
|
|
||||||
|
// TestOfRelationship is to be used when SPDXRef-A is used for testing SPDXRef-B.
|
||||||
|
// Example: Generic relationship for cases where it's clear that something is used for testing but unclear whether it's TEST_CASE_OF or TEST_TOOL_OF.
|
||||||
|
TestOfRelationship RelationshipType = "TEST_OF"
|
||||||
|
|
||||||
|
// TestToolOfRelationship is to be used when SPDXRef-A is used as a test tool for SPDXRef-B.
|
||||||
|
// Example: Any tool used to test the code such as ESlint.
|
||||||
|
TestToolOfRelationship RelationshipType = "TEST_TOOL_OF"
|
||||||
|
|
||||||
|
// DocumentationOfRelationship is to be used when SPDXRef-A provides documentation of SPDXRef-B.
|
||||||
|
// Example: A DOCUMENTATION file readme.txt documents the APPLICATION 'zlib'.
|
||||||
|
DocumentationOfRelationship RelationshipType = "DOCUMENTATION_OF"
|
||||||
|
|
||||||
|
// OptionalComponentOfRelationship is to be used when SPDXRef-A is an optional component of SPDXRef-B.
|
||||||
|
// Example: A SOURCE file fool.c (which is in the contributors directory) may or may not be included in the build of APPLICATION 'atthebar'.
|
||||||
|
OptionalComponentOfRelationship RelationshipType = "OPTIONAL_COMPONENT_OF"
|
||||||
|
|
||||||
|
// MetafileOfRelationship is to be used when SPDXRef-A is a metafile of SPDXRef-B.
|
||||||
|
// Example: A SOURCE file pom.xml is a metafile of the APPLICATION 'Apache Xerces'.
|
||||||
|
MetafileOfRelationship RelationshipType = "METAFILE_OF"
|
||||||
|
|
||||||
|
// PackageOfRelationship is to be used when SPDXRef-A is used as a package as part of SPDXRef-B.
|
||||||
|
// Example: A Linux distribution contains an APPLICATION package gawk as part of the distribution MyLinuxDistro.
|
||||||
|
PackageOfRelationship RelationshipType = "PACKAGE_OF"
|
||||||
|
|
||||||
|
// AmendsRelationship is to be used when (current) SPDXRef-DOCUMENT amends the SPDX information in SPDXRef-B.
|
||||||
|
// Example: (Current) SPDX document A version 2 contains a correction to a previous version of the SPDX document A version 1. Note the reserved identifier SPDXRef-DOCUMENT for the current document is required.
|
||||||
|
AmendsRelationship RelationshipType = "AMENDS"
|
||||||
|
|
||||||
|
// PrerequisiteForRelationship is to be used when SPDXRef-A is a prerequisite for SPDXRef-B.
|
||||||
|
// Example: A library bar.dll is a prerequisite or dependency for APPLICATION foo.exe
|
||||||
|
PrerequisiteForRelationship RelationshipType = "PREREQUISITE_FOR"
|
||||||
|
|
||||||
|
// HasPrerequisiteRelationship is to be used when SPDXRef-A has as a prerequisite SPDXRef-B.
|
||||||
|
// Example: An APPLICATION foo.exe has prerequisite or dependency on bar.dll
|
||||||
|
HasPrerequisiteRelationship RelationshipType = "HAS_PREREQUISITE"
|
||||||
|
|
||||||
|
// OtherRelationship is to be used for a relationship which has not been defined in the formal SPDX specification. A description of the relationship should be included in the Relationship comments field.
|
||||||
|
OtherRelationship RelationshipType = "OTHER"
|
||||||
|
)
|
||||||
339
internal/formats/common/spdxhelpers/to_syft_model.go
Normal file
339
internal/formats/common/spdxhelpers/to_syft_model.go
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
package spdxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spdx/tools-golang/spdx"
|
||||||
|
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/linux"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
|
||||||
|
spdxIDMap := make(map[string]interface{})
|
||||||
|
|
||||||
|
s := &sbom.SBOM{
|
||||||
|
Artifacts: sbom.Artifacts{
|
||||||
|
PackageCatalog: pkg.NewCatalog(),
|
||||||
|
FileMetadata: map[source.Coordinates]source.FileMetadata{},
|
||||||
|
FileDigests: map[source.Coordinates][]file.Digest{},
|
||||||
|
LinuxDistribution: findLinuxReleaseByPURL(doc),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
collectSyftPackages(s, spdxIDMap, doc)
|
||||||
|
|
||||||
|
collectSyftFiles(s, spdxIDMap, doc)
|
||||||
|
|
||||||
|
s.Relationships = toSyftRelationships(spdxIDMap, doc)
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLinuxReleaseByPURL(doc *spdx.Document2_2) *linux.Release {
|
||||||
|
for _, p := range doc.Packages {
|
||||||
|
purlValue := findPURLValue(p)
|
||||||
|
if purlValue == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
purl, err := packageurl.FromString(purlValue)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to parse purl: %s", purlValue)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
distro := findQualifierValue(purl, pkg.PURLQualifierDistro)
|
||||||
|
if distro != "" {
|
||||||
|
parts := strings.Split(distro, "-")
|
||||||
|
name := parts[0]
|
||||||
|
version := ""
|
||||||
|
if len(parts) > 1 {
|
||||||
|
version = parts[1]
|
||||||
|
}
|
||||||
|
return &linux.Release{
|
||||||
|
PrettyName: name,
|
||||||
|
Name: name,
|
||||||
|
ID: name,
|
||||||
|
IDLike: []string{name},
|
||||||
|
Version: version,
|
||||||
|
VersionID: version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]interface{}, doc *spdx.Document2_2) {
|
||||||
|
for _, p := range doc.Packages {
|
||||||
|
syftPkg := toSyftPackage(p)
|
||||||
|
spdxIDMap[string(p.PackageSPDXIdentifier)] = syftPkg
|
||||||
|
s.Artifacts.PackageCatalog.Add(*syftPkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectSyftFiles(s *sbom.SBOM, spdxIDMap map[string]interface{}, doc *spdx.Document2_2) {
|
||||||
|
for _, f := range doc.UnpackagedFiles {
|
||||||
|
l := toSyftLocation(f)
|
||||||
|
spdxIDMap[string(f.FileSPDXIdentifier)] = l
|
||||||
|
|
||||||
|
s.Artifacts.FileMetadata[l.Coordinates] = toFileMetadata(f)
|
||||||
|
s.Artifacts.FileDigests[l.Coordinates] = toFileDigests(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toFileDigests(f *spdx.File2_2) (digests []file.Digest) {
|
||||||
|
for _, digest := range f.FileChecksums {
|
||||||
|
digests = append(digests, file.Digest{
|
||||||
|
Algorithm: string(digest.Algorithm),
|
||||||
|
Value: digest.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return digests
|
||||||
|
}
|
||||||
|
|
||||||
|
func toFileMetadata(f *spdx.File2_2) (meta source.FileMetadata) {
|
||||||
|
// FIXME Syft is currently lossy due to the SPDX 2.2.1 spec not supporting arbitrary mimetypes
|
||||||
|
for _, typ := range f.FileType {
|
||||||
|
switch FileType(typ) {
|
||||||
|
case ImageFileType:
|
||||||
|
meta.MIMEType = "image/"
|
||||||
|
case VideoFileType:
|
||||||
|
meta.MIMEType = "video/"
|
||||||
|
case ApplicationFileType:
|
||||||
|
meta.MIMEType = "application/"
|
||||||
|
case TextFileType:
|
||||||
|
meta.MIMEType = "text/"
|
||||||
|
case AudioFileType:
|
||||||
|
meta.MIMEType = "audio/"
|
||||||
|
case BinaryFileType:
|
||||||
|
case ArchiveFileType:
|
||||||
|
case OtherFileType:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSyftRelationships(spdxIDMap map[string]interface{}, doc *spdx.Document2_2) []artifact.Relationship {
|
||||||
|
var out []artifact.Relationship
|
||||||
|
for _, r := range doc.Relationships {
|
||||||
|
// FIXME what to do with r.RefA.DocumentRefID and r.RefA.SpecialID
|
||||||
|
if r.RefA.DocumentRefID != "" && requireAndTrimPrefix(r.RefA.DocumentRefID, "DocumentRef-") != string(doc.CreationInfo.SPDXIdentifier) {
|
||||||
|
log.Debugf("ignoring relationship to external document: %+v", r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
a := spdxIDMap[string(r.RefA.ElementRefID)]
|
||||||
|
b := spdxIDMap[string(r.RefB.ElementRefID)]
|
||||||
|
from, fromOk := a.(*pkg.Package)
|
||||||
|
toPackage, toPackageOk := b.(*pkg.Package)
|
||||||
|
toLocation, toLocationOk := b.(*source.Location)
|
||||||
|
if !fromOk || !(toPackageOk || toLocationOk) {
|
||||||
|
log.Debugf("unable to find valid relationship mapping from SPDX 2.2 JSON, ignoring: (from: %+v) (to: %+v)", a, b)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var to artifact.Identifiable
|
||||||
|
var typ artifact.RelationshipType
|
||||||
|
if toLocationOk {
|
||||||
|
if r.Relationship == string(ContainsRelationship) {
|
||||||
|
typ = artifact.ContainsRelationship
|
||||||
|
to = toLocation
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch RelationshipType(r.Relationship) {
|
||||||
|
case ContainsRelationship:
|
||||||
|
typ = artifact.ContainsRelationship
|
||||||
|
to = toPackage
|
||||||
|
case BuildDependencyOfRelationship:
|
||||||
|
typ = artifact.BuildDependencyOfRelationship
|
||||||
|
to = toPackage
|
||||||
|
case RuntimeDependencyOfRelationship:
|
||||||
|
typ = artifact.RuntimeDependencyOfRelationship
|
||||||
|
to = toPackage
|
||||||
|
case OtherRelationship:
|
||||||
|
// Encoding uses a specifically formatted comment...
|
||||||
|
if strings.Index(r.RelationshipComment, string(artifact.OwnershipByFileOverlapRelationship)) == 0 {
|
||||||
|
typ = artifact.RuntimeDependencyOfRelationship
|
||||||
|
to = toPackage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ != "" && to != nil {
|
||||||
|
out = append(out, artifact.Relationship{
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
Type: typ,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSyftCoordinates(f *spdx.File2_2) source.Coordinates {
|
||||||
|
const layerIDPrefix = "layerID: "
|
||||||
|
var fileSystemID string
|
||||||
|
if strings.Index(f.FileComment, layerIDPrefix) == 0 {
|
||||||
|
fileSystemID = strings.TrimPrefix(f.FileComment, layerIDPrefix)
|
||||||
|
}
|
||||||
|
if strings.Index(string(f.FileSPDXIdentifier), layerIDPrefix) == 0 {
|
||||||
|
fileSystemID = strings.TrimPrefix(string(f.FileSPDXIdentifier), layerIDPrefix)
|
||||||
|
}
|
||||||
|
return source.Coordinates{
|
||||||
|
RealPath: f.FileName,
|
||||||
|
FileSystemID: fileSystemID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSyftLocation(f *spdx.File2_2) *source.Location {
|
||||||
|
return &source.Location{
|
||||||
|
Coordinates: toSyftCoordinates(f),
|
||||||
|
VirtualPath: f.FileName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func requireAndTrimPrefix(val interface{}, prefix string) string {
|
||||||
|
if v, ok := val.(string); ok {
|
||||||
|
if i := strings.Index(v, prefix); i == 0 {
|
||||||
|
return strings.Replace(v, prefix, "", 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type pkgInfo struct {
|
||||||
|
purl packageurl.PackageURL
|
||||||
|
typ pkg.Type
|
||||||
|
lang pkg.Language
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pkgInfo) qualifierValue(name string) string {
|
||||||
|
return findQualifierValue(p.purl, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findQualifierValue(purl packageurl.PackageURL, qualifier string) string {
|
||||||
|
for _, q := range purl.Qualifiers {
|
||||||
|
if q.Key == qualifier {
|
||||||
|
return q.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractPkgInfo(p *spdx.Package2_2) pkgInfo {
|
||||||
|
pu := findPURLValue(p)
|
||||||
|
purl, err := packageurl.FromString(pu)
|
||||||
|
if err != nil {
|
||||||
|
return pkgInfo{}
|
||||||
|
}
|
||||||
|
return pkgInfo{
|
||||||
|
purl,
|
||||||
|
pkg.TypeByName(purl.Type),
|
||||||
|
pkg.LanguageByName(purl.Type),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSyftPackage(p *spdx.Package2_2) *pkg.Package {
|
||||||
|
info := extractPkgInfo(p)
|
||||||
|
metadataType, metadata := extractMetadata(p, info)
|
||||||
|
sP := pkg.Package{
|
||||||
|
Type: info.typ,
|
||||||
|
Name: p.PackageName,
|
||||||
|
Version: p.PackageVersion,
|
||||||
|
Licenses: parseLicense(p.PackageLicenseDeclared),
|
||||||
|
CPEs: extractCPEs(p),
|
||||||
|
PURL: info.purl.String(),
|
||||||
|
Language: info.lang,
|
||||||
|
MetadataType: metadataType,
|
||||||
|
Metadata: metadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
sP.SetID()
|
||||||
|
|
||||||
|
return &sP
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractMetadata(p *spdx.Package2_2, info pkgInfo) (pkg.MetadataType, interface{}) {
|
||||||
|
arch := info.qualifierValue(pkg.PURLQualifierArch)
|
||||||
|
upstreamValue := info.qualifierValue(pkg.PURLQualifierUpstream)
|
||||||
|
upstream := strings.SplitN(upstreamValue, "@", 2)
|
||||||
|
upstreamName := upstream[0]
|
||||||
|
upstreamVersion := ""
|
||||||
|
if len(upstream) > 1 {
|
||||||
|
upstreamVersion = upstream[1]
|
||||||
|
}
|
||||||
|
switch info.typ {
|
||||||
|
case pkg.ApkPkg:
|
||||||
|
return pkg.ApkMetadataType, pkg.ApkMetadata{
|
||||||
|
Package: p.PackageName,
|
||||||
|
OriginPackage: upstreamName,
|
||||||
|
Maintainer: p.PackageSupplierPerson,
|
||||||
|
Version: p.PackageVersion,
|
||||||
|
License: p.PackageLicenseDeclared,
|
||||||
|
Architecture: arch,
|
||||||
|
URL: p.PackageHomePage,
|
||||||
|
Description: p.PackageDescription,
|
||||||
|
}
|
||||||
|
case pkg.RpmPkg:
|
||||||
|
converted, err := strconv.Atoi(info.qualifierValue(pkg.PURLQualifierEpoch))
|
||||||
|
var epoch *int
|
||||||
|
if err != nil {
|
||||||
|
epoch = nil
|
||||||
|
} else {
|
||||||
|
epoch = &converted
|
||||||
|
}
|
||||||
|
return pkg.RpmdbMetadataType, pkg.RpmdbMetadata{
|
||||||
|
Name: p.PackageName,
|
||||||
|
Version: p.PackageVersion,
|
||||||
|
Epoch: epoch,
|
||||||
|
Arch: arch,
|
||||||
|
SourceRpm: upstreamValue,
|
||||||
|
License: p.PackageLicenseConcluded,
|
||||||
|
Vendor: p.PackageOriginatorOrganization,
|
||||||
|
}
|
||||||
|
case pkg.DebPkg:
|
||||||
|
return pkg.DpkgMetadataType, pkg.DpkgMetadata{
|
||||||
|
Package: p.PackageName,
|
||||||
|
Source: upstreamName,
|
||||||
|
Version: p.PackageVersion,
|
||||||
|
SourceVersion: upstreamVersion,
|
||||||
|
Architecture: arch,
|
||||||
|
Maintainer: p.PackageOriginatorPerson,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pkg.UnknownMetadataType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPURLValue(p *spdx.Package2_2) string {
|
||||||
|
for _, r := range p.PackageExternalReferences {
|
||||||
|
if r.RefType == string(PurlExternalRefType) {
|
||||||
|
return r.Locator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractCPEs(p *spdx.Package2_2) (cpes []pkg.CPE) {
|
||||||
|
for _, r := range p.PackageExternalReferences {
|
||||||
|
if r.RefType == string(Cpe23ExternalRefType) {
|
||||||
|
cpe, err := pkg.NewCPE(r.Locator)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to extract SPDX CPE=%q: %+v", r.Locator, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cpes = append(cpes, cpe)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cpes
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLicense(l string) []string {
|
||||||
|
if l == NOASSERTION || l == NONE {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return strings.Split(l, " AND ")
|
||||||
|
}
|
||||||
197
internal/formats/common/spdxhelpers/to_syft_model_test.go
Normal file
197
internal/formats/common/spdxhelpers/to_syft_model_test.go
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
package spdxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/spdx/tools-golang/spdx"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestToSyftModel(t *testing.T) {
|
||||||
|
sbom, err := ToSyftModel(&spdx.Document2_2{
|
||||||
|
CreationInfo: &spdx.CreationInfo2_2{
|
||||||
|
SPDXVersion: "1",
|
||||||
|
DataLicense: "GPL",
|
||||||
|
SPDXIdentifier: "id-doc-1",
|
||||||
|
DocumentName: "docName",
|
||||||
|
DocumentNamespace: "docNamespace",
|
||||||
|
ExternalDocumentReferences: nil,
|
||||||
|
LicenseListVersion: "",
|
||||||
|
CreatorPersons: nil,
|
||||||
|
CreatorOrganizations: nil,
|
||||||
|
CreatorTools: nil,
|
||||||
|
Created: "",
|
||||||
|
CreatorComment: "",
|
||||||
|
DocumentComment: "",
|
||||||
|
},
|
||||||
|
Packages: map[spdx.ElementID]*spdx.Package2_2{
|
||||||
|
"id-pkg-1": {
|
||||||
|
PackageName: "pkg-1",
|
||||||
|
PackageSPDXIdentifier: "id-pkg-1",
|
||||||
|
PackageVersion: "5.4.3",
|
||||||
|
PackageSupplierPerson: "",
|
||||||
|
PackageSupplierOrganization: "",
|
||||||
|
PackageLicenseDeclared: "",
|
||||||
|
PackageDescription: "",
|
||||||
|
PackageExternalReferences: []*spdx.PackageExternalReference2_2{
|
||||||
|
{
|
||||||
|
Category: "SECURITY",
|
||||||
|
Locator: "cpe:2.3:a:pkg-1:pkg-1:5.4.3:*:*:*:*:*:*:*",
|
||||||
|
RefType: "cpe23Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Category: "SECURITY",
|
||||||
|
Locator: "cpe:2.3:a:pkg_1:pkg_1:5.4.3:*:*:*:*:*:*:*",
|
||||||
|
RefType: "cpe23Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Category: "PACKAGE_MANAGER",
|
||||||
|
Locator: "pkg:alpine/pkg-1@5.4.3?arch=x86_64&upstream=p1-origin&distro=alpine-3.10.9",
|
||||||
|
RefType: "purl",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Files: nil,
|
||||||
|
},
|
||||||
|
"id-pkg-2": {
|
||||||
|
PackageName: "pkg-2",
|
||||||
|
PackageSPDXIdentifier: "id-pkg-2",
|
||||||
|
PackageVersion: "7.3.1",
|
||||||
|
PackageSupplierPerson: "",
|
||||||
|
PackageSupplierOrganization: "",
|
||||||
|
PackageLicenseDeclared: "",
|
||||||
|
PackageDescription: "",
|
||||||
|
PackageExternalReferences: []*spdx.PackageExternalReference2_2{
|
||||||
|
{
|
||||||
|
Category: "SECURITY",
|
||||||
|
Locator: "cpe:2.3:a:pkg-2:pkg-2:7.3.1:*:*:*:*:*:*:*",
|
||||||
|
RefType: "cpe23Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Category: "SECURITY",
|
||||||
|
Locator: "cpe:2.3:a:pkg_2:pkg_2:7.3.1:*:*:*:*:*:*:*",
|
||||||
|
RefType: "cpe23Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Category: "SECURITY",
|
||||||
|
Locator: "cpe:2.3:a:pkg-2:pkg_2:7.3.1:*:*:*:*:*:*:*",
|
||||||
|
RefType: "cpe23Type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Category: "PACKAGE_MANAGER",
|
||||||
|
Locator: "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=p2-origin@9.1.3&distro=debian-3.10.9",
|
||||||
|
RefType: "purl",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Files: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UnpackagedFiles: map[spdx.ElementID]*spdx.File2_2{},
|
||||||
|
Relationships: []*spdx.Relationship2_2{},
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
assert.NotNil(t, sbom)
|
||||||
|
|
||||||
|
pkgs := sbom.Artifacts.PackageCatalog.Sorted()
|
||||||
|
|
||||||
|
assert.Len(t, pkgs, 2)
|
||||||
|
|
||||||
|
p1 := pkgs[0]
|
||||||
|
assert.Equal(t, p1.Name, "pkg-1")
|
||||||
|
assert.Equal(t, p1.MetadataType, pkg.ApkMetadataType)
|
||||||
|
p1meta := p1.Metadata.(pkg.ApkMetadata)
|
||||||
|
assert.Equal(t, p1meta.OriginPackage, "p1-origin")
|
||||||
|
assert.Len(t, p1.CPEs, 2)
|
||||||
|
|
||||||
|
p2 := pkgs[1]
|
||||||
|
assert.Equal(t, p2.Name, "pkg-2")
|
||||||
|
assert.Equal(t, p2.MetadataType, pkg.DpkgMetadataType)
|
||||||
|
p2meta := p2.Metadata.(pkg.DpkgMetadata)
|
||||||
|
assert.Equal(t, p2meta.Source, "p2-origin")
|
||||||
|
assert.Equal(t, p2meta.SourceVersion, "9.1.3")
|
||||||
|
assert.Len(t, p2.CPEs, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_extractMetadata(t *testing.T) {
|
||||||
|
oneTwoThreeFour := 1234
|
||||||
|
tests := []struct {
|
||||||
|
pkg spdx.Package2_2
|
||||||
|
metaType pkg.MetadataType
|
||||||
|
meta interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
pkg: spdx.Package2_2{
|
||||||
|
PackageName: "SomeDebPkg",
|
||||||
|
PackageVersion: "43.1.235",
|
||||||
|
PackageExternalReferences: []*spdx.PackageExternalReference2_2{
|
||||||
|
{
|
||||||
|
Category: "PACKAGE_MANAGER",
|
||||||
|
Locator: "pkg:deb/pkg-2@7.3.1?arch=x86_64&upstream=somedebpkg-origin@9.1.3&distro=debian-3.10.9",
|
||||||
|
RefType: "purl",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metaType: pkg.DpkgMetadataType,
|
||||||
|
meta: pkg.DpkgMetadata{
|
||||||
|
Package: "SomeDebPkg",
|
||||||
|
Source: "somedebpkg-origin",
|
||||||
|
Version: "43.1.235",
|
||||||
|
SourceVersion: "9.1.3",
|
||||||
|
Architecture: "x86_64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pkg: spdx.Package2_2{
|
||||||
|
PackageName: "SomeApkPkg",
|
||||||
|
PackageVersion: "3.2.9",
|
||||||
|
PackageExternalReferences: []*spdx.PackageExternalReference2_2{
|
||||||
|
{
|
||||||
|
Category: "PACKAGE_MANAGER",
|
||||||
|
Locator: "pkg:alpine/pkg-2@7.3.1?arch=x86_64&upstream=apk-origin@9.1.3&distro=alpine-3.10.9",
|
||||||
|
RefType: "purl",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metaType: pkg.ApkMetadataType,
|
||||||
|
meta: pkg.ApkMetadata{
|
||||||
|
Package: "SomeApkPkg",
|
||||||
|
OriginPackage: "apk-origin",
|
||||||
|
Version: "3.2.9",
|
||||||
|
Architecture: "x86_64",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pkg: spdx.Package2_2{
|
||||||
|
PackageName: "SomeRpmPkg",
|
||||||
|
PackageVersion: "13.2.79",
|
||||||
|
PackageExternalReferences: []*spdx.PackageExternalReference2_2{
|
||||||
|
{
|
||||||
|
Category: "PACKAGE_MANAGER",
|
||||||
|
Locator: "pkg:rpm/pkg-2@7.3.1?arch=x86_64&epoch=1234&upstream=some-rpm-origin-1.16.3&distro=alpine-3.10.9",
|
||||||
|
RefType: "purl",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
metaType: pkg.RpmdbMetadataType,
|
||||||
|
meta: pkg.RpmdbMetadata{
|
||||||
|
Name: "SomeRpmPkg",
|
||||||
|
Version: "13.2.79",
|
||||||
|
Epoch: &oneTwoThreeFour,
|
||||||
|
Arch: "x86_64",
|
||||||
|
Release: "",
|
||||||
|
SourceRpm: "some-rpm-origin-1.16.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.pkg.PackageName, func(t *testing.T) {
|
||||||
|
info := extractPkgInfo(&test.pkg)
|
||||||
|
metaType, meta := extractMetadata(&test.pkg, info)
|
||||||
|
assert.Equal(t, test.metaType, metaType)
|
||||||
|
assert.EqualValues(t, test.meta, meta)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
28
internal/formats/spdx22json/decoder.go
Normal file
28
internal/formats/spdx22json/decoder.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package spdx22json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spdx/tools-golang/jsonloader"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decoder(reader io.Reader) (s *sbom.SBOM, err error) {
|
||||||
|
defer func() {
|
||||||
|
// The spdx tools JSON parser panics in quite a lot of situations, just handle this as a parse failure
|
||||||
|
if v := recover(); v != nil {
|
||||||
|
s = nil
|
||||||
|
err = fmt.Errorf("an error occurred during SPDX JSON document parsing: %+v", v)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
doc, err := jsonloader.Load2_2(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode spdx-json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return spdxhelpers.ToSyftModel(doc)
|
||||||
|
}
|
||||||
103
internal/formats/spdx22json/decoder_test.go
Normal file
103
internal/formats/spdx22json/decoder_test.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package spdx22json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSPDXJSONDecoder(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
path string
|
||||||
|
fail bool
|
||||||
|
packages []string
|
||||||
|
relationships []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
path: "alpine-3.10.syft.spdx.json",
|
||||||
|
packages: []string{"busybox", "libssl1.1", "ssl_client"},
|
||||||
|
relationships: []string{"busybox", "busybox", "libssl1.1", "libssl1.1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "alpine-3.10.vendor.spdx.json",
|
||||||
|
packages: []string{"alpine", "busybox", "ssl_client"},
|
||||||
|
relationships: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "example7-bin.spdx.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "example7-go-module.spdx.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "example7-golang.spdx.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "example7-third-party-modules.spdx.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "bad/example7-bin.spdx.json",
|
||||||
|
fail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "bad/example7-go-module.spdx.json",
|
||||||
|
fail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "bad/example7-golang.spdx.json",
|
||||||
|
fail: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "bad/example7-third-party-modules.spdx.json",
|
||||||
|
fail: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.path, func(t *testing.T) {
|
||||||
|
f, err := os.Open("test-fixtures/spdx/" + test.path)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
sbom, err := decoder(f)
|
||||||
|
|
||||||
|
if test.fail {
|
||||||
|
assert.Error(t, err)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.packages != nil {
|
||||||
|
assert.Equal(t, sbom.Artifacts.PackageCatalog.PackageCount(), len(test.packages))
|
||||||
|
|
||||||
|
packages:
|
||||||
|
for _, pkgName := range test.packages {
|
||||||
|
for _, p := range sbom.Artifacts.PackageCatalog.Sorted() {
|
||||||
|
if p.Name == pkgName {
|
||||||
|
continue packages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.NoError(t, fmt.Errorf("Unable to find package: %s", pkgName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.relationships != nil {
|
||||||
|
assert.Len(t, sbom.Relationships, len(test.relationships))
|
||||||
|
|
||||||
|
relationships:
|
||||||
|
for _, pkgName := range test.relationships {
|
||||||
|
for _, rel := range sbom.Relationships {
|
||||||
|
p, ok := rel.From.(*pkg.Package)
|
||||||
|
if ok && p.Name == pkgName {
|
||||||
|
continue relationships
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.NoError(t, fmt.Errorf("Unable to find relationship: %s", pkgName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,12 +2,12 @@ package spdx22json
|
|||||||
|
|
||||||
import "github.com/anchore/syft/syft/format"
|
import "github.com/anchore/syft/syft/format"
|
||||||
|
|
||||||
// note: this format is LOSSY relative to the syftjson formation, which means that decoding and validation is not supported at this time
|
// note: this format is LOSSY relative to the syftjson format
|
||||||
func Format() format.Format {
|
func Format() format.Format {
|
||||||
return format.NewFormat(
|
return format.NewFormat(
|
||||||
format.SPDXJSONOption,
|
format.SPDXJSONOption,
|
||||||
encoder,
|
encoder,
|
||||||
nil,
|
decoder,
|
||||||
nil,
|
validator,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,5 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
type FileType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
DocumentationFileType FileType = "DOCUMENTATION" // if the file serves as documentation
|
|
||||||
ImageFileType FileType = "IMAGE" // if the file is associated with a picture image file (MIME type of image/*, e.g., .jpg, .gif)
|
|
||||||
VideoFileType FileType = "VIDEO" // if the file is associated with a video file type (MIME type of video/*)
|
|
||||||
ArchiveFileType FileType = "ARCHIVE" // if the file represents an archive (.tar, .jar, etc.)
|
|
||||||
SpdxFileType FileType = "SPDX" // if the file is an SPDX document
|
|
||||||
ApplicationFileType FileType = "APPLICATION" // if the file is associated with a specific application type (MIME type of application/*)
|
|
||||||
SourceFileType FileType = "SOURCE" // if the file is human readable source code (.c, .html, etc.)
|
|
||||||
BinaryFileType FileType = "BINARY" // if the file is a compiled object, target image or binary executable (.o, .a, etc.)
|
|
||||||
TextFileType FileType = "TEXT" // if the file is human readable text file (MIME type of text/*)
|
|
||||||
AudioFileType FileType = "AUDIO" // if the file is associated with an audio file (MIME type of audio/* , e.g. .mp3)
|
|
||||||
OtherFileType FileType = "OTHER" // if the file doesn't fit into the above categories (generated artifacts, data files, etc.)
|
|
||||||
)
|
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
Item
|
Item
|
||||||
// (At least one is required.) The checksum property provides a mechanism that can be used to verify that the
|
// (At least one is required.) The checksum property provides a mechanism that can be used to verify that the
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||||
|
|
||||||
type Package struct {
|
type Package struct {
|
||||||
Item
|
Item
|
||||||
// The checksum property provides a mechanism that can be used to verify that the contents of a File or
|
// The checksum property provides a mechanism that can be used to verify that the contents of a File or
|
||||||
@ -14,7 +16,7 @@ type Package struct {
|
|||||||
DownloadLocation string `json:"downloadLocation,omitempty"`
|
DownloadLocation string `json:"downloadLocation,omitempty"`
|
||||||
// An External Reference allows a Package to reference an external source of additional information, metadata,
|
// An External Reference allows a Package to reference an external source of additional information, metadata,
|
||||||
// enumerations, asset identifiers, or downloadable content believed to be relevant to the Package.
|
// enumerations, asset identifiers, or downloadable content believed to be relevant to the Package.
|
||||||
ExternalRefs []ExternalRef `json:"externalRefs,omitempty"`
|
ExternalRefs []spdxhelpers.ExternalRef `json:"externalRefs,omitempty"`
|
||||||
// Indicates whether the file content of this package has been available for or subjected to analysis when
|
// Indicates whether the file content of this package has been available for or subjected to analysis when
|
||||||
// creating the SPDX document. If false indicates packages that represent metadata or URI references to a
|
// creating the SPDX document. If false indicates packages that represent metadata or URI references to a
|
||||||
// project, product, artifact, distribution or a component. If set to false, the package must not contain any files
|
// project, product, artifact, distribution or a component. If set to false, the package must not contain any files
|
||||||
|
|||||||
@ -1,183 +1,13 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||||
|
|
||||||
type Relationship struct {
|
type Relationship struct {
|
||||||
// Id to which the SPDX element is related
|
// Id to which the SPDX element is related
|
||||||
SpdxElementID string `json:"spdxElementId"`
|
SpdxElementID string `json:"spdxElementId"`
|
||||||
// Describes the type of relationship between two SPDX elements.
|
// Describes the type of relationship between two SPDX elements.
|
||||||
RelationshipType RelationshipType `json:"relationshipType"`
|
RelationshipType spdxhelpers.RelationshipType `json:"relationshipType"`
|
||||||
// SPDX ID for SpdxElement. A related SpdxElement.
|
// SPDX ID for SpdxElement. A related SpdxElement.
|
||||||
RelatedSpdxElement string `json:"relatedSpdxElement"`
|
RelatedSpdxElement string `json:"relatedSpdxElement"`
|
||||||
Comment string `json:"comment,omitempty"`
|
Comment string `json:"comment,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// source: https://spdx.github.io/spdx-spec/7-relationships-between-SPDX-elements/
|
|
||||||
type RelationshipType string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// DescribedByRelationship is to be used when SPDXRef-A is described by SPDXREF-Document.
|
|
||||||
// Example: The package 'WildFly' is described by SPDX document WildFly.spdx.
|
|
||||||
DescribedByRelationship RelationshipType = "DESCRIBED_BY"
|
|
||||||
|
|
||||||
// ContainsRelationship is to be used when SPDXRef-A contains SPDXRef-B.
|
|
||||||
// Example: An ARCHIVE file bar.tgz contains a SOURCE file foo.c.
|
|
||||||
ContainsRelationship RelationshipType = "CONTAINS"
|
|
||||||
|
|
||||||
// ContainedByRelationship is to be used when SPDXRef-A is contained by SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.c is contained by ARCHIVE file bar.tgz
|
|
||||||
ContainedByRelationship RelationshipType = "CONTAINED_BY"
|
|
||||||
|
|
||||||
// DependsOnRelationship is to be used when SPDXRef-A depends on SPDXRef-B.
|
|
||||||
// Example: Package A depends on the presence of package B in order to build and run
|
|
||||||
DependsOnRelationship RelationshipType = "DEPENDS_ON"
|
|
||||||
|
|
||||||
// DependencyOfRelationship is to be used when SPDXRef-A is dependency of SPDXRef-B.
|
|
||||||
// Example: A is explicitly stated as a dependency of B in a machine-readable file. Use when a package manager does not define scopes.
|
|
||||||
DependencyOfRelationship RelationshipType = "DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// DependencyManifestOfRelationship is to be used when SPDXRef-A is a manifest file that lists a set of dependencies for SPDXRef-B.
|
|
||||||
// Example: A file package.json is the dependency manifest of a package foo. Note that only one manifest should be used to define the same dependency graph.
|
|
||||||
DependencyManifestOfRelationship RelationshipType = "DEPENDENCY_MANIFEST_OF"
|
|
||||||
|
|
||||||
// BuildDependencyOfRelationship is to be used when SPDXRef-A is a build dependency of SPDXRef-B.
|
|
||||||
// Example: A is in the compile scope of B in a Maven project.
|
|
||||||
BuildDependencyOfRelationship RelationshipType = "BUILD_DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// DevDependencyOfRelationship is to be used when SPDXRef-A is a development dependency of SPDXRef-B.
|
|
||||||
// Example: A is in the devDependencies scope of B in a Maven project.
|
|
||||||
DevDependencyOfRelationship RelationshipType = "DEV_DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// OptionalDependencyOfRelationship is to be used when SPDXRef-A is an optional dependency of SPDXRef-B.
|
|
||||||
// Example: Use when building the code will proceed even if a dependency cannot be found, fails to install, or is only installed on a specific platform. For example, A is in the optionalDependencies scope of npm project B.
|
|
||||||
OptionalDependencyOfRelationship RelationshipType = "OPTIONAL_DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// ProvidedDependencyOfRelationship is to be used when SPDXRef-A is a to be provided dependency of SPDXRef-B.
|
|
||||||
// Example: A is in the provided scope of B in a Maven project, indicating that the project expects it to be provided, for instance, by the container or JDK.
|
|
||||||
ProvidedDependencyOfRelationship RelationshipType = "PROVIDED_DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// TestDependencyOfRelationship is to be used when SPDXRef-A is a test dependency of SPDXRef-B.
|
|
||||||
// Example: A is in the test scope of B in a Maven project.
|
|
||||||
TestDependencyOfRelationship RelationshipType = "TEST_DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// RuntimeDependencyOfRelationship is to be used when SPDXRef-A is a dependency required for the execution of SPDXRef-B.
|
|
||||||
// Example: A is in the runtime scope of B in a Maven project.
|
|
||||||
RuntimeDependencyOfRelationship RelationshipType = "RUNTIME_DEPENDENCY_OF"
|
|
||||||
|
|
||||||
// ExampleOfRelationship is to be used when SPDXRef-A is an example of SPDXRef-B.
|
|
||||||
// Example: The file or snippet that illustrates how to use an application or library.
|
|
||||||
ExampleOfRelationship RelationshipType = "EXAMPLE_OF"
|
|
||||||
|
|
||||||
// GeneratesRelationship is to be used when SPDXRef-A generates SPDXRef-B.
|
|
||||||
// Example: A SOURCE file makefile.mk generates a BINARY file a.out
|
|
||||||
GeneratesRelationship RelationshipType = "GENERATES"
|
|
||||||
|
|
||||||
// GeneratedFromRelationship is to be used when SPDXRef-A was generated from SPDXRef-B.
|
|
||||||
// Example: A BINARY file a.out has been generated from a SOURCE file makefile.mk. A BINARY file foolib.a is generated from a SOURCE file bar.c.
|
|
||||||
GeneratedFromRelationship RelationshipType = "GENERATED_FROM"
|
|
||||||
|
|
||||||
// AncestorOfRelationship is to be used when SPDXRef-A is an ancestor (same lineage but pre-dates) SPDXRef-B.
|
|
||||||
// Example: A SOURCE file makefile.mk is a version of the original ancestor SOURCE file 'makefile2.mk'
|
|
||||||
AncestorOfRelationship RelationshipType = "ANCESTOR_OF"
|
|
||||||
|
|
||||||
// DescendantOfRelationship is to be used when SPDXRef-A is a descendant of (same lineage but postdates) SPDXRef-B.
|
|
||||||
// Example: A SOURCE file makefile2.mk is a descendant of the original SOURCE file 'makefile.mk'
|
|
||||||
DescendantOfRelationship RelationshipType = "DESCENDANT_OF"
|
|
||||||
|
|
||||||
// VariantOfRelationship is to be used when SPDXRef-A is a variant of (same lineage but not clear which came first) SPDXRef-B.
|
|
||||||
// Example: A SOURCE file makefile2.mk is a variant of SOURCE file makefile.mk if they differ by some edit, but there is no way to tell which came first (no reliable date information).
|
|
||||||
VariantOfRelationship RelationshipType = "VARIANT_OF"
|
|
||||||
|
|
||||||
// DistributionArtifactRelationship is to be used when distributing SPDXRef-A requires that SPDXRef-B also be distributed.
|
|
||||||
// Example: A BINARY file foo.o requires that the ARCHIVE file bar-sources.tgz be made available on distribution.
|
|
||||||
DistributionArtifactRelationship RelationshipType = "DISTRIBUTION_ARTIFACT"
|
|
||||||
|
|
||||||
// PatchForRelationship is to be used when SPDXRef-A is a patch file for (to be applied to) SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.diff is a patch file for SOURCE file foo.c.
|
|
||||||
PatchForRelationship RelationshipType = "PATCH_FOR"
|
|
||||||
|
|
||||||
// PatchAppliedRelationship is to be used when SPDXRef-A is a patch file that has been applied to SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.diff is a patch file that has been applied to SOURCE file 'foo-patched.c'.
|
|
||||||
PatchAppliedRelationship RelationshipType = "PATCH_APPLIED"
|
|
||||||
|
|
||||||
// CopyOfRelationship is to be used when SPDXRef-A is an exact copy of SPDXRef-B.
|
|
||||||
// Example: A BINARY file alib.a is an exact copy of BINARY file a2lib.a.
|
|
||||||
CopyOfRelationship RelationshipType = "COPY_OF"
|
|
||||||
|
|
||||||
// FileAddedRelationship is to be used when SPDXRef-A is a file that was added to SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.c has been added to package ARCHIVE bar.tgz.
|
|
||||||
FileAddedRelationship RelationshipType = "FILE_ADDED"
|
|
||||||
|
|
||||||
// FileDeletedRelationship is to be used when SPDXRef-A is a file that was deleted from SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.diff has been deleted from package ARCHIVE bar.tgz.
|
|
||||||
FileDeletedRelationship RelationshipType = "FILE_DELETED"
|
|
||||||
|
|
||||||
// FileModifiedRelationship is to be used when SPDXRef-A is a file that was modified from SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.c has been modified from SOURCE file foo.orig.c.
|
|
||||||
FileModifiedRelationship RelationshipType = "FILE_MODIFIED"
|
|
||||||
|
|
||||||
// ExpandedFromArchiveRelationship is to be used when SPDXRef-A is expanded from the archive SPDXRef-B.
|
|
||||||
// Example: A SOURCE file foo.c, has been expanded from the archive ARCHIVE file xyz.tgz.
|
|
||||||
ExpandedFromArchiveRelationship RelationshipType = "EXPANDED_FROM_ARCHIVE"
|
|
||||||
|
|
||||||
// DynamicLinkRelationship is to be used when SPDXRef-A dynamically links to SPDXRef-B.
|
|
||||||
// Example: An APPLICATION file 'myapp' dynamically links to BINARY file zlib.so.
|
|
||||||
DynamicLinkRelationship RelationshipType = "DYNAMIC_LINK"
|
|
||||||
|
|
||||||
// StaticLinkRelationship is to be used when SPDXRef-A statically links to SPDXRef-B.
|
|
||||||
// Example: An APPLICATION file 'myapp' statically links to BINARY zlib.a.
|
|
||||||
StaticLinkRelationship RelationshipType = "STATIC_LINK"
|
|
||||||
|
|
||||||
// DataFileOfRelationship is to be used when SPDXRef-A is a data file used in SPDXRef-B.
|
|
||||||
// Example: An IMAGE file 'kitty.jpg' is a data file of an APPLICATION 'hellokitty'.
|
|
||||||
DataFileOfRelationship RelationshipType = "DATA_FILE_OF"
|
|
||||||
|
|
||||||
// TestCaseOfRelationship is to be used when SPDXRef-A is a test case used in testing SPDXRef-B.
|
|
||||||
// Example: A SOURCE file testMyCode.java is a unit test file used to test an APPLICATION MyPackage.
|
|
||||||
TestCaseOfRelationship RelationshipType = "TEST_CASE_OF"
|
|
||||||
|
|
||||||
// BuildToolOfRelationship is to be used when SPDXRef-A is used to build SPDXRef-B.
|
|
||||||
// Example: A SOURCE file makefile.mk is used to build an APPLICATION 'zlib'.
|
|
||||||
BuildToolOfRelationship RelationshipType = "BUILD_TOOL_OF"
|
|
||||||
|
|
||||||
// DevToolOfRelationship is to be used when SPDXRef-A is used as a development tool for SPDXRef-B.
|
|
||||||
// Example: Any tool used for development such as a code debugger.
|
|
||||||
DevToolOfRelationship RelationshipType = "DEV_TOOL_OF"
|
|
||||||
|
|
||||||
// TestOfRelationship is to be used when SPDXRef-A is used for testing SPDXRef-B.
|
|
||||||
// Example: Generic relationship for cases where it's clear that something is used for testing but unclear whether it's TEST_CASE_OF or TEST_TOOL_OF.
|
|
||||||
TestOfRelationship RelationshipType = "TEST_OF"
|
|
||||||
|
|
||||||
// TestToolOfRelationship is to be used when SPDXRef-A is used as a test tool for SPDXRef-B.
|
|
||||||
// Example: Any tool used to test the code such as ESlint.
|
|
||||||
TestToolOfRelationship RelationshipType = "TEST_TOOL_OF"
|
|
||||||
|
|
||||||
// DocumentationOfRelationship is to be used when SPDXRef-A provides documentation of SPDXRef-B.
|
|
||||||
// Example: A DOCUMENTATION file readme.txt documents the APPLICATION 'zlib'.
|
|
||||||
DocumentationOfRelationship RelationshipType = "DOCUMENTATION_OF"
|
|
||||||
|
|
||||||
// OptionalComponentOfRelationship is to be used when SPDXRef-A is an optional component of SPDXRef-B.
|
|
||||||
// Example: A SOURCE file fool.c (which is in the contributors directory) may or may not be included in the build of APPLICATION 'atthebar'.
|
|
||||||
OptionalComponentOfRelationship RelationshipType = "OPTIONAL_COMPONENT_OF"
|
|
||||||
|
|
||||||
// MetafileOfRelationship is to be used when SPDXRef-A is a metafile of SPDXRef-B.
|
|
||||||
// Example: A SOURCE file pom.xml is a metafile of the APPLICATION 'Apache Xerces'.
|
|
||||||
MetafileOfRelationship RelationshipType = "METAFILE_OF"
|
|
||||||
|
|
||||||
// PackageOfRelationship is to be used when SPDXRef-A is used as a package as part of SPDXRef-B.
|
|
||||||
// Example: A Linux distribution contains an APPLICATION package gawk as part of the distribution MyLinuxDistro.
|
|
||||||
PackageOfRelationship RelationshipType = "PACKAGE_OF"
|
|
||||||
|
|
||||||
// AmendsRelationship is to be used when (current) SPDXRef-DOCUMENT amends the SPDX information in SPDXRef-B.
|
|
||||||
// Example: (Current) SPDX document A version 2 contains a correction to a previous version of the SPDX document A version 1. Note the reserved identifier SPDXRef-DOCUMENT for the current document is required.
|
|
||||||
AmendsRelationship RelationshipType = "AMENDS"
|
|
||||||
|
|
||||||
// PrerequisiteForRelationship is to be used when SPDXRef-A is a prerequisite for SPDXRef-B.
|
|
||||||
// Example: A library bar.dll is a prerequisite or dependency for APPLICATION foo.exe
|
|
||||||
PrerequisiteForRelationship RelationshipType = "PREREQUISITE_FOR"
|
|
||||||
|
|
||||||
// HasPrerequisiteRelationship is to be used when SPDXRef-A has as a prerequisite SPDXRef-B.
|
|
||||||
// Example: An APPLICATION foo.exe has prerequisite or dependency on bar.dll
|
|
||||||
HasPrerequisiteRelationship RelationshipType = "HAS_PREREQUISITE"
|
|
||||||
|
|
||||||
// OtherRelationship is to be used for a relationship which has not been defined in the formal SPDX specification. A description of the relationship should be included in the Relationship comments field.
|
|
||||||
OtherRelationship RelationshipType = "OTHER"
|
|
||||||
)
|
|
||||||
|
|||||||
@ -0,0 +1,170 @@
|
|||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"name": "alpine-3.10",
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2022-01-20T21:40:24.439211Z",
|
||||||
|
"creators": [
|
||||||
|
"Organization: Anchore, Inc",
|
||||||
|
"Tool: syft-[not provided]"
|
||||||
|
],
|
||||||
|
"licenseListVersion": "3.15"
|
||||||
|
},
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"documentNamespace": "https://anchore.com/syft/image/alpine-3.10-204b304b-beb3-4413-9b38-d8a2e58e3dfb",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-a61243292e73923",
|
||||||
|
"name": "busybox",
|
||||||
|
"licenseConcluded": "GPL-2.0",
|
||||||
|
"description": "Size optimized toolbox of many common UNIX utilities",
|
||||||
|
"downloadLocation": "https://busybox.net/",
|
||||||
|
"externalRefs": [
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:busybox:busybox:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "PACKAGE_MANAGER",
|
||||||
|
"referenceLocator": "pkg:alpine/busybox@1.30.1-r5?arch=x86_64&distro=alpine-3.10.9",
|
||||||
|
"referenceType": "purl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseDeclared": "GPL-2.0",
|
||||||
|
"originator": "Person: Natanael Copa <ncopa@alpinelinux.org>",
|
||||||
|
"sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed",
|
||||||
|
"versionInfo": "1.30.1-r5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-d2f55e316dbe92e4",
|
||||||
|
"name": "libssl1.1",
|
||||||
|
"licenseConcluded": "OpenSSL",
|
||||||
|
"description": "SSL shared libraries",
|
||||||
|
"downloadLocation": "https://www.openssl.org",
|
||||||
|
"externalRefs": [
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:libssl1.1:libssl1.1:1.1.1k-r0:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "PACKAGE_MANAGER",
|
||||||
|
"referenceLocator": "pkg:alpine/libssl1.1@1.1.1k-r0?arch=x86_64&distro=alpine-3.10.9",
|
||||||
|
"referenceType": "purl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseDeclared": "OpenSSL",
|
||||||
|
"originator": "Person: Timo Teras <timo.teras@iki.fi>",
|
||||||
|
"sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed",
|
||||||
|
"versionInfo": "1.1.1k-r0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-2b24657ad7aaafea",
|
||||||
|
"name": "ssl_client",
|
||||||
|
"licenseConcluded": "GPL-2.0",
|
||||||
|
"description": "EXternal ssl_client for busybox wget",
|
||||||
|
"downloadLocation": "https://busybox.net/",
|
||||||
|
"externalRefs": [
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:ssl-client:ssl-client:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:ssl-client:ssl_client:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:ssl_client:ssl-client:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:ssl_client:ssl_client:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:ssl:ssl-client:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "SECURITY",
|
||||||
|
"referenceLocator": "cpe:2.3:a:ssl:ssl_client:1.30.1-r5:*:*:*:*:*:*:*",
|
||||||
|
"referenceType": "cpe23Type"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"referenceCategory": "PACKAGE_MANAGER",
|
||||||
|
"referenceLocator": "pkg:alpine/ssl_client@1.30.1-r5?arch=x86_64&upstream=busybox&distro=alpine-3.10.9",
|
||||||
|
"referenceType": "purl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseDeclared": "GPL-2.0",
|
||||||
|
"originator": "Person: Natanael Copa <ncopa@alpinelinux.org>",
|
||||||
|
"sourceInfo": "acquired package info from APK DB: /lib/apk/db/installed",
|
||||||
|
"versionInfo": "1.30.1-r5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-a07392483a2d0750",
|
||||||
|
"comment": "layerID: sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"fileName": "/bin/busybox"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-aa3cfed221706d80",
|
||||||
|
"comment": "layerID: sha256:9fb3aa2f8b8023a4bebbf92aa567caf88e38e969ada9f0ac12643b2847391635",
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"fileName": "/lib/libssl.so.1.1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relationships": [
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-a61243292e73923",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-a07392483a2d0750"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-a61243292e73923",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-a07392483a2d0750"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-a61243292e73923",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-499bb68237b0f2b8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-a61243292e73923",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-df78c68c8206be69"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-a61243292e73923",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-7c980486fc17af43"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-a61243292e73923",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-8762661e65166719"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-d2f55e316dbe92e4",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-aa3cfed221706d80"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-d2f55e316dbe92e4",
|
||||||
|
"relationshipType": "CONTAINS",
|
||||||
|
"relatedSpdxElement": "SPDXRef-aa3cfed221706d80"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2022-01-18T22:16:16Z",
|
||||||
|
"creators": [
|
||||||
|
"Tool: vendor"
|
||||||
|
],
|
||||||
|
"licenseListVersion": "3.8"
|
||||||
|
},
|
||||||
|
"name": "alpine:3.10",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"documentNamespace": "https://spdx.org/spdxdocs/alpine-154c794c-4264-4e9f-a2ff-5c01bfbfc02c",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-alpine-3.10"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "alpine",
|
||||||
|
"SPDXID": "SPDXRef-alpine-3.10",
|
||||||
|
"versionInfo": "3.10",
|
||||||
|
"downloadLocation": "NOASSERTION",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "busybox",
|
||||||
|
"SPDXID": "SPDXRef-busybox-1.30.1-r5",
|
||||||
|
"versionInfo": "1.30.1-r5",
|
||||||
|
"downloadLocation": "NOASSERTION",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "LicenseRef-7d19a72",
|
||||||
|
"copyrightText": "NONE",
|
||||||
|
"comment": "busybox:\n\twarning: No metadata for key: copyright\n\twarning: No metadata for key: download_url\n\twarning: No metadata for key: checksum\n\twarning: No metadata for key: pkg_licenses\n\twarning: No metadata for key: pkg_format\n\twarning: No metadata for key: src_name\n\twarning: No metadata for key: src_version\n"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ssl_client",
|
||||||
|
"SPDXID": "SPDXRef-ssl_client-1.30.1-r5",
|
||||||
|
"versionInfo": "1.30.1-r5",
|
||||||
|
"downloadLocation": "NOASSERTION",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "LicenseRef-de5acdd",
|
||||||
|
"copyrightText": "NONE",
|
||||||
|
"comment": "ssl_client:\n\twarning: No metadata for key: copyright\n\twarning: No metadata for key: download_url\n\twarning: No metadata for key: checksum\n\twarning: No metadata for key: pkg_licenses\n\twarning: No metadata for key: pkg_format\n\twarning: No metadata for key: src_name\n\twarning: No metadata for key: src_version\n"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relationships": [
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-DOCUMENT",
|
||||||
|
"relatedSpdxElement": "SPDXRef-alpine-3.10",
|
||||||
|
"relationshipType": "DESCRIBES"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-9fb3aa2f8b",
|
||||||
|
"relatedSpdxElement": "SPDXRef-busybox-1.30.1-r5",
|
||||||
|
"relationshipType": "CONTAINS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "SPDXRef-9fb3aa2f8b",
|
||||||
|
"relatedSpdxElement": "SPDXRef-ssl_client-1.30.1-r5",
|
||||||
|
"relationshipType": "CONTAINS"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hasExtractedLicensingInfos": [
|
||||||
|
{
|
||||||
|
"extractedText": "OpenSSL",
|
||||||
|
"licenseId": "LicenseRef-de5acdd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"extractedText": "GPL-2.0",
|
||||||
|
"licenseId": "LicenseRef-7d19a72"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [
|
||||||
|
{
|
||||||
|
"Person": "Nisha K (nishak@vmware.com)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "hello-go-binary.spdx.json",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/hello-go-binary",
|
||||||
|
"externalDocumentRefs": [
|
||||||
|
{
|
||||||
|
"externalDocumentId": "DocumentRef-hello-go-module",
|
||||||
|
"checksum": {
|
||||||
|
"algorithm": "SHA1",
|
||||||
|
"checksumValue": "d661f8f831a99c288a64e5843b4794ad5181224a"
|
||||||
|
},
|
||||||
|
"spdxDocument": "https://swinslow.net/spdx-examples/example7/hello-go-module-cfa0c58d-79db-4860-99b6-258477e4838b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"externalDocumentId": "DocumentRef-golang-dist",
|
||||||
|
"checksum": {
|
||||||
|
"algorithm": "SHA1",
|
||||||
|
"checksumValue": "b6cf54a46329e7cc7610aa5d244018b80103d111"
|
||||||
|
},
|
||||||
|
"spdxDocument": "https://swinslow.net/spdx-examples/example7/golang-dist-492dfde4-318b-49f7-b48c-934bfafbde48"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"externalDocumentId": "DocumentRef-hello-imports",
|
||||||
|
"checksum": {
|
||||||
|
"algorithm": "SHA1",
|
||||||
|
"checksumValue": "14ff98203c3ddd2bd4803c00b5225d2551ca603c"
|
||||||
|
},
|
||||||
|
"spdxDocument": "https://swinslow.net/spdx-examples/example7/hello-imports-c2d068df-67aa-4c68-98c8-100b450fc408"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-go-bin-hello"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"packageName": "hello",
|
||||||
|
"SPDXID": "SPDXRef-go-bin-hello",
|
||||||
|
"downloadLocation": "git@github.com:swinslow/spdx-examples.git#example7/content/build/hello",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "NOASSERTION",
|
||||||
|
"packageCopyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relationships": [
|
||||||
|
{
|
||||||
|
"spdxElementId": "DocumentRef-golang-dist",
|
||||||
|
"relatedSpdxElement": "DocumentRef-hello-go-module",
|
||||||
|
"relationshipType": "BUILD_TOOL_OF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "DocumentRef-golang-dist:SPDXRef-go-compiler",
|
||||||
|
"relatedSpdxElement": "SPDXRef-go-bin-hello",
|
||||||
|
"relationshipType": "GENERATES"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "DocumentRef-hello-imports",
|
||||||
|
"relatedSpdxElement": "SPDXRef-go-bin-hello",
|
||||||
|
"relationshipType": "STATIC_LINK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [
|
||||||
|
{
|
||||||
|
"Person": "Nisha K (nishak@vmware.com)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "hello-go-module.spdx.json",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/hello-go-module",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-go-module-example.com/hello"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"packageName": "example.com/hello",
|
||||||
|
"SPDXID": "SPDXRef-go-module-example.com/hello",
|
||||||
|
"downloadLocation": "git@github.com:swinslow/spdx-examples.git#example7/content/src/hello",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "NOASSERTION",
|
||||||
|
"packageCopyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [
|
||||||
|
{
|
||||||
|
"Person": "Nisha K (nishak@vmware.com)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "golang-dist",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/golang-dist",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-golang-dist"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"packageName": "go1.16.4.linux-amd64",
|
||||||
|
"SPDXID": "SPDXRef-golang-dist",
|
||||||
|
"downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz",
|
||||||
|
"packageVersion": "1.16.4",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"checksums": [
|
||||||
|
{
|
||||||
|
"algorithm": "SHA256",
|
||||||
|
"checksumValue": "7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "LicenseRef-Golang-BSD-plus-Patents",
|
||||||
|
"packageCopyrightText": "Copyright (c) 2009 The Go Authors. All rights reserved."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "go",
|
||||||
|
"SPDXID": "SPDXRef-go-compiler",
|
||||||
|
"downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz",
|
||||||
|
"packageVersion": "1.16.4",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "NOASSERTION",
|
||||||
|
"packageCopyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [
|
||||||
|
{
|
||||||
|
"Person": "Nisha K (nishak@vmware.com)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "hello-imports.spdx.json",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/hello-imports",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-go-module-golang.org/x/text",
|
||||||
|
"SPDXRef-go-module-rsc.io/quote",
|
||||||
|
"SPDXRef-go-module-rsc.io/sampler"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"packageName": "golang.org/x/text",
|
||||||
|
"SPDXID": "SPDXRef-go-module-golang.org/x/text",
|
||||||
|
"downloadLocation": "go://golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "NOASSERTION",
|
||||||
|
"packageCopyrightText": "NOASSERTION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "rsc.io/quote",
|
||||||
|
"SPDXID": "SPDXRef-go-module-rsc.io/quote",
|
||||||
|
"downloadLocation": "go://rsc.io/quote@v1.5.2",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "NOASSERTION",
|
||||||
|
"packageCopyrightText": "NOASSERTION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"packageName": "rsc.io/sampler",
|
||||||
|
"SPDXID": "SPDXRef-go-module-rsc.io/sampler",
|
||||||
|
"downloadLocation": "go://rsc.io/sampler@v1.3.0",
|
||||||
|
"filesAnalyzed": "false",
|
||||||
|
"packageLicenseConcluded": "NOASSERTION",
|
||||||
|
"packageLicenseDeclared": "NOASSERTION",
|
||||||
|
"packageCopyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [ "Person: Nisha K (nishak@vmware.com)" ]
|
||||||
|
},
|
||||||
|
"name": "hello-go-binary.spdx.json",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/hello-go-binary",
|
||||||
|
"externalDocumentRefs": [
|
||||||
|
{
|
||||||
|
"externalDocumentId": "DocumentRef-hello-go-module",
|
||||||
|
"checksum": {
|
||||||
|
"algorithm": "SHA1",
|
||||||
|
"checksumValue": "d661f8f831a99c288a64e5843b4794ad5181224a"
|
||||||
|
},
|
||||||
|
"spdxDocument": "https://swinslow.net/spdx-examples/example7/hello-go-module-cfa0c58d-79db-4860-99b6-258477e4838b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"externalDocumentId": "DocumentRef-golang-dist",
|
||||||
|
"checksum": {
|
||||||
|
"algorithm": "SHA1",
|
||||||
|
"checksumValue": "b6cf54a46329e7cc7610aa5d244018b80103d111"
|
||||||
|
},
|
||||||
|
"spdxDocument": "https://swinslow.net/spdx-examples/example7/golang-dist-492dfde4-318b-49f7-b48c-934bfafbde48"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"externalDocumentId": "DocumentRef-hello-imports",
|
||||||
|
"checksum": {
|
||||||
|
"algorithm": "SHA1",
|
||||||
|
"checksumValue": "14ff98203c3ddd2bd4803c00b5225d2551ca603c"
|
||||||
|
},
|
||||||
|
"spdxDocument": "https://swinslow.net/spdx-examples/example7/hello-imports-c2d068df-67aa-4c68-98c8-100b450fc408"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-go-bin-hello"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "hello",
|
||||||
|
"SPDXID": "SPDXRef-go-bin-hello",
|
||||||
|
"downloadLocation": "git@github.com:swinslow/spdx-examples.git#example7/content/build/hello",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relationships": [
|
||||||
|
{
|
||||||
|
"spdxElementId": "DocumentRef-golang-dist:SPDXRef-golang-dist",
|
||||||
|
"relatedSpdxElement": "DocumentRef-hello-go-module:SPDXRef-hello-go-module",
|
||||||
|
"relationshipType": "BUILD_TOOL_OF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "DocumentRef-golang-dist:SPDXRef-go-compiler",
|
||||||
|
"relatedSpdxElement": "SPDXRef-go-bin-hello",
|
||||||
|
"relationshipType": "GENERATES"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spdxElementId": "DocumentRef-hello-imports:SPDXRef-hello-imports",
|
||||||
|
"relatedSpdxElement": "SPDXRef-go-bin-hello",
|
||||||
|
"relationshipType": "STATIC_LINK"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [ "Person: Nisha K (nishak@vmware.com)" ]
|
||||||
|
},
|
||||||
|
"name": "hello-go-module.spdx.json",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/hello-go-module",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-go-module-example.com/hello"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "example.com/hello",
|
||||||
|
"SPDXID": "SPDXRef-go-module-example.com/hello",
|
||||||
|
"downloadLocation": "git@github.com:swinslow/spdx-examples.git#example7/content/src/hello",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [ "Person: Nisha K (nishak@vmware.com)" ]
|
||||||
|
},
|
||||||
|
"name": "golang-dist",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/golang-dist",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-golang-dist"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "go1.16.4.linux-amd64",
|
||||||
|
"SPDXID": "SPDXRef-golang-dist",
|
||||||
|
"downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz",
|
||||||
|
"versionInfo": "1.16.4",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"checksums": [
|
||||||
|
{
|
||||||
|
"algorithm": "SHA256",
|
||||||
|
"checksumValue": "7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "LicenseRef-Golang-BSD-plus-Patents",
|
||||||
|
"copyrightText": "Copyright (c) 2009 The Go Authors. All rights reserved."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "go",
|
||||||
|
"SPDXID": "SPDXRef-go-compiler",
|
||||||
|
"downloadLocation": "https://golang.org/dl/go1.16.4.linux-amd64.tar.gz",
|
||||||
|
"versionInfo": "1.16.4",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"spdxVersion": "SPDX-2.2",
|
||||||
|
"dataLicense": "CC0-1.0",
|
||||||
|
"SPDXID": "SPDXRef-DOCUMENT",
|
||||||
|
"creationInfo": {
|
||||||
|
"created": "2020-11-24T01:12:27Z",
|
||||||
|
"creators": [ "Person: Nisha K (nishak@vmware.com)" ]
|
||||||
|
},
|
||||||
|
"name": "hello-imports.spdx.json",
|
||||||
|
"documentNamespace": "https://swinslow.net/spdx-examples/example7/hello-imports",
|
||||||
|
"documentDescribes": [
|
||||||
|
"SPDXRef-go-module-golang.org/x/text",
|
||||||
|
"SPDXRef-go-module-rsc.io/quote",
|
||||||
|
"SPDXRef-go-module-rsc.io/sampler"
|
||||||
|
],
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "golang.org/x/text",
|
||||||
|
"SPDXID": "SPDXRef-go-module-golang.org/x/text",
|
||||||
|
"downloadLocation": "go://golang.org/x/text@v0.0.0-20170915032832-14c0d48ead0c",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rsc.io/quote",
|
||||||
|
"SPDXID": "SPDXRef-go-module-rsc.io/quote",
|
||||||
|
"downloadLocation": "go://rsc.io/quote@v1.5.2",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rsc.io/sampler",
|
||||||
|
"SPDXID": "SPDXRef-go-module-rsc.io/sampler",
|
||||||
|
"downloadLocation": "go://rsc.io/sampler@v1.3.0",
|
||||||
|
"filesAnalyzed": false,
|
||||||
|
"licenseConcluded": "NOASSERTION",
|
||||||
|
"licenseDeclared": "NOASSERTION",
|
||||||
|
"copyrightText": "NOASSERTION"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -167,28 +167,28 @@ func toFileTypes(metadata *source.FileMetadata) (ty []string) {
|
|||||||
mimeTypePrefix := strings.Split(metadata.MIMEType, "/")[0]
|
mimeTypePrefix := strings.Split(metadata.MIMEType, "/")[0]
|
||||||
switch mimeTypePrefix {
|
switch mimeTypePrefix {
|
||||||
case "image":
|
case "image":
|
||||||
ty = append(ty, string(model.ImageFileType))
|
ty = append(ty, string(spdxhelpers.ImageFileType))
|
||||||
case "video":
|
case "video":
|
||||||
ty = append(ty, string(model.VideoFileType))
|
ty = append(ty, string(spdxhelpers.VideoFileType))
|
||||||
case "application":
|
case "application":
|
||||||
ty = append(ty, string(model.ApplicationFileType))
|
ty = append(ty, string(spdxhelpers.ApplicationFileType))
|
||||||
case "text":
|
case "text":
|
||||||
ty = append(ty, string(model.TextFileType))
|
ty = append(ty, string(spdxhelpers.TextFileType))
|
||||||
case "audio":
|
case "audio":
|
||||||
ty = append(ty, string(model.AudioFileType))
|
ty = append(ty, string(spdxhelpers.AudioFileType))
|
||||||
}
|
}
|
||||||
|
|
||||||
if internal.IsExecutable(metadata.MIMEType) {
|
if internal.IsExecutable(metadata.MIMEType) {
|
||||||
ty = append(ty, string(model.BinaryFileType))
|
ty = append(ty, string(spdxhelpers.BinaryFileType))
|
||||||
}
|
}
|
||||||
|
|
||||||
if internal.IsArchive(metadata.MIMEType) {
|
if internal.IsArchive(metadata.MIMEType) {
|
||||||
ty = append(ty, string(model.ArchiveFileType))
|
ty = append(ty, string(spdxhelpers.ArchiveFileType))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add support for source, spdx, and documentation file types
|
// TODO: add support for source, spdx, and documentation file types
|
||||||
if len(ty) == 0 {
|
if len(ty) == 0 {
|
||||||
ty = append(ty, string(model.OtherFileType))
|
ty = append(ty, string(spdxhelpers.OtherFileType))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ty
|
return ty
|
||||||
@ -213,12 +213,12 @@ func toRelationships(relationships []artifact.Relationship) (result []model.Rela
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupRelationship(ty artifact.RelationshipType) (bool, model.RelationshipType, string) {
|
func lookupRelationship(ty artifact.RelationshipType) (bool, spdxhelpers.RelationshipType, string) {
|
||||||
switch ty {
|
switch ty {
|
||||||
case artifact.ContainsRelationship:
|
case artifact.ContainsRelationship:
|
||||||
return true, model.ContainsRelationship, ""
|
return true, spdxhelpers.ContainsRelationship, ""
|
||||||
case artifact.OwnershipByFileOverlapRelationship:
|
case artifact.OwnershipByFileOverlapRelationship:
|
||||||
return true, model.OtherRelationship, fmt.Sprintf("%s: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by", ty)
|
return true, spdxhelpers.OtherRelationship, fmt.Sprintf("%s: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by", ty)
|
||||||
}
|
}
|
||||||
return false, "", ""
|
return false, "", ""
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||||
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -27,7 +28,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "application/vnd.unknown",
|
MIMEType: "application/vnd.unknown",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.ApplicationFileType),
|
string(spdxhelpers.ApplicationFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -36,8 +37,8 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "application/zip",
|
MIMEType: "application/zip",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.ApplicationFileType),
|
string(spdxhelpers.ApplicationFileType),
|
||||||
string(model.ArchiveFileType),
|
string(spdxhelpers.ArchiveFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -46,7 +47,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "audio/ogg",
|
MIMEType: "audio/ogg",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.AudioFileType),
|
string(spdxhelpers.AudioFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -55,7 +56,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "video/3gpp",
|
MIMEType: "video/3gpp",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.VideoFileType),
|
string(spdxhelpers.VideoFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -64,7 +65,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "text/html",
|
MIMEType: "text/html",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.TextFileType),
|
string(spdxhelpers.TextFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -73,7 +74,7 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "image/png",
|
MIMEType: "image/png",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.ImageFileType),
|
string(spdxhelpers.ImageFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -82,8 +83,8 @@ func Test_toFileTypes(t *testing.T) {
|
|||||||
MIMEType: "application/x-sharedlib",
|
MIMEType: "application/x-sharedlib",
|
||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
string(model.ApplicationFileType),
|
string(spdxhelpers.ApplicationFileType),
|
||||||
string(model.BinaryFileType),
|
string(spdxhelpers.BinaryFileType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -99,18 +100,18 @@ func Test_lookupRelationship(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
input artifact.RelationshipType
|
input artifact.RelationshipType
|
||||||
exists bool
|
exists bool
|
||||||
ty model.RelationshipType
|
ty spdxhelpers.RelationshipType
|
||||||
comment string
|
comment string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: artifact.ContainsRelationship,
|
input: artifact.ContainsRelationship,
|
||||||
exists: true,
|
exists: true,
|
||||||
ty: model.ContainsRelationship,
|
ty: spdxhelpers.ContainsRelationship,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: artifact.OwnershipByFileOverlapRelationship,
|
input: artifact.OwnershipByFileOverlapRelationship,
|
||||||
exists: true,
|
exists: true,
|
||||||
ty: model.OtherRelationship,
|
ty: spdxhelpers.OtherRelationship,
|
||||||
comment: "ownership-by-file-overlap: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by",
|
comment: "ownership-by-file-overlap: indicates that the parent package claims ownership of a child package since the parent metadata indicates overlap with a location that a cataloger found the child package by",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
10
internal/formats/spdx22json/validator.go
Normal file
10
internal/formats/spdx22json/validator.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package spdx22json
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validator(reader io.Reader) error {
|
||||||
|
_, err := decoder(reader)
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
internal/formats/spdx22tagvalue/decoder.go
Normal file
20
internal/formats/spdx22tagvalue/decoder.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package spdx22tagvalue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/spdx/tools-golang/tvloader"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decoder(reader io.Reader) (*sbom.SBOM, error) {
|
||||||
|
doc, err := tvloader.Load2_2(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to decode spdx-json: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return spdxhelpers.ToSyftModel(doc)
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@ func Format() format.Format {
|
|||||||
return format.NewFormat(
|
return format.NewFormat(
|
||||||
format.SPDXTagValueOption,
|
format.SPDXTagValueOption,
|
||||||
encoder,
|
encoder,
|
||||||
nil,
|
decoder,
|
||||||
nil,
|
validator,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -183,9 +183,11 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
|
|||||||
|
|
||||||
// note: based on the purpose above no discovered checksums should be provided, but instead, only
|
// note: based on the purpose above no discovered checksums should be provided, but instead, only
|
||||||
// tool-derived checksums.
|
// tool-derived checksums.
|
||||||
PackageChecksumSHA1: "",
|
//FIXME: this got removed between 0.1.0 and 0.2.0, is this right? it looks like
|
||||||
PackageChecksumSHA256: "",
|
// it wasn't being used anyway
|
||||||
PackageChecksumMD5: "",
|
//PackageChecksumSHA1: "",
|
||||||
|
//PackageChecksumSHA256: "",
|
||||||
|
//PackageChecksumMD5: "",
|
||||||
|
|
||||||
// 3.11: Package Home Page
|
// 3.11: Package Home Page
|
||||||
// Cardinality: optional, one
|
// Cardinality: optional, one
|
||||||
|
|||||||
10
internal/formats/spdx22tagvalue/validator.go
Normal file
10
internal/formats/spdx22tagvalue/validator.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package spdx22tagvalue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validator(reader io.Reader) error {
|
||||||
|
_, err := decoder(reader)
|
||||||
|
return err
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package syftjson
|
|||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/internal/formats/syftjson/model"
|
"github.com/anchore/syft/internal/formats/syftjson/model"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/linux"
|
"github.com/anchore/syft/syft/linux"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
@ -11,13 +12,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func toSyftModel(doc model.Document) (*sbom.SBOM, error) {
|
func toSyftModel(doc model.Document) (*sbom.SBOM, error) {
|
||||||
|
catalog := toSyftCatalog(doc.Artifacts)
|
||||||
|
|
||||||
return &sbom.SBOM{
|
return &sbom.SBOM{
|
||||||
Artifacts: sbom.Artifacts{
|
Artifacts: sbom.Artifacts{
|
||||||
PackageCatalog: toSyftCatalog(doc.Artifacts),
|
PackageCatalog: catalog,
|
||||||
LinuxDistribution: toSyftLinuxRelease(doc.Distro),
|
LinuxDistribution: toSyftLinuxRelease(doc.Distro),
|
||||||
},
|
},
|
||||||
Source: *toSyftSourceData(doc.Source),
|
Source: *toSyftSourceData(doc.Source),
|
||||||
Descriptor: toSyftDescriptor(doc.Descriptor),
|
Descriptor: toSyftDescriptor(doc.Descriptor),
|
||||||
|
Relationships: toSyftRelationships(&doc, catalog, doc.ArtifactRelationships),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +46,59 @@ func toSyftLinuxRelease(d model.LinuxRelease) *linux.Release {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toSyftRelationships(doc *model.Document, catalog *pkg.Catalog, relationships []model.Relationship) []artifact.Relationship {
|
||||||
|
idMap := make(map[string]interface{})
|
||||||
|
|
||||||
|
for _, p := range catalog.Sorted() {
|
||||||
|
idMap[string(p.ID())] = p
|
||||||
|
for _, l := range p.Locations {
|
||||||
|
idMap[string(l.Coordinates.ID())] = l.Coordinates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range doc.Files {
|
||||||
|
idMap[f.ID] = f.Location
|
||||||
|
}
|
||||||
|
|
||||||
|
var out []artifact.Relationship
|
||||||
|
for _, r := range relationships {
|
||||||
|
syftRelationship := toSyftRelationship(idMap, r)
|
||||||
|
if syftRelationship != nil {
|
||||||
|
out = append(out, *syftRelationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func toSyftRelationship(idMap map[string]interface{}, relationship model.Relationship) *artifact.Relationship {
|
||||||
|
from, ok := idMap[relationship.Parent].(artifact.Identifiable)
|
||||||
|
if !ok {
|
||||||
|
log.Warnf("relationship mapping from key %s is not a valid artifact.Identifiable type: %+v", relationship.Parent, idMap[relationship.Parent])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
to, ok := idMap[relationship.Child].(artifact.Identifiable)
|
||||||
|
if !ok {
|
||||||
|
log.Warnf("relationship mapping to key %s is not a valid artifact.Identifiable type: %+v", relationship.Child, idMap[relationship.Child])
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
typ := artifact.RelationshipType(relationship.Type)
|
||||||
|
|
||||||
|
switch typ {
|
||||||
|
case artifact.OwnershipByFileOverlapRelationship:
|
||||||
|
fallthrough
|
||||||
|
case artifact.ContainsRelationship:
|
||||||
|
default:
|
||||||
|
log.Warnf("unknown relationship type: %s", typ)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &artifact.Relationship{
|
||||||
|
From: from,
|
||||||
|
To: to,
|
||||||
|
Type: typ,
|
||||||
|
Data: relationship.Metadata,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func toSyftDescriptor(d model.Descriptor) sbom.Descriptor {
|
func toSyftDescriptor(d model.Descriptor) sbom.Descriptor {
|
||||||
return sbom.Descriptor{
|
return sbom.Descriptor{
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
|
|||||||
@ -50,11 +50,11 @@ type ApkFileRecord struct {
|
|||||||
// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
// PackageURL returns the PURL for the specific Alpine package (see https://github.com/package-url/purl-spec)
|
||||||
func (m ApkMetadata) PackageURL(distro *linux.Release) string {
|
func (m ApkMetadata) PackageURL(distro *linux.Release) string {
|
||||||
qualifiers := map[string]string{
|
qualifiers := map[string]string{
|
||||||
purlArchQualifier: m.Architecture,
|
PURLQualifierArch: m.Architecture,
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.OriginPackage != "" {
|
if m.OriginPackage != "" {
|
||||||
qualifiers[purlUpstreamQualifier] = m.OriginPackage
|
qualifiers[PURLQualifierUpstream] = m.OriginPackage
|
||||||
}
|
}
|
||||||
|
|
||||||
return packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
|
|||||||
@ -46,14 +46,14 @@ func (m DpkgMetadata) PackageURL(distro *linux.Release) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qualifiers := map[string]string{
|
qualifiers := map[string]string{
|
||||||
purlArchQualifier: m.Architecture,
|
PURLQualifierArch: m.Architecture,
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Source != "" {
|
if m.Source != "" {
|
||||||
if m.SourceVersion != "" {
|
if m.SourceVersion != "" {
|
||||||
qualifiers[purlUpstreamQualifier] = fmt.Sprintf("%s@%s", m.Source, m.SourceVersion)
|
qualifiers[PURLQualifierUpstream] = fmt.Sprintf("%s@%s", m.Source, m.SourceVersion)
|
||||||
} else {
|
} else {
|
||||||
qualifiers[purlUpstreamQualifier] = m.Source
|
qualifiers[PURLQualifierUpstream] = m.Source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -39,7 +39,11 @@ func LanguageFromPURL(p string) Language {
|
|||||||
return UnknownLanguage
|
return UnknownLanguage
|
||||||
}
|
}
|
||||||
|
|
||||||
switch purl.Type {
|
return LanguageByName(purl.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LanguageByName(name string) Language {
|
||||||
|
switch name {
|
||||||
case packageurl.TypeMaven, purlGradlePkgType:
|
case packageurl.TypeMaven, purlGradlePkgType:
|
||||||
return Java
|
return Java
|
||||||
case packageurl.TypeComposer:
|
case packageurl.TypeComposer:
|
||||||
|
|||||||
@ -106,7 +106,7 @@ func (p PythonDirectURLOriginInfo) vcsURLQualifier() packageurl.Qualifiers {
|
|||||||
if p.VCS != "" {
|
if p.VCS != "" {
|
||||||
// Taken from https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#known-qualifiers-keyvalue-pairs
|
// Taken from https://github.com/package-url/purl-spec/blob/master/PURL-SPECIFICATION.rst#known-qualifiers-keyvalue-pairs
|
||||||
// packageurl-go still doesn't support all qualifier names
|
// packageurl-go still doesn't support all qualifier names
|
||||||
return packageurl.Qualifiers{{Key: purlVCSURLQualifier, Value: fmt.Sprintf("%s+%s@%s", p.VCS, p.URL, p.CommitID)}}
|
return packageurl.Qualifiers{{Key: PURLQualifierVCSURL, Value: fmt.Sprintf("%s+%s@%s", p.VCS, p.URL, p.CommitID)}}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,15 +56,15 @@ func (m RpmdbMetadata) PackageURL(distro *linux.Release) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qualifiers := map[string]string{
|
qualifiers := map[string]string{
|
||||||
purlArchQualifier: m.Arch,
|
PURLQualifierArch: m.Arch,
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Epoch != nil {
|
if m.Epoch != nil {
|
||||||
qualifiers[purlEpochQualifier] = strconv.Itoa(*m.Epoch)
|
qualifiers[PURLQualifierEpoch] = strconv.Itoa(*m.Epoch)
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.SourceRpm != "" {
|
if m.SourceRpm != "" {
|
||||||
qualifiers[purlUpstreamQualifier] = m.SourceRpm
|
qualifiers[PURLQualifierUpstream] = m.SourceRpm
|
||||||
}
|
}
|
||||||
|
|
||||||
return packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
|
|||||||
@ -73,7 +73,11 @@ func TypeFromPURL(p string) Type {
|
|||||||
return UnknownPkg
|
return UnknownPkg
|
||||||
}
|
}
|
||||||
|
|
||||||
switch purl.Type {
|
return TypeByName(purl.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TypeByName(name string) Type {
|
||||||
|
switch name {
|
||||||
case packageurl.TypeDebian, "deb":
|
case packageurl.TypeDebian, "deb":
|
||||||
return DebPkg
|
return DebPkg
|
||||||
case packageurl.TypeRPM:
|
case packageurl.TypeRPM:
|
||||||
|
|||||||
@ -11,13 +11,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
purlArchQualifier = "arch"
|
PURLQualifierArch = "arch"
|
||||||
purlDistroQualifier = "distro"
|
PURLQualifierDistro = "distro"
|
||||||
purlEpochQualifier = "epoch"
|
PURLQualifierEpoch = "epoch"
|
||||||
purlVCSURLQualifier = "vcs_url"
|
PURLQualifierVCSURL = "vcs_url"
|
||||||
|
|
||||||
// this qualifier is not in the pURL spec, but is used by grype to perform indirect matching based on source information
|
// PURLQualifierUpstream this qualifier is not in the pURL spec, but is used by grype to perform indirect matching based on source information
|
||||||
purlUpstreamQualifier = "upstream"
|
PURLQualifierUpstream = "upstream"
|
||||||
|
|
||||||
purlCargoPkgType = "cargo"
|
purlCargoPkgType = "cargo"
|
||||||
purlGradlePkgType = "gradle"
|
purlGradlePkgType = "gradle"
|
||||||
@ -83,7 +83,7 @@ func purlQualifiers(vars map[string]string, release *linux.Release) (q packageur
|
|||||||
|
|
||||||
if release != nil && release.ID != "" && release.VersionID != "" {
|
if release != nil && release.ID != "" && release.VersionID != "" {
|
||||||
q = append(q, packageurl.Qualifier{
|
q = append(q, packageurl.Qualifier{
|
||||||
Key: purlDistroQualifier,
|
Key: PURLQualifierDistro,
|
||||||
Value: fmt.Sprintf("%s-%s", release.ID, release.VersionID),
|
Value: fmt.Sprintf("%s-%s", release.ID, release.VersionID),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user