Skip to content

Commit

Permalink
fix: CycloneDX SBOM support introduced in #111
Browse files Browse the repository at this point in the history
  • Loading branch information
abhisek committed Aug 16, 2023
1 parent 7fea4f3 commit fe6fc26
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 38 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,23 @@ vet scan --lockfiles /path/to/package-lock.json

> [Example Security Gate](https://github.com/safedep/demo-client-java/pull/2) using `vet` to prevent introducing new OSS dependency risk in an application.
#### Scanning SBOM

- To scan an SBOM in [CycloneDX](https://cyclonedx.org/) format

```bash
vet scan --lockfiles /path/to/cyclonedx-sbom.json --lockfile-as bom-cyclonedx
```

> **Note:** SBOM scanning feature is currently in experimental stage
#### Available Parsers

- To list supported package manifest parsers including experimental modules

```bash
vet scan parsers --experimental
```

## 📖 Documentation

Expand Down
7 changes: 7 additions & 0 deletions docs/docs/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,11 @@ vet scan --lockfiles /path/to/requirements.txt
vet scan --lockfiles /path/to/package-lock.json
```

:::info

To list all available package manifest parsers run
`vet scan parsers --experimental`

:::

![vet scan files](/img/vet/vet-scan-files.png)
4 changes: 2 additions & 2 deletions gen/exceptionsapi/exceptions_spec.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions gen/filterinput/filter_input_spec.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions gen/filtersuite/filter_suite_spec.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/safedep/vet
go 1.18

require (
github.com/CycloneDX/cyclonedx-go v0.7.1
github.com/deepmap/oapi-codegen v1.13.3
github.com/golang/protobuf v1.5.3
github.com/google/cel-go v0.17.1
Expand All @@ -19,7 +20,6 @@ require (

require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/CycloneDX/cyclonedx-go v0.7.1 // indirect
github.com/Masterminds/semver/v3 v3.2.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/g
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0=
Expand Down
23 changes: 9 additions & 14 deletions pkg/parser/cyclonedx_sbom.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
package parser

import (
"bufio"
"fmt"
// "errors"
"os"
"strings"
"bufio"

cdx "github.com/CycloneDX/cyclonedx-go"
"github.com/google/osv-scanner/pkg/lockfile"
"github.com/safedep/vet/pkg/common/logger"
cdx "github.com/CycloneDX/cyclonedx-go"
)

// https://packaging.python.org/en/latest/specifications/binary-distribution-format/
func parseCyclonedxSBOM(pathToLockfile string) ([]lockfile.PackageDetails, error) {
details := []lockfile.PackageDetails{}

bom := new(cdx.BOM)
if file, err := os.Open(pathToLockfile); err != nil {
logger.Warnf("Error opening sbom file %v", err)
return nil, err
} else {
sbom_content := bufio.NewReader(file)
Expand All @@ -27,19 +24,15 @@ func parseCyclonedxSBOM(pathToLockfile string) ([]lockfile.PackageDetails, error
return nil, err
}
}

// fmt.Printf("%v", bom.Components)

for _, comp := range *bom.Components {
if d, err := convertSbomComponent2LPD(&comp); err != nil {
// fmt.Println(err)
logger.Warnf("Failed Converting sbom to lockfile component. %v", err)
logger.Warnf("Failed converting sbom to lockfile component: %v", err)
} else {
// fmt.Println(*d)
details = append(details, *d)
}
}

// fmt.Printf("%v", details)
return details, nil
}

Expand All @@ -50,6 +43,7 @@ func convertSbomComponent2LPD(comp *cdx.Component) (*lockfile.PackageDetails, er
} else {
name = comp.Name
}

var ecosysystem lockfile.Ecosystem
if eco, err := convertBomRefAsEcosystem(comp.BOMRef); err != nil {
return nil, err
Expand All @@ -69,10 +63,11 @@ func convertSbomComponent2LPD(comp *cdx.Component) (*lockfile.PackageDetails, er

func convertBomRefAsEcosystem(bomref string) (lockfile.Ecosystem, error) {
if strings.Contains(bomref, "pkg:pypi") {
return lockfile.PipEcosystem, nil
return lockfile.PipEcosystem, nil
} else if strings.Contains(bomref, "pkg:npm") {
return lockfile.NpmEcosystem, nil
return lockfile.NpmEcosystem, nil
} else {
return lockfile.NpmEcosystem, fmt.Errorf("Failed parsing %s to ecosystem", bomref)
// Return an error, the ecosystem here does not matter
return lockfile.NpmEcosystem, fmt.Errorf("failed parsing bomref %s to ecosystem", bomref)
}
}
27 changes: 14 additions & 13 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,23 @@ import (
)

const (
customParserTypePyWheel = "python-wheel"
customParserTypePyWheel = "python-wheel"
customParserCycloneDXSBOM = "bom-cyclonedx"
)

// We are supporting only those ecosystems for which we have data
// for enrichment. More ecosystems will be supported as we improve
// the capability of our Insights API
var supportedEcosystems map[string]bool = map[string]bool{
models.EcosystemGo: true,
models.EcosystemMaven: true,
models.EcosystemNpm: true,
models.EcosystemPyPI: true,
models.EcosystemCyDxSBOM: true,
models.EcosystemGo: true,
models.EcosystemMaven: true,
models.EcosystemNpm: true,
models.EcosystemPyPI: true,
models.EcosystemCyDxSBOM: true,
}

var customExperimentalParsers map[string]lockfile.PackageDetailsParser = map[string]lockfile.PackageDetailsParser{
customParserTypePyWheel: parsePythonWheelDist,
customParserTypePyWheel: parsePythonWheelDist,
customParserCycloneDXSBOM: parseCyclonedxSBOM,
}

Expand All @@ -39,7 +39,7 @@ type parserWrapper struct {
parseAs string
}

func List() []string {
func List(experimental bool) []string {
supportedParsers := make([]string, 0, 0)
parsers := lockfile.ListParsers()

Expand All @@ -52,10 +52,11 @@ func List() []string {
supportedParsers = append(supportedParsers, p)
}

// //In order to show users, even the list of custom parsers
// for p := range customExperimentalParsers {
// supportedParsers = append(supportedParsers, p)
// }
if experimental {
for p := range customExperimentalParsers {
supportedParsers = append(supportedParsers, p)
}
}

return supportedParsers
}
Expand Down Expand Up @@ -121,7 +122,7 @@ func (pw *parserWrapper) Ecosystem() string {
case customParserCycloneDXSBOM:
return models.EcosystemCyDxSBOM
default:
logger.Debugf("Unsupported lockfile-as %s. Skipping...", pw.parseAs)
logger.Debugf("Unsupported lockfile-as %s", pw.parseAs)
return ""
}
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func TestListParser(t *testing.T) {
parsers := List()
parsers := List(false)
assert.Equal(t, 10, len(parsers))
}

Expand All @@ -17,7 +17,7 @@ func TestInvalidEcosystemMapping(t *testing.T) {
}

func TestEcosystemMapping(t *testing.T) {
for _, lf := range List() {
for _, lf := range List(false) {
t.Run(lf, func(t *testing.T) {
pw := &parserWrapper{parseAs: lf}
assert.NotEmpty(t, pw.Ecosystem())
Expand Down
10 changes: 8 additions & 2 deletions scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ var (
syncReport bool
syncReportProject string
syncReportStream string
listExperimentalParsers bool
)

func newScanCommand() *cobra.Command {
Expand Down Expand Up @@ -100,20 +101,25 @@ func newScanCommand() *cobra.Command {
}

func listParsersCommand() *cobra.Command {
return &cobra.Command{
cmd := &cobra.Command{
Use: "parsers",
Short: "List available lockfile parsers",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("Available Lockfile Parsers\n")
fmt.Printf("==========================\n\n")

for idx, p := range parser.List() {
for idx, p := range parser.List(listExperimentalParsers) {
fmt.Printf("[%d] %s\n", idx, p)
}

return nil
},
}

cmd.Flags().BoolVarP(&listExperimentalParsers, "experimental", "", false,
"Include experimental parsers in the list")

return cmd
}

func startScan() {
Expand Down

0 comments on commit fe6fc26

Please sign in to comment.