mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 00:43:20 +01:00
Improve CycloneDX format output (#710)
* Improve CycloneDX format output ## Additions to CycloneDX output * CPEs * Authors * Publishers * External References (Website, Distribution, VCS) * Description Signed-off-by: Sambhav Kothari <skothari44@bloomberg.net>
This commit is contained in:
parent
829e500aa9
commit
aebe843c6f
32
internal/formats/common/cyclonedxhelpers/author.go
Normal file
32
internal/formats/common/cyclonedxhelpers/author.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Author(p pkg.Package) string {
|
||||||
|
if hasMetadata(p) {
|
||||||
|
switch metadata := p.Metadata.(type) {
|
||||||
|
case pkg.NpmPackageJSONMetadata:
|
||||||
|
return metadata.Author
|
||||||
|
case pkg.PythonPackageMetadata:
|
||||||
|
author := metadata.Author
|
||||||
|
if metadata.AuthorEmail != "" {
|
||||||
|
if author == "" {
|
||||||
|
return metadata.AuthorEmail
|
||||||
|
}
|
||||||
|
author += fmt.Sprintf(" <%s>", metadata.AuthorEmail)
|
||||||
|
}
|
||||||
|
return author
|
||||||
|
case pkg.GemMetadata:
|
||||||
|
if len(metadata.Authors) > 0 {
|
||||||
|
return strings.Join(metadata.Authors, ",")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
87
internal/formats/common/cyclonedxhelpers/author_test.go
Normal file
87
internal/formats/common/cyclonedxhelpers/author_test.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Author(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "no metadata",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from gem",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.GemMetadata{
|
||||||
|
Authors: []string{
|
||||||
|
"auth1",
|
||||||
|
"auth2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth1,auth2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from npm",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
Author: "auth",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from python - just name",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.PythonPackageMetadata{
|
||||||
|
Author: "auth",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from python - just email",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.PythonPackageMetadata{
|
||||||
|
AuthorEmail: "auth@auth.gov",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth@auth.gov",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from python - both name and email",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.PythonPackageMetadata{
|
||||||
|
Author: "auth",
|
||||||
|
AuthorEmail: "auth@auth.gov",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth <auth@auth.gov>",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "empty",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
Author: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, Author(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
26
internal/formats/common/cyclonedxhelpers/component.go
Normal file
26
internal/formats/common/cyclonedxhelpers/component.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Component(p pkg.Package) cyclonedx.Component {
|
||||||
|
return cyclonedx.Component{
|
||||||
|
Type: cyclonedx.ComponentTypeLibrary,
|
||||||
|
Name: p.Name,
|
||||||
|
Version: p.Version,
|
||||||
|
PackageURL: p.PURL,
|
||||||
|
Licenses: Licenses(p),
|
||||||
|
CPE: CPE(p),
|
||||||
|
Author: Author(p),
|
||||||
|
Publisher: Publisher(p),
|
||||||
|
Description: Description(p),
|
||||||
|
ExternalReferences: ExternalReferences(p),
|
||||||
|
Properties: Properties(p),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasMetadata(p pkg.Package) bool {
|
||||||
|
return p.Metadata != nil
|
||||||
|
}
|
||||||
12
internal/formats/common/cyclonedxhelpers/cpe.go
Normal file
12
internal/formats/common/cyclonedxhelpers/cpe.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/syft/pkg"
|
||||||
|
|
||||||
|
func CPE(p pkg.Package) string {
|
||||||
|
// Since the CPEs in a package are sorted by specificity
|
||||||
|
// we can extract the first CPE as the one to output in cyclonedx
|
||||||
|
if len(p.CPEs) > 0 {
|
||||||
|
return pkg.CPEString(p.CPEs[0])
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
57
internal/formats/common/cyclonedxhelpers/cpe_test.go
Normal file
57
internal/formats/common/cyclonedxhelpers/cpe_test.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CPE(t *testing.T) {
|
||||||
|
testCPE := pkg.MustCPE("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*")
|
||||||
|
testCPE2 := pkg.MustCPE("cpe:2.3:a:name:name2:3.2:*:*:*:*:*:*:*")
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "no metadata",
|
||||||
|
input: pkg.Package{
|
||||||
|
CPEs: []pkg.CPE{},
|
||||||
|
},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single CPE",
|
||||||
|
input: pkg.Package{
|
||||||
|
CPEs: []pkg.CPE{
|
||||||
|
testCPE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple CPEs",
|
||||||
|
input: pkg.Package{
|
||||||
|
CPEs: []pkg.CPE{
|
||||||
|
testCPE2,
|
||||||
|
testCPE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "cpe:2.3:a:name:name2:3.2:*:*:*:*:*:*:*",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "empty",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, CPE(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
15
internal/formats/common/cyclonedxhelpers/description.go
Normal file
15
internal/formats/common/cyclonedxhelpers/description.go
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import "github.com/anchore/syft/syft/pkg"
|
||||||
|
|
||||||
|
func Description(p pkg.Package) string {
|
||||||
|
if hasMetadata(p) {
|
||||||
|
switch metadata := p.Metadata.(type) {
|
||||||
|
case pkg.ApkMetadata:
|
||||||
|
return metadata.Description
|
||||||
|
case pkg.NpmPackageJSONMetadata:
|
||||||
|
return metadata.Description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
56
internal/formats/common/cyclonedxhelpers/description_test.go
Normal file
56
internal/formats/common/cyclonedxhelpers/description_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Description(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "no metadata",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from apk",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Description: "a description!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "a description!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from npm",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
Description: "a description!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "a description!",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "empty",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
Homepage: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, Description(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
|
||||||
|
refs := []cyclonedx.ExternalReference{}
|
||||||
|
if hasMetadata(p) {
|
||||||
|
switch metadata := p.Metadata.(type) {
|
||||||
|
case pkg.ApkMetadata:
|
||||||
|
if metadata.URL != "" {
|
||||||
|
refs = append(refs, cyclonedx.ExternalReference{
|
||||||
|
URL: metadata.URL,
|
||||||
|
Type: cyclonedx.ERTypeDistribution,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case pkg.CargoPackageMetadata:
|
||||||
|
if metadata.Source != "" {
|
||||||
|
refs = append(refs, cyclonedx.ExternalReference{
|
||||||
|
URL: metadata.Source,
|
||||||
|
Type: cyclonedx.ERTypeDistribution,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case pkg.NpmPackageJSONMetadata:
|
||||||
|
if metadata.URL != "" {
|
||||||
|
refs = append(refs, cyclonedx.ExternalReference{
|
||||||
|
URL: metadata.URL,
|
||||||
|
Type: cyclonedx.ERTypeDistribution,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if metadata.Homepage != "" {
|
||||||
|
refs = append(refs, cyclonedx.ExternalReference{
|
||||||
|
URL: metadata.Homepage,
|
||||||
|
Type: cyclonedx.ERTypeWebsite,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case pkg.GemMetadata:
|
||||||
|
if metadata.Homepage != "" {
|
||||||
|
refs = append(refs, cyclonedx.ExternalReference{
|
||||||
|
URL: metadata.Homepage,
|
||||||
|
Type: cyclonedx.ERTypeWebsite,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
case pkg.PythonPackageMetadata:
|
||||||
|
if metadata.DirectURLOrigin != nil && metadata.DirectURLOrigin.URL != "" {
|
||||||
|
ref := cyclonedx.ExternalReference{
|
||||||
|
URL: metadata.DirectURLOrigin.URL,
|
||||||
|
Type: cyclonedx.ERTypeVCS,
|
||||||
|
}
|
||||||
|
if metadata.DirectURLOrigin.CommitID != "" {
|
||||||
|
ref.Comment = fmt.Sprintf("commit: %s", metadata.DirectURLOrigin.CommitID)
|
||||||
|
}
|
||||||
|
refs = append(refs, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(refs) > 0 {
|
||||||
|
return &refs
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_ExternalReferences(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected *[]cyclonedx.ExternalReference
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no metadata",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from apk",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
URL: "http://a-place.gov",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "http://a-place.gov", Type: cyclonedx.ERTypeDistribution},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from npm",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
URL: "http://a-place.gov",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "http://a-place.gov", Type: cyclonedx.ERTypeDistribution},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from cargo lock",
|
||||||
|
input: pkg.Package{
|
||||||
|
Name: "ansi_term",
|
||||||
|
Version: "0.12.1",
|
||||||
|
Language: pkg.Rust,
|
||||||
|
Type: pkg.RustPkg,
|
||||||
|
MetadataType: pkg.RustCargoPackageMetadataType,
|
||||||
|
Licenses: nil,
|
||||||
|
Metadata: pkg.CargoPackageMetadata{
|
||||||
|
Name: "ansi_term",
|
||||||
|
Version: "0.12.1",
|
||||||
|
Source: "registry+https://github.com/rust-lang/crates.io-index",
|
||||||
|
Checksum: "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2",
|
||||||
|
Dependencies: []string{
|
||||||
|
"winapi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "registry+https://github.com/rust-lang/crates.io-index", Type: cyclonedx.ERTypeDistribution},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from npm with homepage",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
URL: "http://a-place.gov",
|
||||||
|
Homepage: "http://homepage",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "http://a-place.gov", Type: cyclonedx.ERTypeDistribution},
|
||||||
|
{URL: "http://homepage", Type: cyclonedx.ERTypeWebsite},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from gem",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.GemMetadata{
|
||||||
|
Homepage: "http://a-place.gov",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "http://a-place.gov", Type: cyclonedx.ERTypeWebsite},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from python direct url",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.PythonPackageMetadata{
|
||||||
|
DirectURLOrigin: &pkg.PythonDirectURLOriginInfo{
|
||||||
|
URL: "http://a-place.gov",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "http://a-place.gov", Type: cyclonedx.ERTypeVCS},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from python direct url with commit",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.PythonPackageMetadata{
|
||||||
|
DirectURLOrigin: &pkg.PythonDirectURLOriginInfo{
|
||||||
|
URL: "http://a-place.gov",
|
||||||
|
CommitID: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.ExternalReference{
|
||||||
|
{URL: "http://a-place.gov", Type: cyclonedx.ERTypeVCS, Comment: "commit: test"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
URL: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, ExternalReferences(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/CycloneDX/cyclonedx-go"
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
"github.com/anchore/syft/internal"
|
"github.com/anchore/syft/internal"
|
||||||
"github.com/anchore/syft/internal/version"
|
"github.com/anchore/syft/internal/version"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/linux"
|
||||||
"github.com/anchore/syft/syft/sbom"
|
"github.com/anchore/syft/syft/sbom"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -25,13 +25,63 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
|||||||
packages := s.Artifacts.PackageCatalog.Sorted()
|
packages := s.Artifacts.PackageCatalog.Sorted()
|
||||||
components := make([]cyclonedx.Component, len(packages))
|
components := make([]cyclonedx.Component, len(packages))
|
||||||
for i, p := range packages {
|
for i, p := range packages {
|
||||||
components[i] = toComponent(p)
|
components[i] = Component(p)
|
||||||
}
|
}
|
||||||
|
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
|
||||||
cdxBOM.Components = &components
|
cdxBOM.Components = &components
|
||||||
|
|
||||||
return cdxBOM
|
return cdxBOM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||||
|
if distro == nil {
|
||||||
|
return []cyclonedx.Component{}
|
||||||
|
}
|
||||||
|
eRefs := &[]cyclonedx.ExternalReference{}
|
||||||
|
if distro.BugReportURL != "" {
|
||||||
|
*eRefs = append(*eRefs, cyclonedx.ExternalReference{
|
||||||
|
URL: distro.BugReportURL,
|
||||||
|
Type: cyclonedx.ERTypeIssueTracker,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if distro.HomeURL != "" {
|
||||||
|
*eRefs = append(*eRefs, cyclonedx.ExternalReference{
|
||||||
|
URL: distro.HomeURL,
|
||||||
|
Type: cyclonedx.ERTypeWebsite,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if distro.SupportURL != "" {
|
||||||
|
*eRefs = append(*eRefs, cyclonedx.ExternalReference{
|
||||||
|
URL: distro.SupportURL,
|
||||||
|
Type: cyclonedx.ERTypeOther,
|
||||||
|
Comment: "support",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if distro.PrivacyPolicyURL != "" {
|
||||||
|
*eRefs = append(*eRefs, cyclonedx.ExternalReference{
|
||||||
|
URL: distro.PrivacyPolicyURL,
|
||||||
|
Type: cyclonedx.ERTypeOther,
|
||||||
|
Comment: "privacyPolicy",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(*eRefs) == 0 {
|
||||||
|
eRefs = nil
|
||||||
|
}
|
||||||
|
props := getCycloneDXProperties(*distro)
|
||||||
|
if len(*props) == 0 {
|
||||||
|
props = nil
|
||||||
|
}
|
||||||
|
return []cyclonedx.Component{
|
||||||
|
{
|
||||||
|
Type: cyclonedx.ComponentTypeOS,
|
||||||
|
Name: distro.Name,
|
||||||
|
Version: distro.Version,
|
||||||
|
CPE: distro.CPEName,
|
||||||
|
ExternalReferences: eRefs,
|
||||||
|
Properties: props,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details.
|
// NewBomDescriptor returns a new BomDescriptor tailored for the current time and "syft" tool details.
|
||||||
func toBomDescriptor(name, version string, srcMetadata source.Metadata) *cyclonedx.Metadata {
|
func toBomDescriptor(name, version string, srcMetadata source.Metadata) *cyclonedx.Metadata {
|
||||||
return &cyclonedx.Metadata{
|
return &cyclonedx.Metadata{
|
||||||
@ -47,16 +97,6 @@ func toBomDescriptor(name, version string, srcMetadata source.Metadata) *cyclone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toComponent(p pkg.Package) cyclonedx.Component {
|
|
||||||
return cyclonedx.Component{
|
|
||||||
Type: cyclonedx.ComponentTypeLibrary,
|
|
||||||
Name: p.Name,
|
|
||||||
Version: p.Version,
|
|
||||||
PackageURL: p.PURL,
|
|
||||||
Licenses: toLicenses(p.Licenses),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
||||||
switch srcMetadata.Scheme {
|
switch srcMetadata.Scheme {
|
||||||
case source.ImageScheme:
|
case source.ImageScheme:
|
||||||
@ -74,20 +114,3 @@ func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLicenses(ls []string) *cyclonedx.Licenses {
|
|
||||||
if len(ls) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
lc := make(cyclonedx.Licenses, len(ls))
|
|
||||||
for i, licenseName := range ls {
|
|
||||||
lc[i] = cyclonedx.LicenseChoice{
|
|
||||||
License: &cyclonedx.License{
|
|
||||||
Name: licenseName,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &lc
|
|
||||||
}
|
|
||||||
|
|||||||
24
internal/formats/common/cyclonedxhelpers/licenses.go
Normal file
24
internal/formats/common/cyclonedxhelpers/licenses.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/internal/spdxlicense"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Licenses(p pkg.Package) *cyclonedx.Licenses {
|
||||||
|
lc := cyclonedx.Licenses{}
|
||||||
|
for _, licenseName := range p.Licenses {
|
||||||
|
if value, exists := spdxlicense.ID(licenseName); exists {
|
||||||
|
lc = append(lc, cyclonedx.LicenseChoice{
|
||||||
|
License: &cyclonedx.License{
|
||||||
|
ID: value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(lc) > 0 {
|
||||||
|
return &lc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
83
internal/formats/common/cyclonedxhelpers/licenses_test.go
Normal file
83
internal/formats/common/cyclonedxhelpers/licenses_test.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_License(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected *cyclonedx.Licenses
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no licenses",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no SPDX licenses",
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: []string{
|
||||||
|
"made-up",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with SPDX license",
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: []string{
|
||||||
|
"MIT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &cyclonedx.Licenses{
|
||||||
|
{License: &cyclonedx.License{ID: "MIT"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with SPDX license expression",
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: []string{
|
||||||
|
"MIT",
|
||||||
|
"GPL-3.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &cyclonedx.Licenses{
|
||||||
|
{License: &cyclonedx.License{ID: "MIT"}},
|
||||||
|
{License: &cyclonedx.License{ID: "GPL-3.0"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cap insensitive",
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: []string{
|
||||||
|
"gpl-3.0",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &cyclonedx.Licenses{
|
||||||
|
{License: &cyclonedx.License{ID: "GPL-3.0"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "debian to spdx conversion",
|
||||||
|
input: pkg.Package{
|
||||||
|
Licenses: []string{
|
||||||
|
"GPL-2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &cyclonedx.Licenses{
|
||||||
|
{License: &cyclonedx.License{ID: "GPL-2.0"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, Licenses(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
68
internal/formats/common/cyclonedxhelpers/properties.go
Normal file
68
internal/formats/common/cyclonedxhelpers/properties.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Properties(p pkg.Package) *[]cyclonedx.Property {
|
||||||
|
props := []cyclonedx.Property{}
|
||||||
|
props = append(props, *getCycloneDXProperties(p)...)
|
||||||
|
if len(p.Locations) > 0 {
|
||||||
|
for _, l := range p.Locations {
|
||||||
|
props = append(props, *getCycloneDXProperties(l.Coordinates)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if hasMetadata(p) {
|
||||||
|
props = append(props, *getCycloneDXProperties(p.Metadata)...)
|
||||||
|
}
|
||||||
|
if len(props) > 0 {
|
||||||
|
return &props
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCycloneDXProperties(m interface{}) *[]cyclonedx.Property {
|
||||||
|
props := []cyclonedx.Property{}
|
||||||
|
structValue := reflect.ValueOf(m)
|
||||||
|
// we can only handle top level structs as interfaces for now
|
||||||
|
if structValue.Kind() != reflect.Struct {
|
||||||
|
return &props
|
||||||
|
}
|
||||||
|
structType := structValue.Type()
|
||||||
|
for i := 0; i < structValue.NumField(); i++ {
|
||||||
|
if name, value := getCycloneDXPropertyName(structType.Field(i)), getCycloneDXPropertyValue(structValue.Field(i)); name != "" && value != "" {
|
||||||
|
props = append(props, cyclonedx.Property{
|
||||||
|
Name: name,
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &props
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCycloneDXPropertyName(field reflect.StructField) string {
|
||||||
|
if value, exists := field.Tag.Lookup("cyclonedx"); exists {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCycloneDXPropertyValue(field reflect.Value) string {
|
||||||
|
if field.IsZero() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.String, reflect.Bool, reflect.Int, reflect.Float32, reflect.Float64, reflect.Complex128, reflect.Complex64:
|
||||||
|
if field.CanInterface() {
|
||||||
|
return fmt.Sprint(field.Interface())
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
case reflect.Ptr:
|
||||||
|
return getCycloneDXPropertyValue(reflect.Indirect(field))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
138
internal/formats/common/cyclonedxhelpers/properties_test.go
Normal file
138
internal/formats/common/cyclonedxhelpers/properties_test.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CycloneDX/cyclonedx-go"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Properties(t *testing.T) {
|
||||||
|
epoch := 2
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected *[]cyclonedx.Property
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no metadata",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from apk",
|
||||||
|
input: pkg.Package{
|
||||||
|
FoundBy: "cataloger",
|
||||||
|
Locations: []source.Location{
|
||||||
|
{Coordinates: source.Coordinates{RealPath: "test"}},
|
||||||
|
},
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Package: "libc-utils",
|
||||||
|
OriginPackage: "libc-dev",
|
||||||
|
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
|
||||||
|
Version: "0.7.2-r0",
|
||||||
|
License: "BSD",
|
||||||
|
Architecture: "x86_64",
|
||||||
|
URL: "http://alpinelinux.org",
|
||||||
|
Description: "Meta package to pull in correct libc",
|
||||||
|
Size: 0,
|
||||||
|
InstalledSize: 4096,
|
||||||
|
PullDependencies: "musl-utils",
|
||||||
|
PullChecksum: "Q1p78yvTLG094tHE1+dToJGbmYzQE=",
|
||||||
|
GitCommitOfAport: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479",
|
||||||
|
Files: []pkg.ApkFileRecord{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.Property{
|
||||||
|
{Name: "foundBy", Value: "cataloger"},
|
||||||
|
{Name: "path", Value: "test"},
|
||||||
|
{Name: "originPackage", Value: "libc-dev"},
|
||||||
|
{Name: "installedSize", Value: "4096"},
|
||||||
|
{Name: "pullDependencies", Value: "musl-utils"},
|
||||||
|
{Name: "pullChecksum", Value: "Q1p78yvTLG094tHE1+dToJGbmYzQE="},
|
||||||
|
{Name: "gitCommitOfApkPort", Value: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from dpkg",
|
||||||
|
input: pkg.Package{
|
||||||
|
MetadataType: pkg.DpkgMetadataType,
|
||||||
|
Metadata: pkg.DpkgMetadata{
|
||||||
|
Package: "tzdata",
|
||||||
|
Version: "2020a-0+deb10u1",
|
||||||
|
Source: "tzdata-dev",
|
||||||
|
SourceVersion: "1.0",
|
||||||
|
Architecture: "all",
|
||||||
|
InstalledSize: 3036,
|
||||||
|
Maintainer: "GNU Libc Maintainers <debian-glibc@lists.debian.org>",
|
||||||
|
Files: []pkg.DpkgFileRecord{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.Property{
|
||||||
|
{Name: "metadataType", Value: "DpkgMetadata"},
|
||||||
|
{Name: "source", Value: "tzdata-dev"},
|
||||||
|
{Name: "sourceVersion", Value: "1.0"},
|
||||||
|
{Name: "installedSize", Value: "3036"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from go bin",
|
||||||
|
input: pkg.Package{
|
||||||
|
Name: "golang.org/x/net",
|
||||||
|
Version: "v0.0.0-20211006190231-62292e806868",
|
||||||
|
Language: pkg.Go,
|
||||||
|
Type: pkg.GoModulePkg,
|
||||||
|
MetadataType: pkg.GolangBinMetadataType,
|
||||||
|
Metadata: pkg.GolangBinMetadata{
|
||||||
|
GoCompiledVersion: "1.17",
|
||||||
|
Architecture: "amd64",
|
||||||
|
H1Digest: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k=",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.Property{
|
||||||
|
{Name: "language", Value: pkg.Go.String()},
|
||||||
|
{Name: "type", Value: "go-module"},
|
||||||
|
{Name: "metadataType", Value: "GolangBinMetadata"},
|
||||||
|
{Name: "goCompiledVersion", Value: "1.17"},
|
||||||
|
{Name: "architecture", Value: "amd64"},
|
||||||
|
{Name: "h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from rpm",
|
||||||
|
input: pkg.Package{
|
||||||
|
Name: "dive",
|
||||||
|
Version: "0.9.2-1",
|
||||||
|
Type: pkg.RpmPkg,
|
||||||
|
MetadataType: pkg.RpmdbMetadataType,
|
||||||
|
Metadata: pkg.RpmdbMetadata{
|
||||||
|
Name: "dive",
|
||||||
|
Epoch: &epoch,
|
||||||
|
Arch: "x86_64",
|
||||||
|
Release: "1",
|
||||||
|
Version: "0.9.2",
|
||||||
|
SourceRpm: "dive-0.9.2-1.src.rpm",
|
||||||
|
Size: 12406784,
|
||||||
|
License: "MIT",
|
||||||
|
Vendor: "",
|
||||||
|
Files: []pkg.RpmdbFileRecord{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &[]cyclonedx.Property{
|
||||||
|
{Name: "type", Value: "rpm"},
|
||||||
|
{Name: "metadataType", Value: "RpmdbMetadata"},
|
||||||
|
{Name: "epoch", Value: "2"},
|
||||||
|
{Name: "release", Value: "1"},
|
||||||
|
{Name: "sourceRpm", Value: "dive-0.9.2-1.src.rpm"},
|
||||||
|
{Name: "size", Value: "12406784"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, Properties(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
19
internal/formats/common/cyclonedxhelpers/publisher.go
Normal file
19
internal/formats/common/cyclonedxhelpers/publisher.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Publisher(p pkg.Package) string {
|
||||||
|
if hasMetadata(p) {
|
||||||
|
switch metadata := p.Metadata.(type) {
|
||||||
|
case pkg.ApkMetadata:
|
||||||
|
return metadata.Maintainer
|
||||||
|
case pkg.RpmdbMetadata:
|
||||||
|
return metadata.Vendor
|
||||||
|
case pkg.DpkgMetadata:
|
||||||
|
return metadata.Maintainer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
65
internal/formats/common/cyclonedxhelpers/publisher_test.go
Normal file
65
internal/formats/common/cyclonedxhelpers/publisher_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package cyclonedxhelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Publisher(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input pkg.Package
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "no metadata",
|
||||||
|
input: pkg.Package{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from apk",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Maintainer: "auth",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from rpm",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.RpmdbMetadata{
|
||||||
|
Vendor: "auth",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "from dpkg",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.DpkgMetadata{
|
||||||
|
Maintainer: "auth",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "auth",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||||
|
name: "empty",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.NpmPackageJSONMetadata{
|
||||||
|
Author: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, Publisher(test.input))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -33,8 +33,8 @@ func TestCycloneDxImageEncoder(t *testing.T) {
|
|||||||
func cycloneDxRedactor(s []byte) []byte {
|
func cycloneDxRedactor(s []byte) []byte {
|
||||||
serialPattern := regexp.MustCompile(`urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`)
|
serialPattern := regexp.MustCompile(`urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}`)
|
||||||
rfc3339Pattern := regexp.MustCompile(`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`)
|
rfc3339Pattern := regexp.MustCompile(`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`)
|
||||||
|
sha256Pattern := regexp.MustCompile(`sha256:[A-Fa-f0-9]{64}`)
|
||||||
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern} {
|
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern, sha256Pattern} {
|
||||||
s = pattern.ReplaceAll(s, []byte("redacted"))
|
s = pattern.ReplaceAll(s, []byte("redacted"))
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"bomFormat": "CycloneDX",
|
"bomFormat": "CycloneDX",
|
||||||
"specVersion": "1.3",
|
"specVersion": "1.3",
|
||||||
"serialNumber": "urn:uuid:a81dc685-cf22-48e0-bda5-65ea1a8bca5b",
|
"serialNumber": "urn:uuid:258d2616-5b1f-48cd-82a3-d6c95e262950",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"timestamp": "2021-12-03T13:17:26-08:00",
|
"timestamp": "2022-01-14T22:47:00Z",
|
||||||
"tools": [
|
"tools": [
|
||||||
{
|
{
|
||||||
"vendor": "anchore",
|
"vendor": "anchore",
|
||||||
@ -26,17 +26,78 @@
|
|||||||
"licenses": [
|
"licenses": [
|
||||||
{
|
{
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT"
|
"id": "MIT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"purl": "a-purl-2"
|
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
|
||||||
|
"purl": "a-purl-2",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "foundBy",
|
||||||
|
"value": "the-cataloger-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "language",
|
||||||
|
"value": "python"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"value": "python"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "metadataType",
|
||||||
|
"value": "PythonPackageMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"value": "/some/path/pkg1"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"purl": "a-purl-2"
|
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
|
||||||
|
"purl": "a-purl-2",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "foundBy",
|
||||||
|
"value": "the-cataloger-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"value": "deb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "metadataType",
|
||||||
|
"value": "DpkgMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"value": "/some/path/pkg1"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "operating-system",
|
||||||
|
"name": "debian",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "prettyName",
|
||||||
|
"value": "debian"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "debian"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "versionID",
|
||||||
|
"value": "1.2.3"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"bomFormat": "CycloneDX",
|
"bomFormat": "CycloneDX",
|
||||||
"specVersion": "1.3",
|
"specVersion": "1.3",
|
||||||
"serialNumber": "urn:uuid:2156ac1f-c838-4e93-8dc5-a3874ffeb967",
|
"serialNumber": "urn:uuid:8a84b1cf-e918-4842-a6a8-c7fdafc55bc0",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"timestamp": "2021-12-03T13:17:26-08:00",
|
"timestamp": "2022-01-14T22:47:00Z",
|
||||||
"tools": [
|
"tools": [
|
||||||
{
|
{
|
||||||
"vendor": "anchore",
|
"vendor": "anchore",
|
||||||
@ -26,17 +26,86 @@
|
|||||||
"licenses": [
|
"licenses": [
|
||||||
{
|
{
|
||||||
"license": {
|
"license": {
|
||||||
"name": "MIT"
|
"id": "MIT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"purl": "a-purl-1"
|
"cpe": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*",
|
||||||
|
"purl": "a-purl-1",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "foundBy",
|
||||||
|
"value": "the-cataloger-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "language",
|
||||||
|
"value": "python"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"value": "python"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "metadataType",
|
||||||
|
"value": "PythonPackageMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"value": "/somefile-1.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "layerID",
|
||||||
|
"value": "sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"name": "package-2",
|
"name": "package-2",
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"purl": "a-purl-2"
|
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
|
||||||
|
"purl": "a-purl-2",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "foundBy",
|
||||||
|
"value": "the-cataloger-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "type",
|
||||||
|
"value": "deb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "metadataType",
|
||||||
|
"value": "DpkgMetadata"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "path",
|
||||||
|
"value": "/somefile-2.txt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "layerID",
|
||||||
|
"value": "sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "operating-system",
|
||||||
|
"name": "debian",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "prettyName",
|
||||||
|
"value": "debian"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"value": "debian"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "versionID",
|
||||||
|
"value": "1.2.3"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -33,8 +33,9 @@ func TestCycloneDxImageEncoder(t *testing.T) {
|
|||||||
func cycloneDxRedactor(s []byte) []byte {
|
func cycloneDxRedactor(s []byte) []byte {
|
||||||
serialPattern := regexp.MustCompile(`serialNumber="[a-zA-Z0-9\-:]+"`)
|
serialPattern := regexp.MustCompile(`serialNumber="[a-zA-Z0-9\-:]+"`)
|
||||||
rfc3339Pattern := regexp.MustCompile(`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`)
|
rfc3339Pattern := regexp.MustCompile(`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([\+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`)
|
||||||
|
sha256Pattern := regexp.MustCompile(`sha256:[A-Fa-f0-9]{64}`)
|
||||||
|
|
||||||
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern} {
|
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern, sha256Pattern} {
|
||||||
s = pattern.ReplaceAll(s, []byte("redacted"))
|
s = pattern.ReplaceAll(s, []byte("redacted"))
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:7b1c3b1d-ea3b-4022-9dcc-80f4b4cbce36" version="1">
|
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:16a426e7-fcc7-4b94-abd2-66c67569cc44" version="1">
|
||||||
<metadata>
|
<metadata>
|
||||||
<timestamp>2021-12-03T13:16:45-08:00</timestamp>
|
<timestamp>2022-01-14T22:46:49Z</timestamp>
|
||||||
<tools>
|
<tools>
|
||||||
<tool>
|
<tool>
|
||||||
<vendor>anchore</vendor>
|
<vendor>anchore</vendor>
|
||||||
@ -20,15 +20,39 @@
|
|||||||
<version>1.0.1</version>
|
<version>1.0.1</version>
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>MIT</name>
|
<id>MIT</id>
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||||
<purl>a-purl-2</purl>
|
<purl>a-purl-2</purl>
|
||||||
|
<properties>
|
||||||
|
<property name="foundBy">the-cataloger-1</property>
|
||||||
|
<property name="language">python</property>
|
||||||
|
<property name="type">python</property>
|
||||||
|
<property name="metadataType">PythonPackageMetadata</property>
|
||||||
|
<property name="path">/some/path/pkg1</property>
|
||||||
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
<component type="library">
|
<component type="library">
|
||||||
<name>package-2</name>
|
<name>package-2</name>
|
||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
|
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||||
<purl>a-purl-2</purl>
|
<purl>a-purl-2</purl>
|
||||||
|
<properties>
|
||||||
|
<property name="foundBy">the-cataloger-2</property>
|
||||||
|
<property name="type">deb</property>
|
||||||
|
<property name="metadataType">DpkgMetadata</property>
|
||||||
|
<property name="path">/some/path/pkg1</property>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component type="operating-system">
|
||||||
|
<name>debian</name>
|
||||||
|
<version>1.2.3</version>
|
||||||
|
<properties>
|
||||||
|
<property name="prettyName">debian</property>
|
||||||
|
<property name="id">debian</property>
|
||||||
|
<property name="versionID">1.2.3</property>
|
||||||
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
</components>
|
</components>
|
||||||
</bom>
|
</bom>
|
||||||
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:66bda3e1-888a-4d43-b906-7fd96d428753" version="1">
|
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:a3d776c6-a2ca-4116-8b99-b3538dd1a460" version="1">
|
||||||
<metadata>
|
<metadata>
|
||||||
<timestamp>2021-12-03T13:16:45-08:00</timestamp>
|
<timestamp>2022-01-14T22:46:49Z</timestamp>
|
||||||
<tools>
|
<tools>
|
||||||
<tool>
|
<tool>
|
||||||
<vendor>anchore</vendor>
|
<vendor>anchore</vendor>
|
||||||
@ -20,15 +20,41 @@
|
|||||||
<version>1.0.1</version>
|
<version>1.0.1</version>
|
||||||
<licenses>
|
<licenses>
|
||||||
<license>
|
<license>
|
||||||
<name>MIT</name>
|
<id>MIT</id>
|
||||||
</license>
|
</license>
|
||||||
</licenses>
|
</licenses>
|
||||||
|
<cpe>cpe:2.3:*:some:package:1:*:*:*:*:*:*:*</cpe>
|
||||||
<purl>a-purl-1</purl>
|
<purl>a-purl-1</purl>
|
||||||
|
<properties>
|
||||||
|
<property name="foundBy">the-cataloger-1</property>
|
||||||
|
<property name="language">python</property>
|
||||||
|
<property name="type">python</property>
|
||||||
|
<property name="metadataType">PythonPackageMetadata</property>
|
||||||
|
<property name="path">/somefile-1.txt</property>
|
||||||
|
<property name="layerID">sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab</property>
|
||||||
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
<component type="library">
|
<component type="library">
|
||||||
<name>package-2</name>
|
<name>package-2</name>
|
||||||
<version>2.0.1</version>
|
<version>2.0.1</version>
|
||||||
|
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||||
<purl>a-purl-2</purl>
|
<purl>a-purl-2</purl>
|
||||||
|
<properties>
|
||||||
|
<property name="foundBy">the-cataloger-2</property>
|
||||||
|
<property name="type">deb</property>
|
||||||
|
<property name="metadataType">DpkgMetadata</property>
|
||||||
|
<property name="path">/somefile-2.txt</property>
|
||||||
|
<property name="layerID">sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67</property>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<component type="operating-system">
|
||||||
|
<name>debian</name>
|
||||||
|
<version>1.2.3</version>
|
||||||
|
<properties>
|
||||||
|
<property name="prettyName">debian</property>
|
||||||
|
<property name="id">debian</property>
|
||||||
|
<property name="versionID">1.2.3</property>
|
||||||
|
</properties>
|
||||||
</component>
|
</component>
|
||||||
</components>
|
</components>
|
||||||
</bom>
|
</bom>
|
||||||
Binary file not shown.
@ -2,14 +2,14 @@ package linux
|
|||||||
|
|
||||||
// Release represents Linux Distribution release information as specified from https://www.freedesktop.org/software/systemd/man/os-release.html
|
// Release represents Linux Distribution release information as specified from https://www.freedesktop.org/software/systemd/man/os-release.html
|
||||||
type Release struct {
|
type Release struct {
|
||||||
PrettyName string // A pretty operating system name in a format suitable for presentation to the user.
|
PrettyName string `cyclonedx:"prettyName"` // A pretty operating system name in a format suitable for presentation to the user.
|
||||||
Name string // identifies the operating system, without a version component, and suitable for presentation to the user.
|
Name string // identifies the operating system, without a version component, and suitable for presentation to the user.
|
||||||
ID string // identifies the operating system, excluding any version information and suitable for processing by scripts or usage in generated filenames.
|
ID string `cyclonedx:"id"` // identifies the operating system, excluding any version information and suitable for processing by scripts or usage in generated filenames.
|
||||||
IDLike []string // list of operating system identifiers in the same syntax as the ID= setting. It should list identifiers of operating systems that are closely related to the local operating system in regards to packaging and programming interfaces.
|
IDLike []string `cyclonedx:"idLike"` // list of operating system identifiers in the same syntax as the ID= setting. It should list identifiers of operating systems that are closely related to the local operating system in regards to packaging and programming interfaces.
|
||||||
Version string // identifies the operating system version, excluding any OS name information, possibly including a release code name, and suitable for presentation to the user.
|
Version string // identifies the operating system version, excluding any OS name information, possibly including a release code name, and suitable for presentation to the user.
|
||||||
VersionID string // identifies the operating system version, excluding any OS name information or release code name, and suitable for processing by scripts or usage in generated filenames.
|
VersionID string `cyclonedx:"versionID"` // identifies the operating system version, excluding any OS name information or release code name, and suitable for processing by scripts or usage in generated filenames.
|
||||||
Variant string // identifies a specific variant or edition of the operating system suitable for presentation to the user.
|
Variant string `cyclonedx:"variant"` // identifies a specific variant or edition of the operating system suitable for presentation to the user.
|
||||||
VariantID string // identifies a specific variant or edition of the operating system. This may be interpreted by other packages in order to determine a divergent default configuration.
|
VariantID string `cyclonedx:"variantID"` // identifies a specific variant or edition of the operating system. This may be interpreted by other packages in order to determine a divergent default configuration.
|
||||||
HomeURL string
|
HomeURL string
|
||||||
SupportURL string
|
SupportURL string
|
||||||
BugReportURL string
|
BugReportURL string
|
||||||
|
|||||||
@ -20,18 +20,18 @@ var _ FileOwner = (*ApkMetadata)(nil)
|
|||||||
// - https://git.alpinelinux.org/apk-tools/tree/src/database.c
|
// - https://git.alpinelinux.org/apk-tools/tree/src/database.c
|
||||||
type ApkMetadata struct {
|
type ApkMetadata struct {
|
||||||
Package string `mapstructure:"P" json:"package"`
|
Package string `mapstructure:"P" json:"package"`
|
||||||
OriginPackage string `mapstructure:"o" json:"originPackage"`
|
OriginPackage string `mapstructure:"o" json:"originPackage" cyclonedx:"originPackage"`
|
||||||
Maintainer string `mapstructure:"m" json:"maintainer"`
|
Maintainer string `mapstructure:"m" json:"maintainer"`
|
||||||
Version string `mapstructure:"V" json:"version"`
|
Version string `mapstructure:"V" json:"version"`
|
||||||
License string `mapstructure:"L" json:"license"`
|
License string `mapstructure:"L" json:"license"`
|
||||||
Architecture string `mapstructure:"A" json:"architecture"`
|
Architecture string `mapstructure:"A" json:"architecture"`
|
||||||
URL string `mapstructure:"U" json:"url"`
|
URL string `mapstructure:"U" json:"url"`
|
||||||
Description string `mapstructure:"T" json:"description"`
|
Description string `mapstructure:"T" json:"description"`
|
||||||
Size int `mapstructure:"S" json:"size"`
|
Size int `mapstructure:"S" json:"size" cyclonedx:"size"`
|
||||||
InstalledSize int `mapstructure:"I" json:"installedSize"`
|
InstalledSize int `mapstructure:"I" json:"installedSize" cyclonedx:"installedSize"`
|
||||||
PullDependencies string `mapstructure:"D" json:"pullDependencies"`
|
PullDependencies string `mapstructure:"D" json:"pullDependencies" cyclonedx:"pullDependencies"`
|
||||||
PullChecksum string `mapstructure:"C" json:"pullChecksum"`
|
PullChecksum string `mapstructure:"C" json:"pullChecksum" cyclonedx:"pullChecksum"`
|
||||||
GitCommitOfAport string `mapstructure:"c" json:"gitCommitOfApkPort"`
|
GitCommitOfAport string `mapstructure:"c" json:"gitCommitOfApkPort" cyclonedx:"gitCommitOfApkPort"`
|
||||||
Files []ApkFileRecord `json:"files"`
|
Files []ApkFileRecord `json:"files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -18,12 +18,12 @@ var _ FileOwner = (*DpkgMetadata)(nil)
|
|||||||
// at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section.
|
// at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section.
|
||||||
type DpkgMetadata struct {
|
type DpkgMetadata struct {
|
||||||
Package string `mapstructure:"Package" json:"package"`
|
Package string `mapstructure:"Package" json:"package"`
|
||||||
Source string `mapstructure:"Source" json:"source"`
|
Source string `mapstructure:"Source" json:"source" cyclonedx:"source"`
|
||||||
Version string `mapstructure:"Version" json:"version"`
|
Version string `mapstructure:"Version" json:"version"`
|
||||||
SourceVersion string `mapstructure:"SourceVersion" json:"sourceVersion"`
|
SourceVersion string `mapstructure:"SourceVersion" json:"sourceVersion" cyclonedx:"sourceVersion"`
|
||||||
Architecture string `mapstructure:"Architecture" json:"architecture"`
|
Architecture string `mapstructure:"Architecture" json:"architecture"`
|
||||||
Maintainer string `mapstructure:"Maintainer" json:"maintainer"`
|
Maintainer string `mapstructure:"Maintainer" json:"maintainer"`
|
||||||
InstalledSize int `mapstructure:"InstalledSize" json:"installedSize"`
|
InstalledSize int `mapstructure:"InstalledSize" json:"installedSize" cyclonedx:"installedSize"`
|
||||||
Files []DpkgFileRecord `json:"files"`
|
Files []DpkgFileRecord `json:"files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ package pkg
|
|||||||
|
|
||||||
// GolangBinMetadata represents all captured data for a Golang Binary
|
// GolangBinMetadata represents all captured data for a Golang Binary
|
||||||
type GolangBinMetadata struct {
|
type GolangBinMetadata struct {
|
||||||
GoCompiledVersion string `json:"goCompiledVersion"`
|
GoCompiledVersion string `json:"goCompiledVersion" cyclonedx:"goCompiledVersion"`
|
||||||
Architecture string `json:"architecture"`
|
Architecture string `json:"architecture" cyclonedx:"architecture"`
|
||||||
H1Digest string `json:"h1Digest"`
|
H1Digest string `json:"h1Digest" cyclonedx:"h1Digest"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,14 +17,14 @@ type Package struct {
|
|||||||
id artifact.ID `hash:"ignore"`
|
id artifact.ID `hash:"ignore"`
|
||||||
Name string // the package name
|
Name string // the package name
|
||||||
Version string // the version of the package
|
Version string // the version of the package
|
||||||
FoundBy string // the specific cataloger that discovered this package
|
FoundBy string `cyclonedx:"foundBy"` // the specific cataloger that discovered this package
|
||||||
Locations []source.Location // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
Locations []source.Location // the locations that lead to the discovery of this package (note: this is not necessarily the locations that make up this package)
|
||||||
Licenses []string // licenses discovered with the package metadata
|
Licenses []string // licenses discovered with the package metadata
|
||||||
Language Language // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
Language Language `cyclonedx:"language"` // the language ecosystem this package belongs to (e.g. JavaScript, Python, etc)
|
||||||
Type Type // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
Type Type `cyclonedx:"type"` // the package type (e.g. Npm, Yarn, Python, Rpm, Deb, etc)
|
||||||
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
|
CPEs []CPE `hash:"ignore"` // all possible Common Platform Enumerators (note: this is NOT included in the definition of the ID since all fields on a CPE are derived from other fields)
|
||||||
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec) (note: this is NOT included in the definition of the ID since all fields on a pURL are derived from other fields)
|
PURL string `hash:"ignore"` // the Package URL (see https://github.com/package-url/purl-spec) (note: this is NOT included in the definition of the ID since all fields on a pURL are derived from other fields)
|
||||||
MetadataType MetadataType // the shape of the additional data in the "metadata" field
|
MetadataType MetadataType `cyclonedx:"metadataType"` // the shape of the additional data in the "metadata" field
|
||||||
Metadata interface{} // additional data found while parsing the package source
|
Metadata interface{} // additional data found while parsing the package source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -21,11 +21,11 @@ var _ FileOwner = (*RpmdbMetadata)(nil)
|
|||||||
type RpmdbMetadata struct {
|
type RpmdbMetadata struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Epoch *int `json:"epoch"`
|
Epoch *int `json:"epoch" cyclonedx:"epoch"`
|
||||||
Arch string `json:"architecture"`
|
Arch string `json:"architecture"`
|
||||||
Release string `json:"release"`
|
Release string `json:"release" cyclonedx:"release"`
|
||||||
SourceRpm string `json:"sourceRpm"`
|
SourceRpm string `json:"sourceRpm" cyclonedx:"sourceRpm"`
|
||||||
Size int `json:"size"`
|
Size int `json:"size" cyclonedx:"size"`
|
||||||
License string `json:"license"`
|
License string `json:"license"`
|
||||||
Vendor string `json:"vendor"`
|
Vendor string `json:"vendor"`
|
||||||
Files []RpmdbFileRecord `json:"files"`
|
Files []RpmdbFileRecord `json:"files"`
|
||||||
|
|||||||
@ -10,8 +10,8 @@ import (
|
|||||||
|
|
||||||
// Coordinates contains the minimal information needed to describe how to find a file within any possible source object (e.g. image and directory sources)
|
// Coordinates contains the minimal information needed to describe how to find a file within any possible source object (e.g. image and directory sources)
|
||||||
type Coordinates struct {
|
type Coordinates struct {
|
||||||
RealPath string `json:"path"` // The path where all path ancestors have no hardlinks / symlinks
|
RealPath string `json:"path" cyclonedx:"path"` // The path where all path ancestors have no hardlinks / symlinks
|
||||||
FileSystemID string `json:"layerID,omitempty"` // An ID representing the filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
|
FileSystemID string `json:"layerID,omitempty" cyclonedx:"layerID"` // An ID representing the filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoordinateSet represents a set of string types.
|
// CoordinateSet represents a set of string types.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user