Skip to content

Commit 40aeece

Browse files
Merge pull request #18 from snyk/UNIFY-757-sbom-test-with-reachability-invokes-test-shim-api
feat: wire up sbom reachability flow
2 parents 26a8da1 + 48a8fc4 commit 40aeece

File tree

16 files changed

+379
-123
lines changed

16 files changed

+379
-123
lines changed

.gitattributes

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Ignore Go checksum file
2+
go.sum linguist-generated
3+
4+
# Ignore NPM versioning file
5+
package-lock.json linguist-generated
6+
7+
# Ignore snapshots
8+
**/testdata/snapshots/** linguist-generated

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ require (
1414
github.com/rs/zerolog v1.34.0
1515
github.com/snyk/code-client-go v1.21.3
1616
github.com/snyk/error-catalog-golang-public v0.0.0-20250429130542-564b0605020e
17-
github.com/snyk/go-application-framework v0.0.0-20250624132329-67306d427077
17+
github.com/snyk/go-application-framework v0.0.0-20250716102454-7496bd64ed98
1818
github.com/spf13/pflag v1.0.6
1919
github.com/stretchr/testify v1.10.0
2020
golang.org/x/net v0.38.0

go.sum

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/bundlestore/client.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"io"
88
"net/http"
99
"os"
10-
"path/filepath"
1110
"runtime"
1211

1312
"github.com/google/uuid"
@@ -52,6 +51,9 @@ type (
5251
}
5352
)
5453

54+
// SBOMMonitorFilename is the hardcoded SBOM file name the server is looking for.
55+
const SBOMMonitorFilename = "sbom_monitor_820F1A6A-3CFD-4B81-AD30-7F255A9D93ED.sbom"
56+
5557
// NewClient creates a new client for interacting with the Snyk bundle store.
5658
func NewClient(httpClient *http.Client, csConfig CodeClientConfig, cScanner codeclient.CodeScanner, logger *zerolog.Logger) *HTTPClient {
5759
return &HTTPClient{
@@ -175,10 +177,8 @@ func (c *HTTPClient) UploadSBOM(ctx context.Context, sbomPath string) (string, e
175177
return "", fmt.Errorf("could not read sbom file %s: %w", sbomPath, err)
176178
}
177179

178-
relativeFilePath, err := toRelativeUnixPath(filepath.Dir(sbomPath), sbomPath)
179-
if err != nil {
180-
return "", err
181-
}
180+
// We use the hardcoded SBOM file name expected on the server side.
181+
relativeFilePath := SBOMMonitorFilename
182182

183183
bf := bundleFileFrom(fileContent)
184184
fileHashes := make(map[string]string)

internal/bundlestore/utils.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import (
44
"bytes"
55
"crypto/sha256"
66
"encoding/hex"
7-
"fmt"
87
"io"
9-
"path/filepath"
108

119
"golang.org/x/net/html/charset"
1210
)
@@ -40,17 +38,3 @@ func encodeRequestBody(requestBody []byte) (*bytes.Buffer, error) {
4038
}
4139
return b, nil
4240
}
43-
44-
func toRelativeUnixPath(baseDir, absoluteFilePath string) (string, error) {
45-
relativePath, err := filepath.Rel(baseDir, absoluteFilePath)
46-
if err != nil {
47-
relativePath = absoluteFilePath
48-
if baseDir != "" {
49-
errMsg := fmt.Sprint("could not get relative path for file: ", absoluteFilePath, " and root path: ", baseDir)
50-
return "", fmt.Errorf("%s: %w", errMsg, err)
51-
}
52-
}
53-
54-
relativePath = filepath.ToSlash(relativePath) // treat all paths as unix paths
55-
return relativePath, nil
56-
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
2+
[Test_RunSbomReachabilityFlow_Success - 1]
3+
{
4+
"dependencyCount": 0,
5+
"displayTargetFile": "",
6+
"filesystemPolicy": false,
7+
"filtered": {
8+
"ignore": [],
9+
"patch": []
10+
},
11+
"hasUnknownVersions": false,
12+
"ignoreSettings": {
13+
"adminOnly": false,
14+
"autoApproveIgnores": false,
15+
"disregardFilesystemIgnores": false,
16+
"reasonRequired": false
17+
},
18+
"isPrivate": false,
19+
"licensesPolicy": {
20+
"orgLicenseRules": null,
21+
"severities": null
22+
},
23+
"ok": false,
24+
"org": "",
25+
"packageManager": "",
26+
"policy": "",
27+
"projectName": "",
28+
"summary": "",
29+
"uniqueCount": 1,
30+
"vulnerabilities": [
31+
{
32+
"creationTime": "2025-07-28T17:11:43.000000Z",
33+
"cvssScore": 0,
34+
"description": "Test vulnerability description",
35+
"disclosureTime": "2025-07-28T17:11:43.000000Z",
36+
"epssDetails": null,
37+
"from": [],
38+
"id": "snyk-vuln-123",
39+
"isPatchable": false,
40+
"isUpgradable": false,
41+
"language": "js",
42+
"malicious": true,
43+
"modificationTime": "2025-07-28T17:11:43.000000Z",
44+
"name": "foo",
45+
"packageManager": "npm",
46+
"packageName": "foo",
47+
"publicationTime": "2025-07-28T17:11:43.000000Z",
48+
"reachability": "REACHABLE",
49+
"riskScore": 80,
50+
"severity": "high",
51+
"socialTrendAlert": false,
52+
"title": "Test High Severity Finding",
53+
"upgradePath": null,
54+
"version": "0.0.0"
55+
}
56+
]
57+
}
58+
---
59+
60+
[Test_RunSbomReachabilityFlow_Success - 2]
61+
{
62+
"artifacts": 0,
63+
"results": [
64+
{
65+
"ignored": 0,
66+
"open": 1,
67+
"severity": "high",
68+
"total": 1
69+
}
70+
],
71+
"severity_order_asc": [
72+
"low",
73+
"medium",
74+
"high",
75+
"critical"
76+
],
77+
"type": "open-source"
78+
}
79+
---

internal/commands/ostest/sbom_reachability_flow.go

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package ostest
22

33
import (
4+
"bytes"
45
"context"
6+
"encoding/json"
57
"fmt"
68
"os"
79

810
"github.com/rs/zerolog"
11+
"github.com/snyk/go-application-framework/pkg/apiclients/testapi"
912
"github.com/snyk/go-application-framework/pkg/workflow"
1013

1114
"github.com/snyk/cli-extension-os-flows/internal/bundlestore"
@@ -15,11 +18,14 @@ import (
1518
// RunSbomReachabilityFlow runs the SBOM reachability flow.
1619
func RunSbomReachabilityFlow(
1720
ctx context.Context,
21+
ictx workflow.InvocationContext,
22+
testClient testapi.TestClient,
1823
errFactory *errors.ErrorFactory,
1924
logger *zerolog.Logger,
2025
sbomPath string,
2126
sourceCodePath string,
2227
bsClient bundlestore.Client,
28+
orgID string,
2329
) ([]workflow.Data, error) {
2430
if sourceCodePath == "" {
2531
sourceCodePath = "."
@@ -44,7 +50,45 @@ func RunSbomReachabilityFlow(
4450
}
4551
logger.Println("sourceCodeBundleHash", sourceCodeBundleHash)
4652

47-
return nil, nil // TODO: return something meaningful once this function is complete
53+
var subject testapi.TestSubjectCreate
54+
err = subject.FromSbomReachabilitySubject(testapi.SbomReachabilitySubject{
55+
Type: testapi.SbomReachability,
56+
CodeBundleId: sourceCodeBundleHash,
57+
SbomBundleId: sbomBundleHash,
58+
Locator: testapi.LocalPathLocator{
59+
Paths: []string{
60+
sbomPath,
61+
sourceCodePath,
62+
},
63+
Type: testapi.LocalPath,
64+
},
65+
})
66+
if err != nil {
67+
logger.Error().Err(err).Msg("Failed to create SBOM reachability test subject")
68+
return nil, fmt.Errorf("failed to create sbom test reachability subject: %w", err)
69+
}
70+
71+
findings, summary, err := RunTest(ctx, ictx, testClient, subject, "", "", int(0), "", orgID, errFactory, logger, nil)
72+
if err != nil {
73+
return nil, err
74+
}
75+
76+
var finalOutput []workflow.Data
77+
var buffer bytes.Buffer
78+
encoder := json.NewEncoder(&buffer)
79+
encoder.SetEscapeHTML(false)
80+
encoder.SetIndent("", " ")
81+
err = encoder.Encode(findings)
82+
if err != nil {
83+
return nil, errFactory.NewLegacyJSONTransformerError(fmt.Errorf("marshaling to json: %w", err))
84+
}
85+
// encoder.Encode adds a newline, which we trim to match Marshal's behavior.
86+
findingsBytes := bytes.TrimRight(buffer.Bytes(), "\n")
87+
88+
finalOutput = append(finalOutput, NewWorkflowData(ApplicationJSONContentType, findingsBytes))
89+
finalOutput = append(finalOutput, summary...)
90+
91+
return finalOutput, nil
4892
}
4993

5094
// validateDirectory checks if the given path exists and contains files.

0 commit comments

Comments
 (0)