Skip to content

Commit 1c4539d

Browse files
refactor: commands folder
chore: fix most linting issues
1 parent 15567cf commit 1c4539d

File tree

15 files changed

+697
-838
lines changed

15 files changed

+697
-838
lines changed

cmd/develop/main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import (
44
"log"
55

66
"github.com/snyk/go-application-framework/pkg/devtools"
7+
8+
"github.com/snyk/cli-extension-os-flows/pkg/osflows"
79
)
810

911
func main() {
10-
cmd, err := devtools.Cmd()
12+
cmd, err := devtools.Cmd(osflows.Init)
1113
if err != nil {
1214
log.Fatal(err)
1315
}

internal/bundlestore/client.go

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ import (
1313
"github.com/google/uuid"
1414
"github.com/rs/zerolog"
1515
codeclient "github.com/snyk/code-client-go"
16-
codeclienthttp "github.com/snyk/code-client-go/http"
1716
codeclientscan "github.com/snyk/code-client-go/scan"
18-
"github.com/snyk/go-application-framework/pkg/configuration"
1917

2018
listsources "github.com/snyk/cli-extension-os-flows/internal/files"
2119
)
@@ -26,14 +24,15 @@ type Client interface {
2624
UploadSBOM(ctx context.Context, sbomPath string) (string, error)
2725
}
2826

29-
type client struct {
27+
// HTTPClient is the concrete implementation of the Client interface.
28+
type HTTPClient struct {
3029
httpClient *http.Client
31-
codeClientConfig
30+
CodeClientConfig
3231
codeScanner codeclient.CodeScanner
3332
logger *zerolog.Logger
3433
}
3534

36-
var _ Client = (*client)(nil)
35+
var _ Client = (*HTTPClient)(nil)
3736

3837
type (
3938
// BundleFile represents a file to be included in a bundle, including its hash and content.
@@ -53,46 +52,24 @@ type (
5352
}
5453
)
5554

56-
// CodeScanner is a global instance of the Snyk Code scanner.
57-
var CodeScanner codeclient.CodeScanner
58-
5955
// NewClient creates a new client for interacting with the Snyk bundle store.
60-
//
61-
//nolint:ireturn // returning an interface is desired here to decouple the implementation
62-
func NewClient(config configuration.Configuration, hc codeclienthttp.HTTPClientFactory, logger *zerolog.Logger) Client {
63-
codeScannerConfig := &codeClientConfig{
64-
localConfiguration: config,
65-
}
66-
67-
httpClient := codeclienthttp.NewHTTPClient(
68-
hc,
69-
codeclienthttp.WithLogger(logger),
70-
)
71-
72-
if CodeScanner == nil {
73-
CodeScanner = codeclient.NewCodeScanner(
74-
codeScannerConfig,
75-
httpClient,
76-
codeclient.WithLogger(logger),
77-
)
78-
}
79-
80-
return &client{
81-
hc(),
82-
*codeScannerConfig,
83-
CodeScanner,
56+
func NewClient(httpClient *http.Client, csConfig CodeClientConfig, cScanner codeclient.CodeScanner, logger *zerolog.Logger) *HTTPClient {
57+
return &HTTPClient{
58+
httpClient,
59+
csConfig,
60+
cScanner,
8461
logger,
8562
}
8663
}
8764

88-
func (c *client) host() string {
89-
if c.codeClientConfig.IsFedramp() {
90-
return c.SnykApi() + "/hidden/orgs/" + c.codeClientConfig.Organization() + "/code"
65+
func (c *HTTPClient) host() string {
66+
if c.CodeClientConfig.IsFedramp() {
67+
return c.SnykApi() + "/hidden/orgs/" + c.CodeClientConfig.Organization() + "/code"
9168
}
9269
return c.SnykCodeApi()
9370
}
9471

95-
func (c *client) request(
72+
func (c *HTTPClient) request(
9673
ctx context.Context,
9774
method string,
9875
path string,
@@ -108,7 +85,7 @@ func (c *client) request(
10885
return nil, fmt.Errorf("error creating request: %w", err)
10986
}
11087

111-
org := c.codeClientConfig.Organization()
88+
org := c.CodeClientConfig.Organization()
11289
if org != "" {
11390
req.Header.Set("snyk-org-name", org)
11491
}
@@ -141,7 +118,7 @@ func (c *client) request(
141118
}
142119

143120
//nolint:gocritic // Code copied verbatim from code-client-go
144-
func (c *client) createBundle(ctx context.Context, fileHashes map[string]string) (string, []string, error) {
121+
func (c *HTTPClient) createBundle(ctx context.Context, fileHashes map[string]string) (string, []string, error) {
145122
requestBody, err := json.Marshal(fileHashes)
146123
if err != nil {
147124
return "", nil, fmt.Errorf("failed to marshal create bundle request: %w", err)
@@ -161,7 +138,7 @@ func (c *client) createBundle(ctx context.Context, fileHashes map[string]string)
161138
}
162139

163140
//nolint:gocritic // Code copied verbatim from code-client-go
164-
func (c *client) extendBundle(ctx context.Context, bundleHash string, files map[string]BundleFile, removedFiles []string) (string, []string, error) {
141+
func (c *HTTPClient) extendBundle(ctx context.Context, bundleHash string, files map[string]BundleFile, removedFiles []string) (string, []string, error) {
165142
requestBody, err := json.Marshal(ExtendBundleRequest{
166143
Files: files,
167144
RemovedFiles: removedFiles,
@@ -183,7 +160,8 @@ func (c *client) extendBundle(ctx context.Context, bundleHash string, files map[
183160
return bundleResponse.BundleHash, bundleResponse.MissingFiles, nil
184161
}
185162

186-
func (c *client) UploadSBOM(ctx context.Context, sbomPath string) (string, error) {
163+
// UploadSBOM uploads an SBOM file to the bundle store and returns the bundle hash.
164+
func (c *HTTPClient) UploadSBOM(ctx context.Context, sbomPath string) (string, error) {
187165
var fileContent []byte
188166
fileContent, err := os.ReadFile(sbomPath)
189167
if err != nil {
@@ -216,7 +194,8 @@ func (c *client) UploadSBOM(ctx context.Context, sbomPath string) (string, error
216194
return bundleHash, nil
217195
}
218196

219-
func (c *client) UploadSourceCode(ctx context.Context, sourceCodePath string) (string, error) {
197+
// UploadSourceCode uploads source code from the specified path to the bundle store and returns the bundle hash.
198+
func (c *HTTPClient) UploadSourceCode(ctx context.Context, sourceCodePath string) (string, error) {
220199
numThreads := runtime.NumCPU()
221200
filesChan, err := listsources.ForPath(sourceCodePath, c.logger, numThreads)
222201
if err != nil {

internal/bundlestore/config.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,43 @@ import (
99

1010
var defaultSnykCodeTimeout = 12 * time.Hour
1111

12-
type codeClientConfig struct {
13-
localConfiguration configuration.Configuration
12+
// CodeClientConfig holds the configuration for the code client.
13+
type CodeClientConfig struct {
14+
// LocalConfiguration is the underlying configuration source.
15+
LocalConfiguration configuration.Configuration
1416
}
1517

16-
func (c *codeClientConfig) Organization() string {
17-
return c.localConfiguration.GetString(configuration.ORGANIZATION)
18+
// Organization returns the organization ID from the configuration.
19+
func (c *CodeClientConfig) Organization() string {
20+
return c.LocalConfiguration.GetString(configuration.ORGANIZATION)
1821
}
1922

20-
func (c *codeClientConfig) IsFedramp() bool {
21-
return c.localConfiguration.GetBool(configuration.IS_FEDRAMP)
23+
// IsFedramp returns true if the configuration is set for FedRAMP.
24+
func (c *CodeClientConfig) IsFedramp() bool {
25+
return c.LocalConfiguration.GetBool(configuration.IS_FEDRAMP)
2226
}
2327

28+
// SnykCodeApi returns the Snyk Code API URL, replacing "api" with "deeproxy" in the base API URL.
29+
//
2430
//nolint:revive,var-naming // SnykCodeApi is intentionally cased this way.
25-
func (c *codeClientConfig) SnykCodeApi() string {
31+
func (c *CodeClientConfig) SnykCodeApi() string {
2632
//nolint:gocritic // Code copied verbatim from code-client-go
27-
return strings.Replace(c.localConfiguration.GetString(configuration.API_URL), "api", "deeproxy", -1)
33+
return strings.Replace(c.LocalConfiguration.GetString(configuration.API_URL), "api", "deeproxy", -1)
2834
}
2935

36+
// SnykApi returns the base Snyk API URL from the configuration.
37+
//
3038
//nolint:revive,var-naming // SnykApi is intentionally cased this way.
31-
func (c *codeClientConfig) SnykApi() string {
32-
return c.localConfiguration.GetString(configuration.API_URL)
39+
func (c *CodeClientConfig) SnykApi() string {
40+
return c.LocalConfiguration.GetString(configuration.API_URL)
3341
}
3442

35-
func (c *codeClientConfig) SnykCodeAnalysisTimeout() time.Duration {
36-
if !c.localConfiguration.IsSet(configuration.TIMEOUT) {
43+
// SnykCodeAnalysisTimeout returns the timeout duration for Snyk Code analysis.
44+
// If not set in the configuration, it returns the default timeout.
45+
func (c *CodeClientConfig) SnykCodeAnalysisTimeout() time.Duration {
46+
if !c.LocalConfiguration.IsSet(configuration.TIMEOUT) {
3747
return defaultSnykCodeTimeout
3848
}
39-
timeoutInSeconds := c.localConfiguration.GetInt(configuration.TIMEOUT)
49+
timeoutInSeconds := c.LocalConfiguration.GetInt(configuration.TIMEOUT)
4050
return time.Duration(timeoutInSeconds) * time.Second
4151
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package ostest
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
9+
"github.com/rs/zerolog"
10+
"github.com/snyk/go-application-framework/pkg/apiclients/testapi"
11+
"github.com/snyk/go-application-framework/pkg/workflow"
12+
13+
service "github.com/snyk/cli-extension-os-flows/internal/common"
14+
"github.com/snyk/cli-extension-os-flows/internal/errors"
15+
"github.com/snyk/cli-extension-os-flows/internal/flags"
16+
"github.com/snyk/cli-extension-os-flows/internal/legacy/definitions"
17+
)
18+
19+
// RunUnifiedTestFlow handles the unified test API flow.
20+
func RunUnifiedTestFlow(
21+
ctx context.Context,
22+
ictx workflow.InvocationContext,
23+
testClient testapi.TestClient,
24+
riskScoreThreshold *uint16,
25+
severityThreshold *testapi.Severity,
26+
orgID string,
27+
errFactory *errors.ErrorFactory,
28+
logger *zerolog.Logger,
29+
) ([]workflow.Data, error) {
30+
logger.Info().Msg("Starting open source test")
31+
32+
// Create depgraphs and get their associated target files
33+
depGraphs, displayTargetFiles, err := createDepGraphs(ictx)
34+
if err != nil {
35+
return nil, err
36+
}
37+
var allFindings []definitions.LegacyVulnerabilityResponse
38+
var allSummaries []workflow.Data
39+
40+
localPolicy := createLocalPolicy(riskScoreThreshold, severityThreshold)
41+
42+
for i, depGraph := range depGraphs {
43+
displayTargetFile := ""
44+
if i < len(displayTargetFiles) {
45+
displayTargetFile = displayTargetFiles[i]
46+
}
47+
48+
// Create depgraph subject
49+
depGraphSubject := testapi.DepGraphSubjectCreate{
50+
Type: testapi.DepGraphSubjectCreateTypeDepGraph,
51+
DepGraph: depGraph,
52+
Locator: testapi.LocalPathLocator{
53+
Paths: []string{displayTargetFile},
54+
Type: testapi.LocalPath,
55+
},
56+
}
57+
58+
// Create test subject with depgraph
59+
var subject testapi.TestSubjectCreate
60+
err = subject.FromDepGraphSubjectCreate(depGraphSubject)
61+
if err != nil {
62+
return nil, fmt.Errorf("failed to create test subject: %w", err)
63+
}
64+
65+
// Project name assigned as follows: --project-name || config project name || scannedProject?.depTree?.name
66+
// TODO: use project name from Config file
67+
config := ictx.GetConfiguration()
68+
projectName := config.GetString(flags.FlagProjectName)
69+
if projectName == "" && len(depGraph.Pkgs) > 0 {
70+
projectName = depGraph.Pkgs[0].Info.Name
71+
}
72+
73+
packageManager := depGraph.PkgManager.Name
74+
depCount := max(0, len(depGraph.Pkgs)-1)
75+
76+
// Run the test with the depgraph subject
77+
findings, summary, err := RunTest(ctx, testClient, subject, projectName, packageManager, depCount, displayTargetFile, orgID, errFactory, logger, localPolicy)
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
if findings != nil {
83+
allFindings = append(allFindings, *findings)
84+
}
85+
if summary != nil {
86+
allSummaries = append(allSummaries, summary)
87+
}
88+
}
89+
90+
var finalOutput []workflow.Data
91+
if len(allFindings) > 0 {
92+
var findingsData any
93+
if len(allFindings) == 1 {
94+
findingsData = allFindings[0]
95+
} else {
96+
findingsData = allFindings
97+
}
98+
99+
var buffer bytes.Buffer
100+
encoder := json.NewEncoder(&buffer)
101+
encoder.SetEscapeHTML(false)
102+
encoder.SetIndent("", " ")
103+
err := encoder.Encode(findingsData)
104+
if err != nil {
105+
return nil, errFactory.NewLegacyJSONTransformerError(fmt.Errorf("marshaling to json: %w", err))
106+
}
107+
// encoder.Encode adds a newline, which we trim to match Marshal's behavior.
108+
findingsBytes := bytes.TrimRight(buffer.Bytes(), "\n")
109+
110+
finalOutput = append(finalOutput, NewWorkflowData(ApplicationJSONContentType, findingsBytes))
111+
}
112+
113+
finalOutput = append(finalOutput, allSummaries...)
114+
115+
return finalOutput, nil
116+
}
117+
118+
// Create local policy only if risk score or severity threshold are specified.
119+
func createLocalPolicy(riskScoreThreshold *uint16, severityThreshold *testapi.Severity) *testapi.LocalPolicy {
120+
if riskScoreThreshold == nil && severityThreshold == nil {
121+
return nil
122+
}
123+
124+
localPolicy := &testapi.LocalPolicy{}
125+
if riskScoreThreshold != nil {
126+
localPolicy.RiskScoreThreshold = riskScoreThreshold
127+
}
128+
if severityThreshold != nil {
129+
localPolicy.SeverityThreshold = severityThreshold
130+
}
131+
return localPolicy
132+
}
133+
134+
// createDepGraphs creates depgraphs from the file parameter in the context.
135+
func createDepGraphs(ictx workflow.InvocationContext) ([]testapi.IoSnykApiV1testdepgraphRequestDepGraph, []string, error) {
136+
depGraphResult, err := service.GetDepGraph(ictx)
137+
if err != nil {
138+
return nil, nil, fmt.Errorf("failed to get dependency graph: %w", err)
139+
}
140+
141+
if len(depGraphResult.DepGraphBytes) == 0 {
142+
return nil, nil, fmt.Errorf("no dependency graphs found")
143+
}
144+
145+
depGraphs := make([]testapi.IoSnykApiV1testdepgraphRequestDepGraph, len(depGraphResult.DepGraphBytes))
146+
for i, depGraphBytes := range depGraphResult.DepGraphBytes {
147+
var depGraphStruct testapi.IoSnykApiV1testdepgraphRequestDepGraph
148+
err = json.Unmarshal(depGraphBytes, &depGraphStruct)
149+
if err != nil {
150+
return nil, nil,
151+
fmt.Errorf("unmarshaling depGraph from args failed: %w", err)
152+
}
153+
depGraphs[i] = depGraphStruct
154+
}
155+
156+
return depGraphs, depGraphResult.DisplayTargetFiles, nil
157+
}

0 commit comments

Comments
 (0)