Skip to content

Commit 1b82676

Browse files
authored
Add testing and fixed scorecard ingestion (#140)
* Add testing and fixed scorecard ingestion - Added testing for scorecard ingestion in the service and in pkg - Fixed scorecard ingestion - Moved LoadDataFromPath to cmd, cutting the dependency of cmd on pkg Signed-off-by: neilnaveen <[email protected]> * Deal with errors better Signed-off-by: neilnaveen <[email protected]> --------- Signed-off-by: neilnaveen <[email protected]>
1 parent ebb5d42 commit 1b82676

File tree

11 files changed

+255
-178
lines changed

11 files changed

+255
-178
lines changed

api/v1/service_test.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/RoaringBitmap/roaring"
1010
service "github.com/bitbomdev/minefield/gen/api/v1"
1111
"github.com/bitbomdev/minefield/pkg/graph"
12-
"github.com/bitbomdev/minefield/pkg/tools/ingest"
1312
"github.com/stretchr/testify/assert"
1413
"github.com/stretchr/testify/require"
1514
"google.golang.org/protobuf/types/known/emptypb"
@@ -73,23 +72,26 @@ func TestGetNodesByGlob(t *testing.T) {
7372
func TestQueriesIngestAndCache(t *testing.T) {
7473
s := setupService()
7574

76-
result, err := ingest.LoadDataFromPath("../../testdata/osv-sboms/google_agi.sbom.json")
75+
// Read and ingest SBOM file
76+
sbomData, err := os.ReadFile("../../testdata/osv-sboms/google_agi.sbom.json")
7777
require.NoError(t, err)
78-
for _, data := range result {
79-
sbomReq := connect.NewRequest(&service.IngestSBOMRequest{
80-
Sbom: data.Data,
81-
})
82-
_, err = s.IngestSBOM(context.Background(), sbomReq)
83-
require.NoError(t, err)
84-
}
8578

86-
content, err := os.ReadFile("../../testdata/osv-vulns/GHSA-cx63-2mw6-8hw5.json")
79+
sbomReq := connect.NewRequest(&service.IngestSBOMRequest{
80+
Sbom: sbomData,
81+
})
82+
_, err = s.IngestSBOM(context.Background(), sbomReq)
8783
require.NoError(t, err)
84+
85+
// Read and ingest vulnerability file
86+
vulnData, err := os.ReadFile("../../testdata/osv-vulns/GHSA-cx63-2mw6-8hw5.json")
87+
require.NoError(t, err)
88+
8889
vulnReq := connect.NewRequest(&service.IngestVulnerabilityRequest{
89-
Vulnerability: content,
90+
Vulnerability: vulnData,
9091
})
9192
_, err = s.IngestVulnerability(context.Background(), vulnReq)
9293
require.NoError(t, err)
94+
9395
// Check if the node with name "pkg:pypi/[email protected]" exists
9496
graphReq := connect.NewRequest(&service.GetNodeByNameRequest{Name: "pkg:pypi/[email protected]"})
9597
resp, err := s.GetNodeByName(context.Background(), graphReq)
@@ -179,6 +181,17 @@ func TestIngestVulnerability(t *testing.T) {
179181
require.NoError(t, err)
180182
}
181183

184+
func TestIngestScorecard(t *testing.T) {
185+
s := setupService()
186+
content, err := os.ReadFile("../../testdata/scorecards/scorecards.json")
187+
require.NoError(t, err)
188+
req := connect.NewRequest(&service.IngestScorecardRequest{
189+
Scorecard: content,
190+
})
191+
_, err = s.IngestScorecard(context.Background(), req)
192+
require.NoError(t, err)
193+
}
194+
182195
func TestHealthCheck(t *testing.T) {
183196
s := setupService()
184197
req := connect.NewRequest(&emptypb.Empty{})

pkg/tools/ingest/loader.go renamed to cmd/helpers/fileLoader.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package ingest
1+
package helpers
22

33
import (
44
"archive/zip"

cmd/ingest/osv/osv.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@ import (
66
"net/http"
77

88
"connectrpc.com/connect"
9+
"github.com/bitbomdev/minefield/cmd/helpers"
910
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
1011
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
1112
"github.com/bitbomdev/minefield/pkg/tools"
12-
"github.com/bitbomdev/minefield/pkg/tools/ingest"
1313
"github.com/spf13/cobra"
1414
)
1515

1616
type options struct {
17-
addr string // Address of the minefield server
18-
17+
addr string // Address of the minefield server
1918
ingestServiceClient apiv1connect.IngestServiceClient
2019
}
2120

@@ -35,7 +34,7 @@ func (o *options) Run(_ *cobra.Command, args []string) error {
3534
}
3635
vulnsPath := args[0]
3736
// Ingest vulnerabilities
38-
result, err := ingest.LoadDataFromPath(vulnsPath)
37+
result, err := helpers.LoadDataFromPath(vulnsPath)
3938
if err != nil {
4039
return fmt.Errorf("failed to load vulnerabilities: %w", err)
4140
}

cmd/ingest/sbom/sbom.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import (
66
"net/http"
77

88
"connectrpc.com/connect"
9+
"github.com/bitbomdev/minefield/cmd/helpers"
910
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
1011
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
1112
"github.com/bitbomdev/minefield/pkg/tools"
12-
"github.com/bitbomdev/minefield/pkg/tools/ingest"
1313
"github.com/spf13/cobra"
1414
)
1515

@@ -36,7 +36,7 @@ func (o *options) Run(_ *cobra.Command, args []string) error {
3636
}
3737
sbomPath := args[0]
3838
// Ingest SBOM
39-
result, err := ingest.LoadDataFromPath(sbomPath)
39+
result, err := helpers.LoadDataFromPath(sbomPath)
4040
if err != nil {
4141
return fmt.Errorf("failed to ingest SBOM: %w", err)
4242
}

cmd/ingest/scorecard/scorecard.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,15 @@ import (
66
"net/http"
77

88
"connectrpc.com/connect"
9+
"github.com/bitbomdev/minefield/cmd/helpers"
910
apiv1 "github.com/bitbomdev/minefield/gen/api/v1"
1011
"github.com/bitbomdev/minefield/gen/api/v1/apiv1connect"
1112
"github.com/bitbomdev/minefield/pkg/tools"
12-
"github.com/bitbomdev/minefield/pkg/tools/ingest"
1313
"github.com/spf13/cobra"
1414
)
1515

1616
type options struct {
17-
addr string // Address of the minefield server
18-
17+
addr string // Address of the minefield server
1918
ingestServiceClient apiv1connect.IngestServiceClient
2019
}
2120

@@ -36,7 +35,7 @@ func (o *options) Run(_ *cobra.Command, args []string) error {
3635
}
3736
scorecardPath := args[0]
3837

39-
result, err := ingest.LoadDataFromPath(scorecardPath)
38+
result, err := helpers.LoadDataFromPath(scorecardPath)
4039
if err != nil {
4140
return fmt.Errorf("failed to ingest SBOM: %w", err)
4241
}

pkg/storages/e2e_test.go

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package storages
33
import (
44
"os"
55
"path/filepath"
6+
"strings"
67
"testing"
78

89
"github.com/bitbomdev/minefield/pkg/graph"
@@ -19,19 +20,38 @@ func TestParseAndExecute_E2E(t *testing.T) {
1920
sbomPath := filepath.Join("..", "..", "testdata", "sboms")
2021
vulnsPath := filepath.Join("..", "..", "testdata", "osv-vulns")
2122

22-
// Ingest data from the folder
23-
result, err := ingest.LoadDataFromPath(sbomPath)
23+
// Process SBOM files
24+
sbomFiles, err := os.ReadDir(sbomPath)
2425
assert.NoError(t, err)
25-
for _, data := range result {
26-
if err := ingest.SBOM(redisStorage, data.Data); err != nil {
27-
t.Fatalf("Failed to load SBOM from data: %v", err)
26+
27+
for _, file := range sbomFiles {
28+
if !strings.HasSuffix(file.Name(), ".json") {
29+
continue
30+
}
31+
32+
data, err := os.ReadFile(filepath.Join(sbomPath, file.Name()))
33+
assert.NoError(t, err)
34+
35+
err = ingest.SBOM(redisStorage, data)
36+
if err != nil {
37+
t.Fatalf("Failed to load SBOM from file %s: %v", file.Name(), err)
2838
}
2939
}
30-
result, err = ingest.LoadDataFromPath(vulnsPath)
40+
// Process vulnerability files
41+
vulnFiles, err := os.ReadDir(vulnsPath)
3142
assert.NoError(t, err)
32-
for _, data := range result {
33-
if err := ingest.Vulnerabilities(redisStorage, data.Data); err != nil {
34-
t.Fatalf("Failed to load vulnerabilities from data: %v", err)
43+
44+
for _, file := range vulnFiles {
45+
if !strings.HasSuffix(file.Name(), ".json") {
46+
continue
47+
}
48+
49+
data, err := os.ReadFile(filepath.Join(vulnsPath, file.Name()))
50+
assert.NoError(t, err)
51+
52+
err = ingest.Vulnerabilities(redisStorage, data)
53+
if err != nil {
54+
t.Fatalf("Failed to load vulnerabilities from file %s: %v", file.Name(), err)
3555
}
3656
}
3757

pkg/tools/ingest/sbom_test.go

Lines changed: 25 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -2,118 +2,47 @@ package ingest
22

33
import (
44
"os"
5-
"sort"
5+
"path/filepath"
6+
"strings"
67
"testing"
78

89
"github.com/bitbomdev/minefield/pkg/graph"
910
)
1011

1112
func TestIngestSBOM(t *testing.T) {
12-
createTestFiles(t)
13-
tests := []struct {
14-
name string
15-
sbomPath string
16-
want map[uint32]*graph.Node
17-
wantErr bool
18-
}{
19-
{
20-
name: "non-existent file",
21-
sbomPath: "non_existent_file.json",
22-
wantErr: true,
23-
},
24-
{
25-
name: "empty directory",
26-
sbomPath: "../../../empty_dir",
27-
want: map[uint32]*graph.Node{},
28-
wantErr: false,
29-
},
30-
{
31-
name: "SBOM with no components",
32-
sbomPath: "../../../no_components_sbom.json",
33-
want: map[uint32]*graph.Node{},
34-
},
35-
}
36-
for _, test := range tests {
37-
t.Run(test.name, func(t *testing.T) {
38-
storage := graph.NewMockStorage()
39-
result, err := LoadDataFromPath(test.sbomPath)
40-
if test.wantErr != (err != nil) {
41-
t.Errorf("Sbom() error = %v, wantErr = %v", err, test.wantErr)
42-
}
43-
44-
for _, data := range result {
45-
if err := SBOM(storage, data.Data); err != nil {
46-
if test.wantErr != (err != nil) {
47-
t.Errorf("Sbom() error = %v, wantErr = %v", err, test.wantErr)
48-
}
49-
}
50-
}
13+
storage := graph.NewMockStorage()
5114

52-
keys, err := storage.GetAllKeys()
53-
if err != nil {
54-
t.Fatalf("Failed to get all keys, %v", err)
55-
}
15+
sbomDir := "../../../testdata/sboms"
5616

57-
sort.Slice(keys, func(i, j int) bool {
58-
return keys[i] < keys[j]
59-
})
60-
61-
for _, key := range keys {
62-
node, err := storage.GetNode(key)
63-
if err != nil {
64-
t.Fatalf("Failed to get node, %v", err)
65-
}
66-
if !nodeEquals(node, test.want[key]) {
67-
t.Fatalf("expected node %v, got %v", test.want[key], node)
68-
}
69-
}
70-
})
71-
}
72-
if err := os.RemoveAll("../../../empty_dir"); err != nil {
73-
t.Fatal(err)
74-
}
75-
if err := os.Remove("../../../invalid_sbom.json"); err != nil {
76-
t.Fatal(err)
77-
}
78-
if err := os.Remove("../../../no_components_sbom.json"); err != nil {
79-
t.Fatal(err)
17+
// Read SBOM files
18+
sbomFiles, err := os.ReadDir(sbomDir)
19+
if err != nil {
20+
t.Fatalf("Failed to read SBOM directory: %v", err)
8021
}
81-
}
8222

83-
func nodeEquals(n, n2 *graph.Node) bool {
84-
if ((n == nil || n2 == nil) && n != n2) ||
85-
(n != nil && (n.ID != n2.ID || n.Type != n2.Type || n.Name != n2.Name)) {
86-
return false
87-
}
88-
return true
89-
}
23+
for _, file := range sbomFiles {
24+
if !strings.HasSuffix(file.Name(), ".json") {
25+
continue
26+
}
9027

91-
func createTestFiles(t *testing.T) {
92-
t.Helper()
28+
data, err := os.ReadFile(filepath.Join(sbomDir, file.Name()))
29+
if err != nil {
30+
t.Fatalf("Failed to read SBOM file %s: %v", file.Name(), err)
31+
}
9332

94-
// Create an empty directory
95-
err := os.MkdirAll("../../../empty_dir", 0o755)
96-
if err != nil {
97-
t.Fatalf("Failed to create empty directory: %v", err)
33+
if err := SBOM(storage, data); err != nil {
34+
t.Fatalf("Failed to process SBOM from file %s: %v", file.Name(), err)
35+
}
9836
}
9937

100-
// Create an invalid SBOM file
101-
invalidSBOM := []byte(`{"invalid": "json"}`)
102-
err = os.WriteFile("../../../invalid_sbom.json", invalidSBOM, 0o644)
38+
keys, err := storage.GetAllKeys()
10339
if err != nil {
104-
t.Fatalf("Failed to create invalid SBOM file: %v", err)
40+
t.Fatalf("Failed to get all keys: %v", err)
10541
}
10642

107-
// Create a SBOM file with no components
108-
noComponentsSBOM := []byte(`{
109-
"bomFormat": "CycloneDX",
110-
"specVersion": "1.5",
111-
"version": 1,
112-
"metadata": {},
113-
"components": []
114-
}`)
115-
err = os.WriteFile("../../../no_components_sbom.json", noComponentsSBOM, 0o644)
116-
if err != nil {
117-
t.Fatalf("Failed to create no components SBOM file: %v", err)
43+
// Verify we have the expected number of nodes
44+
if len(keys) != 1600 {
45+
t.Fatalf("Expected 1600 nodes to be created from SBOM ingestion, got %d", len(keys))
11846
}
47+
11948
}

pkg/tools/ingest/scorecard.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package ingest
33
import (
44
"encoding/json"
55
"fmt"
6+
"log"
67

78
"strings"
89

910
"github.com/bitbomdev/minefield/pkg/graph"
1011
"github.com/bitbomdev/minefield/pkg/tools"
12+
"github.com/package-url/packageurl-go"
1113
)
1214

1315
type Repo struct {
@@ -45,7 +47,6 @@ type ScorecardResult struct {
4547

4648
// Scorecard processes the Scorecard JSON data and stores it in the graph.
4749
func Scorecards(storage graph.Storage, data []byte) error {
48-
4950
if len(data) == 0 {
5051
return fmt.Errorf("data is empty")
5152
}
@@ -61,7 +62,19 @@ func Scorecards(storage graph.Storage, data []byte) error {
6162
if !result.Success {
6263
continue
6364
}
64-
scorecardResults[result.PURL] = append(scorecardResults[result.PURL], result)
65+
purl, err := packageurl.FromString(result.PURL)
66+
if err != nil {
67+
// Log and skip invalid PURLs instead of failing
68+
log.Printf("Warning: Invalid PURL %q: %v", result.PURL, err)
69+
continue
70+
}
71+
72+
if purl.Name == "" {
73+
log.Printf("Warning: Empty package name in PURL %q", result.PURL)
74+
continue
75+
}
76+
77+
scorecardResults[purl.Name] = append(scorecardResults[purl.Name], result)
6578
}
6679

6780
keys, err := storage.GetAllKeys()

0 commit comments

Comments
 (0)