From 6978bccb7b56d937e055bee37214e45547863a70 Mon Sep 17 00:00:00 2001 From: bpatra3 Date: Mon, 6 Jul 2026 09:44:43 +0000 Subject: [PATCH] feature-b(#8037): Added support for spdx sbom detection --- internal/file/finder.go | 25 +++++++++++++-- internal/file/finder_test.go | 32 +++++++++++++++++++ internal/file/groups_test.go | 22 +++++++++++++ internal/upload/batch_test.go | 30 +++++++++++++++++ .../upload/testdata/spdx/example.spdx.json | 27 ++++++++++++++++ 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 internal/upload/testdata/spdx/example.spdx.json diff --git a/internal/file/finder.go b/internal/file/finder.go index 6ea0b04c..bd2973b1 100644 --- a/internal/file/finder.go +++ b/internal/file/finder.go @@ -10,12 +10,15 @@ import ( "net/http" "os" "path/filepath" + "strings" "github.com/debricked/cli/internal/client" ioFs "github.com/debricked/cli/internal/io" "github.com/fatih/color" ) +const spdxFormatJSON = `{"regex":"","documentationUrl":"https://docs.debricked.com/overview/language-support","lockFileRegexes":["^.*\\.spdx\\.json$"]}` + //go:embed embedded/supported_formats.json var supportedFormats embed.FS @@ -245,7 +248,12 @@ func (finder *Finder) GetSupportedFormatsJson() ([]byte, error) { defer res.Body.Close() - return io.ReadAll(res.Body) + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + return addSPDXFormat(body), nil } func (finder *Finder) GetSupportedFormatsFallbackJson() ([]byte, error) { @@ -260,5 +268,18 @@ func (finder *Finder) GetSupportedFormatsFallbackJson() ([]byte, error) { return nil, err } - return jsonData, nil + return addSPDXFormat(jsonData), nil +} + +func addSPDXFormat(jsonData []byte) []byte { + if strings.Contains(string(jsonData), "spdx") { + return jsonData + } + + content := string(jsonData) + if i := strings.LastIndex(content, "]"); i >= 0 { + return []byte(content[:i] + "," + spdxFormatJSON + content[i:]) + } + + return jsonData } diff --git a/internal/file/finder_test.go b/internal/file/finder_test.go index be92edda..c5b2032a 100644 --- a/internal/file/finder_test.go +++ b/internal/file/finder_test.go @@ -302,6 +302,38 @@ func TestGetGroupsWithOnlyLockFiles(t *testing.T) { assert.Contains(t, file, "Cargo.lock", "failed to assert that the related file was Cargo.lock") } +func TestGetGroupsSPDX(t *testing.T) { + setUp(true) + dir := t.TempDir() + spdxFile := filepath.Join(dir, "example.spdx.json") + if err := os.WriteFile(spdxFile, []byte("{}"), 0600); err != nil { + t.Fatal(err) + } + // A non-SPDX json file in the same dir must not be discovered. + if err := os.WriteFile(filepath.Join(dir, "notes.json"), []byte("{}"), 0600); err != nil { + t.Fatal(err) + } + + const nbrOfGroups = 1 + fileGroups, err := finder.GetGroups( + DebrickedOptions{ + RootPath: dir, + Exclusions: []string{}, + Inclusions: []string{}, + LockFileOnly: false, + Strictness: StrictAll, + }, + ) + + assert.NoError(t, err) + assert.Equalf(t, nbrOfGroups, fileGroups.Size(), "failed to assert that %d groups were created. %d was found", nbrOfGroups, fileGroups.Size()) + + fileGroup := fileGroups.groups[0] + assert.False(t, fileGroup.HasFile(), "failed to assert that SPDX group lacked a manifest file") + assert.Len(t, fileGroup.LockFiles, 1, "failed to assert that there was one SPDX lock file") + assert.Contains(t, fileGroup.GetAllFiles()[0], "example.spdx.json", "failed to assert that the SPDX file was discovered") +} + func TestGetGroupsWithTwoFileMatchesInSameDir(t *testing.T) { setUp(true) const nbrOfGroups = 3 diff --git a/internal/file/groups_test.go b/internal/file/groups_test.go index edf97534..b52ef17c 100644 --- a/internal/file/groups_test.go +++ b/internal/file/groups_test.go @@ -58,6 +58,28 @@ func TestMatch(t *testing.T) { } } +func TestMatchSPDX(t *testing.T) { + f := Format{ + ManifestFileRegex: "", + DocumentationUrl: "", + LockFileRegexes: []string{"^.*\\.spdx\\.json$"}, + } + compiledF, _ := NewCompiledFormat(&f) + var gs Groups + + matched := gs.Match(compiledF, "directory/example.spdx.json", false) + assert.True(t, matched, "failed to assert that the SPDX file matched") + assert.Equal(t, 1, gs.Size(), "failed to assert that there was one Group in Groups") + + g := gs.groups[0] + assert.False(t, g.HasFile(), "failed to assert that SPDX group had no manifest file") + assert.Len(t, g.LockFiles, 1, "failed to assert that there was one SPDX lock file") + assert.Equal(t, "directory/example.spdx.json", g.LockFiles[0], "failed to assert SPDX lock file name") + + // A non-SPDX json file must not match this format. + assert.False(t, gs.Match(compiledF, "directory/package.json", false), "failed to assert that a non-SPDX file did not match") +} + func TestGetFiles(t *testing.T) { g1 := NewGroup("file1", nil, []string{"lockfile1"}) g2 := NewGroup("", nil, []string{"lockfile2"}) diff --git a/internal/upload/batch_test.go b/internal/upload/batch_test.go index dc4b6abb..58f948b8 100644 --- a/internal/upload/batch_test.go +++ b/internal/upload/batch_test.go @@ -154,6 +154,36 @@ func TestInitUpload(t *testing.T) { assert.Equal(t, 1, batch.ciUploadId) } +func TestInitUploadSPDX(t *testing.T) { + spdxFile := filepath.Join("testdata", "spdx", "example.spdx.json") + group := file.NewGroup("", nil, []string{spdxFile}) + var groups file.Groups + groups.Add(*group) + + assert.Contains(t, groups.GetFiles(), spdxFile, "failed to assert that the SPDX file is part of the files to upload") + + metaObj, err := git.NewMetaObject("", "repository-name", "commit-name", "", "", "") + if err != nil { + t.Fatal("failed to create new MetaObject") + } + + clientMock := testdata.NewDebClientMock() + mockRes := testdata.MockResponse{ + StatusCode: http.StatusOK, + ResponseBody: io.NopCloser(strings.NewReader(`{"ciUploadId": 1}`)), + } + clientMock.AddMockResponse(mockRes) + + var c client.IDebClient = clientMock + batch := newUploadBatch(&c, groups, metaObj, "CLI", 10*60, true, &DebrickedConfig{}, true, false) + + files, err := batch.initUpload() + + assert.NoError(t, err) + assert.Len(t, files, 0, "failed to assert that the SPDX file was uploaded during init") + assert.Equal(t, 1, batch.ciUploadId) +} + func TestGetDebrickedConfig(t *testing.T) { config := GetDebrickedConfig(filepath.Join("testdata", "debricked-config.yaml")) configJSON, err := json.Marshal(config) diff --git a/internal/upload/testdata/spdx/example.spdx.json b/internal/upload/testdata/spdx/example.spdx.json new file mode 100644 index 00000000..516ef612 --- /dev/null +++ b/internal/upload/testdata/spdx/example.spdx.json @@ -0,0 +1,27 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "example-project", + "documentNamespace": "https://debricked.com/spdxdocs/example-project", + "creationInfo": { + "created": "2024-01-01T00:00:00Z", + "creators": ["Tool: debricked-cli"] + }, + "packages": [ + { + "name": "lodash", + "SPDXID": "SPDXRef-Package-lodash", + "versionInfo": "4.17.21", + "downloadLocation": "NOASSERTION", + "licenseConcluded": "MIT", + "externalRefs": [ + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:npm/lodash@4.17.21" + } + ] + } + ] +}