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

More specific cyclone dx parsing #258

Merged
merged 5 commits into from
Mar 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
604 changes: 604 additions & 0 deletions cmd/osv-scanner/fixtures/locks-many/alpine.cdx.xml

Large diffs are not rendered by default.

5,110 changes: 5,110 additions & 0 deletions cmd/osv-scanner/fixtures/sbom-insecure/postgres-stretch.cdx.xml

Large diffs are not rendered by default.

23 changes: 22 additions & 1 deletion cmd/osv-scanner/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,27 @@ func TestRun(t *testing.T) {
wantExitCode: 0,
wantStdout: `
Scanning dir ./fixtures/locks-many/composer.lock
Scanned %%/fixtures/locks-many/composer.lock file and found 1 packages
Scanned %%/fixtures/locks-many/composer.lock file and found 1 packages
`,
wantStderr: "",
},
// one specific supported sbom with vulns
{
name: "",
args: []string{"", "./fixtures/sbom-insecure/postgres-stretch.cdx.xml"},
wantExitCode: 1,
wantStdout: `
Scanning dir ./fixtures/sbom-insecure/postgres-stretch.cdx.xml
Scanned %%/fixtures/sbom-insecure/postgres-stretch.cdx.xml as CycloneDX SBOM and found 136 packages
+-------------------------------------+-----------+---------+------------------------------------+-------------------------------------------------+
| OSV URL (ID IN BOLD) | ECOSYSTEM | PACKAGE | VERSION | SOURCE |
+-------------------------------------+-----------+---------+------------------------------------+-------------------------------------------------+
| https://osv.dev/GHSA-v95c-p5hm-xq8f | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GO-2022-0274 | | | | |
| https://osv.dev/GHSA-f3fp-gc8g-vw66 | Go | runc | v1.0.1 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GHSA-p782-xgp4-8hr8 | Go | sys | v0.0.0-20210817142637-7d9622a276b7 | fixtures/sbom-insecure/postgres-stretch.cdx.xml |
| https://osv.dev/GO-2022-0493 | | | | |
+-------------------------------------+-----------+---------+------------------------------------+-------------------------------------------------+
`,
wantStderr: "",
},
Expand All @@ -152,6 +172,7 @@ func TestRun(t *testing.T) {
wantStdout: `
Scanning dir ./fixtures/locks-many
Scanned %%/fixtures/locks-many/Gemfile.lock file and found 1 packages
Scanned %%/fixtures/locks-many/alpine.cdx.xml as CycloneDX SBOM and found 15 packages
Scanned %%/fixtures/locks-many/composer.lock file and found 1 packages
Scanned %%/fixtures/locks-many/yarn.lock file and found 1 packages
`,
Expand Down
25 changes: 25 additions & 0 deletions internal/sbom/cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sbom
import (
"fmt"
"io"
"path/filepath"
"strings"

"github.com/CycloneDX/cyclonedx-go"
Expand All @@ -21,6 +22,30 @@ func (c *CycloneDX) Name() string {
return "CycloneDX"
}

func (c *CycloneDX) MatchesRecognizedFileNames(path string) bool {
// See https://cyclonedx.org/specification/overview/#recognized-file-patterns
expectedGlobs := []string{
"bom.xml",
"bom.json",
"*.cdx.json",
"*.cdx.xml",
}
filename := filepath.Base(path)
for _, v := range expectedGlobs {
matched, err := filepath.Match(v, filename)
if err != nil {
// Just panic since the only error is invalid glob pattern
panic("Glob pattern is invalid: " + err.Error())
}

if matched {
return true
}
}

return false
}

func (c *CycloneDX) enumerateComponents(components []cyclonedx.Component, callback func(Identifier) error) error {
for _, component := range components {
if component.PackageURL != "" {
Expand Down
2 changes: 2 additions & 0 deletions internal/sbom/sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ type Identifier struct {
// SBOMReader is an interface for all SBOM providers.
type SBOMReader interface {
Name() string
// Checks if the file path is a standard recognized file name
MatchesRecognizedFileNames(string) bool
GetPackages(io.ReadSeeker, func(Identifier) error) error
}

Expand Down
10 changes: 9 additions & 1 deletion internal/sbom/spdx.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ package sbom
import (
"fmt"
"io"
"path/filepath"
"strings"

"github.com/spdx/tools-golang/json"
spdx_json "github.com/spdx/tools-golang/json"
"github.com/spdx/tools-golang/rdfloader"
"github.com/spdx/tools-golang/spdx/v2_3"
"github.com/spdx/tools-golang/tvloader"
Expand All @@ -26,6 +28,12 @@ func (s *SPDX) Name() string {
return "SPDX"
}

func (s *SPDX) MatchesRecognizedFileNames(path string) bool {
// All spdx files should have the .spdx in the filename, even if
// it's not the extension: https://spdx.github.io/spdx-spec/v2.3/conformance/
return strings.Contains(strings.ToLower(filepath.Base(path)), ".spdx")
}

func (s *SPDX) enumeratePackages(doc *v2_3.Document, callback func(Identifier) error) error {
for _, p := range doc.Packages {
for _, r := range p.PackageExternalReferences {
Expand Down
26 changes: 13 additions & 13 deletions pkg/osvscanner/osvscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,22 +222,22 @@ func scanLockfile(r *output.Reporter, query *osv.BatchedQuery, path string, pars
// scanSBOMFile will load, identify, and parse the SBOM path passed in, and add the dependencies specified
// within to `query`
func scanSBOMFile(r *output.Reporter, query *osv.BatchedQuery, path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()

for _, provider := range sbom.Providers {
if provider.Name() == "SPDX" &&
!strings.Contains(strings.ToLower(filepath.Base(path)), ".spdx") {
// All spdx files should have the .spdx in the filename, even if
// it's not the extension: https://spdx.github.io/spdx-spec/v2.3/conformance/
// Skip if this isn't the case to avoid panics
if !provider.MatchesRecognizedFileNames(path) {
// Skip if filename is not usually a sbom file of this format
continue
}

// Opening file inside loop is OK, since providers is not very long,
// and it is unlikely that multiple providers accept the same file name
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()

count := 0
err := provider.GetPackages(file, func(id sbom.Identifier) error {
err = provider.GetPackages(file, func(id sbom.Identifier) error {
purlQuery := osv.MakePURLRequest(id.PURL)
purlQuery.Source = models.SourceInfo{
Path: path,
Expand All @@ -250,7 +250,7 @@ func scanSBOMFile(r *output.Reporter, query *osv.BatchedQuery, path string) erro
})
if err == nil {
// Found the right format.
r.PrintText(fmt.Sprintf("Scanned %s SBOM and found %d packages\n", provider.Name(), count))
r.PrintText(fmt.Sprintf("Scanned %s as %s SBOM and found %d packages\n", path, provider.Name(), count))
return nil
}

Expand Down