Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CycloneDX decoder #811

Merged
merged 16 commits into from
Feb 18, 2022
2 changes: 1 addition & 1 deletion internal/formats/common/cyclonedxhelpers/author.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/anchore/syft/syft/pkg"
)

func Author(p pkg.Package) string {
func encodeAuthor(p pkg.Package) string {
if hasMetadata(p) {
switch metadata := p.Metadata.(type) {
case pkg.NpmPackageJSONMetadata:
Expand Down
4 changes: 2 additions & 2 deletions internal/formats/common/cyclonedxhelpers/author_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)

func Test_Author(t *testing.T) {
func Test_encodeAuthor(t *testing.T) {
tests := []struct {
name string
input pkg.Package
Expand Down Expand Up @@ -81,7 +81,7 @@ func Test_Author(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, Author(test.input))
assert.Equal(t, test.expected, encodeAuthor(test.input))
})
}
}
97 changes: 88 additions & 9 deletions internal/formats/common/cyclonedxhelpers/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,102 @@ import (
"github.com/anchore/syft/syft/pkg"
)

func Component(p pkg.Package) cyclonedx.Component {
func encodeComponent(p pkg.Package) cyclonedx.Component {
return cyclonedx.Component{
Type: cyclonedx.ComponentTypeLibrary,
Name: p.Name,
Group: Group(p),
Group: encodeGroup(p),
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),
Licenses: encodeLicenses(p),
CPE: encodeCPE(p),
Author: encodeAuthor(p),
Publisher: encodePublisher(p),
Description: encodeDescription(p),
ExternalReferences: encodeExternalReferences(p),
Properties: encodeProperties(p),
}
}

func hasMetadata(p pkg.Package) bool {
return p.Metadata != nil
}

func decodeComponent(c *cyclonedx.Component) *pkg.Package {
typ := pkg.Type(prop(c, "type"))
purl := c.PackageURL
if typ == "" && purl != "" {
typ = pkg.TypeFromPURL(purl)
}

metaType, meta := decodePackageMetadata(c)

p := &pkg.Package{
Name: c.Name,
Version: c.Version,
FoundBy: prop(c, "foundBy"),
Locations: nil,
Licenses: decodeLicenses(c),
Language: pkg.Language(prop(c, "language")),
Type: typ,
CPEs: decodeCPEs(c),
PURL: purl,
MetadataType: metaType,
Metadata: meta,
}

return p
}

func decodePackageMetadata(c *cyclonedx.Component) (pkg.MetadataType, interface{}) {
if c.Properties != nil {
typ := prop(c, "metadataType")
if typ != "" {
switch typ {
case "ApkMetadata":
return pkg.ApkMetadataType, pkg.ApkMetadata{
Package: prop(c, "package"),
OriginPackage: prop(c, "originPackage"),
Maintainer: prop(c, "maintainer"),
Version: prop(c, "version"),
License: prop(c, "license"),
Architecture: prop(c, "architecture"),
URL: prop(c, "url"),
Description: prop(c, "description"),
Size: propInt(c, "size"),
InstalledSize: propInt(c, "installedSize"),
PullDependencies: prop(c, "pullDependencies"),
PullChecksum: prop(c, "pullChecksum"),
GitCommitOfAport: prop(c, "gitCommitOfAport"),
Files: []pkg.ApkFileRecord{},
}
case "DpkgMetadata":
return pkg.DpkgMetadataType, pkg.DpkgMetadata{
Package: prop(c, "package"),
Source: prop(c, "source"),
Version: prop(c, "version"),
SourceVersion: prop(c, "sourceVersion"),
Architecture: prop(c, "architecture"),
Maintainer: prop(c, "maintainer"),
InstalledSize: propInt(c, "installedSize"),
Files: []pkg.DpkgFileRecord{},
}
case "RpmdbMetadata":
return pkg.RpmdbMetadataType, pkg.RpmdbMetadata{
Name: prop(c, "name"),
Version: prop(c, "version"),
Epoch: propIntNil(c, "epoch"),
Arch: prop(c, "arch"),
Release: prop(c, "release"),
SourceRpm: prop(c, "sourceRpm"),
Size: propInt(c, "size"),
License: prop(c, "license"),
Vendor: prop(c, "vendor"),
Files: []pkg.RpmdbFileRecord{},
}
}
}
}

return pkg.UnknownMetadataType, nil
}
31 changes: 28 additions & 3 deletions internal/formats/common/cyclonedxhelpers/cpe.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,37 @@
package cyclonedxhelpers

import "github.com/anchore/syft/syft/pkg"
import (
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
)

func CPE(p pkg.Package) string {
func encodeCPE(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
// we can extract the first encodeCPE as the one to output in cyclonedx
if len(p.CPEs) > 0 {
return pkg.CPEString(p.CPEs[0])
}
return ""
}

func decodeCPEs(c *cyclonedx.Component) []pkg.CPE {
// FIXME -- why are we not encoding all the CPEs and what is the right behavior to decode them?
kzantow marked this conversation as resolved.
Show resolved Hide resolved
cpes := cpe.Generate(pkg.Package{
Name: c.Name,
Version: c.Version,
PURL: c.PackageURL,
})

if c.CPE != "" {
cp, err := pkg.NewCPE(c.CPE)
if err != nil {
log.Warnf("invalid CPE: %s", c.CPE)
} else {
cpes = append(cpes, cp)
}
}

return cpes
}
4 changes: 2 additions & 2 deletions internal/formats/common/cyclonedxhelpers/cpe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
)

func Test_CPE(t *testing.T) {
func Test_encodeCPE(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 {
Expand Down Expand Up @@ -51,7 +51,7 @@ func Test_CPE(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.expected, CPE(test.input))
assert.Equal(t, test.expected, encodeCPE(test.input))
})
}
}
Loading