Skip to content

Commit

Permalink
Merge pull request #182 from vulncheck-oss/add-sbom-ref
Browse files Browse the repository at this point in the history
❇️ add input sbom refs
  • Loading branch information
acidjazz authored Dec 12, 2024
2 parents 4566bd2 + abd1b62 commit eb0a49e
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 13 deletions.
70 changes: 63 additions & 7 deletions pkg/bill/bill.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bill

import (
"context"
"encoding/json"
"fmt"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/format"
Expand All @@ -17,10 +18,16 @@ import (
"github.com/vulncheck-oss/cli/pkg/session"
"github.com/vulncheck-oss/sdk-go"
"github.com/vulncheck-oss/sdk-go/pkg/client"
"io"
"os"
"strings"
)

type InputSbomRef struct {
SbomRef string
PURL string
}

func GetSBOM(dir string) (*sbom.SBOM, error) {
src, err := syft.GetSource(context.Background(), dir, nil)

Expand Down Expand Up @@ -64,22 +71,60 @@ func SaveSBOM(sbm *sbom.SBOM, file string) error {
return nil
}

func LoadSBOM(inputFile string) (*sbom.SBOM, error) {
func LoadSBOM(inputFile string) (*sbom.SBOM, []InputSbomRef, error) {
file, err := os.Open(inputFile)
if err != nil {
return nil, fmt.Errorf("unable to open SBOM file %s: %w", inputFile, err)
return nil, nil, fmt.Errorf("unable to open SBOM file %s: %w", inputFile, err)
}
defer file.Close()

// Read the entire file content
content, err := io.ReadAll(file)
if err != nil {
return nil, nil, fmt.Errorf("unable to read SBOM file %s: %w", inputFile, err)
}

// Parse JSON to extract bom-ref and purl
var rawSBOM map[string]interface{}
err = json.Unmarshal(content, &rawSBOM)
if err != nil {
return nil, nil, fmt.Errorf("unable to parse SBOM JSON from file %s: %w", inputFile, err)
}

var inputSbomRefs []InputSbomRef

// Extract bom-ref and purl from components
if components, ok := rawSBOM["components"].([]interface{}); ok {
for _, comp := range components {
if component, ok := comp.(map[string]interface{}); ok {
bomRef, bomRefOk := component["bom-ref"].(string)
purl, purlOk := component["purl"].(string)
if bomRefOk && purlOk {
inputSbomRefs = append(inputSbomRefs, InputSbomRef{
SbomRef: bomRef,
PURL: purl,
})
}
}
}
}

// Reset file pointer to the beginning for Syft to read
_, err = file.Seek(0, 0)
if err != nil {
return nil, nil, fmt.Errorf("unable to reset file pointer: %w", err)
}

// Decode SBOM using Syft
sbm, _, _, err := format.Decode(file)
if err != nil {
return nil, fmt.Errorf("unable to decode SBOM from file %s: %w", inputFile, err)
return nil, nil, fmt.Errorf("unable to decode SBOM from file %s: %w", inputFile, err)
}

return sbm, nil
return sbm, inputSbomRefs, nil
}

func GetPURLDetail(sbm *sbom.SBOM) []models.PurlDetail {
func GetPURLDetail(sbm *sbom.SBOM, inputRefs []InputSbomRef) []models.PurlDetail {

if sbm == nil {
return []models.PurlDetail{}
Expand All @@ -93,12 +138,23 @@ func GetPURLDetail(sbm *sbom.SBOM) []models.PurlDetail {
for i, l := range p.Locations.ToSlice() {
locations[i] = l.RealPath
}
purls = append(purls, models.PurlDetail{

purlDetail := models.PurlDetail{
Purl: p.PURL,
PackageType: string(p.Type),
Cataloger: p.FoundBy,
Locations: locations,
})
}

for _, ref := range inputRefs {
if ref.PURL == p.PURL {
purlDetail.SbomRef = ref.SbomRef
break
}
}

purls = append(purls, purlDetail)

}
}
return purls
Expand Down
6 changes: 3 additions & 3 deletions pkg/bill/bill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestLoadSBOM(t *testing.T) {
t.Fatalf("SaveSBOM failed: %v", err)
}

loadedSBOM, err := LoadSBOM(sbomFile)
loadedSBOM, _, err := LoadSBOM(sbomFile)
if err != nil {
t.Fatalf("LoadSBOM failed: %v", err)
}
Expand All @@ -57,14 +57,14 @@ func TestGetPURLDetail(t *testing.T) {
mockSBOM := &sbom.SBOM{}
// TODO: Populate mockSBOM with test data

purls := GetPURLDetail(mockSBOM)
purls := GetPURLDetail(mockSBOM, nil)

if len(purls) != 0 {
t.Errorf("Expected 0 PURLs, got %d", len(purls))
}

// Test with nil SBOM
nilPurls := GetPURLDetail(nil)
nilPurls := GetPURLDetail(nil, nil)
if len(nilPurls) != 0 {
t.Errorf("Expected 0 PURLs for nil SBOM, got %d", len(nilPurls))
}
Expand Down
8 changes: 5 additions & 3 deletions pkg/cmd/scan/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ func Command() *cobra.Command {
Example: i18n.C.ScanExample,
RunE: func(cmd *cobra.Command, args []string) error {

if opts.SbomInput == "" && len(args) != 1 {
if opts.SbomInput == "" && len(args) < 1 {
return ui.Error(i18n.C.ScanErrorDirectoryRequired)
}

var sbm *sbom.SBOM
var inputRefs []bill.InputSbomRef
var purls []models.PurlDetail
var vulns []models.ScanResultVulnerabilities

Expand All @@ -56,7 +57,8 @@ func Command() *cobra.Command {
Title: fmt.Sprintf("Loading SBOM from %s", opts.SbomInput),
Task: func(t *taskin.Task) error {
var err error
sbm, err = bill.LoadSBOM(opts.SbomInput)
sbm, inputRefs, err = bill.LoadSBOM(opts.SbomInput)

if err != nil {
return err
}
Expand Down Expand Up @@ -84,7 +86,7 @@ func Command() *cobra.Command {
{
Title: i18n.C.ScanExtractPurlStart,
Task: func(t *taskin.Task) error {
purls = bill.GetPURLDetail(sbm)
purls = bill.GetPURLDetail(sbm, inputRefs)
t.Title = fmt.Sprintf(i18n.C.ScanExtractPurlEnd, len(purls))
return nil
},
Expand Down
1 change: 1 addition & 0 deletions pkg/models/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type PurlDetail struct {
PackageType string `json:"type"`
Cataloger string `json:"cataloger"`
Locations []string `json:"locations"`
SbomRef string `json:"sbom_ref"`
}

type ScanResultVulnerabilities struct {
Expand Down

0 comments on commit eb0a49e

Please sign in to comment.