mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
Add dependencies to cyclonedx (#768)
Add dependencies to cyclonedx Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> Co-authored-by: hectorj2f <hectorf@vmware.com>
This commit is contained in:
parent
161fa7be4a
commit
024a5a9f3f
@ -5,7 +5,9 @@ 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/log"
|
||||||
"github.com/anchore/syft/internal/version"
|
"github.com/anchore/syft/internal/version"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/linux"
|
"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"
|
||||||
@ -29,6 +31,12 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
|||||||
}
|
}
|
||||||
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
|
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
|
||||||
cdxBOM.Components = &components
|
cdxBOM.Components = &components
|
||||||
|
|
||||||
|
dependencies := toDependencies(s.Relationships)
|
||||||
|
if len(dependencies) > 0 {
|
||||||
|
cdxBOM.Dependencies = &dependencies
|
||||||
|
}
|
||||||
|
|
||||||
return cdxBOM
|
return cdxBOM
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,18 +105,66 @@ func toBomDescriptor(name, version string, srcMetadata source.Metadata) *cyclone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// used to indicate that a relationship listed under the syft artifact package can be represented as a cyclonedx dependency.
|
||||||
|
// NOTE: CycloneDX provides the ability to describe components and their dependency on other components.
|
||||||
|
// The dependency graph is capable of representing both direct and transitive relationships.
|
||||||
|
// If a relationship is either direct or transitive it can be included in this function.
|
||||||
|
// An example of a relationship to not include would be: OwnershipByFileOverlapRelationship.
|
||||||
|
func isExpressiblePackageRelationship(ty artifact.RelationshipType) bool {
|
||||||
|
switch ty {
|
||||||
|
case artifact.RuntimeDependencyOfRelationship:
|
||||||
|
return true
|
||||||
|
case artifact.DevDependencyOfRelationship:
|
||||||
|
return true
|
||||||
|
case artifact.BuildDependencyOfRelationship:
|
||||||
|
return true
|
||||||
|
case artifact.DependencyOfRelationship:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependency {
|
||||||
|
result := make([]cyclonedx.Dependency, 0)
|
||||||
|
for _, r := range relationships {
|
||||||
|
exists := isExpressiblePackageRelationship(r.Type)
|
||||||
|
if !exists {
|
||||||
|
log.Warnf("unable to convert relationship from CycloneDX 1.3 JSON, dropping: %+v", r)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
innerDeps := []cyclonedx.Dependency{}
|
||||||
|
innerDeps = append(innerDeps, cyclonedx.Dependency{Ref: string(r.From.ID())})
|
||||||
|
result = append(result, cyclonedx.Dependency{
|
||||||
|
Ref: string(r.To.ID()),
|
||||||
|
Dependencies: &innerDeps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
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:
|
||||||
|
bomRef, err := artifact.IDByHash(srcMetadata.ImageMetadata.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to get fingerprint of image metadata=%s: %+v", srcMetadata.ImageMetadata.ID, err)
|
||||||
|
}
|
||||||
return &cyclonedx.Component{
|
return &cyclonedx.Component{
|
||||||
|
BOMRef: string(bomRef),
|
||||||
Type: cyclonedx.ComponentTypeContainer,
|
Type: cyclonedx.ComponentTypeContainer,
|
||||||
Name: srcMetadata.ImageMetadata.UserInput,
|
Name: srcMetadata.ImageMetadata.UserInput,
|
||||||
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
||||||
}
|
}
|
||||||
case source.DirectoryScheme, source.FileScheme:
|
case source.DirectoryScheme, source.FileScheme:
|
||||||
|
bomRef, err := artifact.IDByHash(srcMetadata.Path)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("unable to get fingerprint of source metadata path=%s: %+v", srcMetadata.Path, err)
|
||||||
|
}
|
||||||
return &cyclonedx.Component{
|
return &cyclonedx.Component{
|
||||||
Type: cyclonedx.ComponentTypeFile,
|
BOMRef: string(bomRef),
|
||||||
Name: srcMetadata.Path,
|
Type: cyclonedx.ComponentTypeFile,
|
||||||
|
Name: srcMetadata.Path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -37,5 +37,8 @@ func cycloneDxRedactor(s []byte) []byte {
|
|||||||
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern, sha256Pattern} {
|
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern, sha256Pattern} {
|
||||||
s = pattern.ReplaceAll(s, []byte("redacted"))
|
s = pattern.ReplaceAll(s, []byte("redacted"))
|
||||||
}
|
}
|
||||||
|
// the bom-ref will be autogenerated every time, the value here should not be directly tested in snapshot tests
|
||||||
|
s = regexp.MustCompile(` "bom-ref": .*\n`).ReplaceAll(s, []byte(""))
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,5 +38,13 @@ func cycloneDxRedactor(s []byte) []byte {
|
|||||||
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern, sha256Pattern} {
|
for _, pattern := range []*regexp.Regexp{serialPattern, rfc3339Pattern, sha256Pattern} {
|
||||||
s = pattern.ReplaceAll(s, []byte("redacted"))
|
s = pattern.ReplaceAll(s, []byte("redacted"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the bom-ref will be autogenerated every time, the value here should not be directly tested in snapshot tests
|
||||||
|
bomRefPattern := regexp.MustCompile(` bom-ref="[a-zA-Z0-9\-:]+"`)
|
||||||
|
bomRef3339Pattern := 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]))`)
|
||||||
|
for _, pattern := range []*regexp.Regexp{bomRefPattern, bomRef3339Pattern} {
|
||||||
|
s = pattern.ReplaceAll(s, []byte(""))
|
||||||
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,18 @@ const (
|
|||||||
|
|
||||||
// ContainsRelationship (supports any-to-any linkages) is a proxy for the SPDX 2.2 CONTAINS relationship.
|
// ContainsRelationship (supports any-to-any linkages) is a proxy for the SPDX 2.2 CONTAINS relationship.
|
||||||
ContainsRelationship RelationshipType = "contains"
|
ContainsRelationship RelationshipType = "contains"
|
||||||
|
|
||||||
|
// RuntimeDependencyOfRelationship is a proxy for the SPDX 2.2.1 RUNTIME_DEPENDENCY_OF relationship.
|
||||||
|
RuntimeDependencyOfRelationship RelationshipType = "runtime-dependency-of"
|
||||||
|
|
||||||
|
// DevDependencyOfRelationship is a proxy for the SPDX 2.2.1 DEV_DEPENDENCY_OF relationship.
|
||||||
|
DevDependencyOfRelationship RelationshipType = "dev-dependency-of"
|
||||||
|
|
||||||
|
// BuildDependencyOfRelationship is a proxy for the SPDX 2.2.1 BUILD_DEPENDENCY_OF relationship.
|
||||||
|
BuildDependencyOfRelationship RelationshipType = "build-dependency-of"
|
||||||
|
|
||||||
|
// DependencyOfRelationship is a proxy for the SPDX 2.2.1 DEPENDENCY_OF relationship.
|
||||||
|
DependencyOfRelationship RelationshipType = "dependency-of"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RelationshipType string
|
type RelationshipType string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user