@@ -37,21 +37,13 @@ import (
37
37
testApi "github.com/snyk/code-client-go/internal/api/test/2024-12-21"
38
38
testModels "github.com/snyk/code-client-go/internal/api/test/2024-12-21/models"
39
39
"github.com/snyk/code-client-go/internal/bundle"
40
- orchestrationClient "github.com/snyk/code-client-go/internal/orchestration/2024-02-16"
41
- scans "github.com/snyk/code-client-go/internal/orchestration/2024-02-16/scans"
42
- workspaceClient "github.com/snyk/code-client-go/internal/workspace/2024-05-14"
43
- workspaces "github.com/snyk/code-client-go/internal/workspace/2024-05-14/workspaces"
44
40
"github.com/snyk/code-client-go/observability"
45
41
"github.com/snyk/code-client-go/sarif"
46
42
"github.com/snyk/code-client-go/scan"
47
43
)
48
44
49
45
//go:generate mockgen -destination=mocks/analysis.go -source=analysis.go -package mocks
50
46
type AnalysisOrchestrator interface {
51
- CreateWorkspace (ctx context.Context , orgId string , requestId string , path scan.Target , bundleHash string ) (string , error )
52
- RunAnalysis (ctx context.Context , orgId string , rootPath string , workspaceId string ) (* sarif.SarifResponse , error )
53
- RunIncrementalAnalysis (ctx context.Context , orgId string , rootPath string , workspaceId string , limitToFiles []string ) (* sarif.SarifResponse , error )
54
-
55
47
RunTest (ctx context.Context , orgId string , b bundle.Bundle , target scan.Target , reportingOptions AnalysisConfig ) (* sarif.SarifResponse , * scan.ResultMetaData , error )
56
48
RunTestRemote (ctx context.Context , orgId string , reportingOptions AnalysisConfig ) (* sarif.SarifResponse , * scan.ResultMetaData , error )
57
49
}
@@ -70,7 +62,6 @@ type analysisOrchestrator struct {
70
62
logger * zerolog.Logger
71
63
trackerFactory scan.TrackerFactory
72
64
config config.Config
73
- flow scans.Flow
74
65
testType testModels.Scan
75
66
}
76
67
@@ -114,8 +105,6 @@ func NewAnalysisOrchestrator(
114
105
options ... OptionFunc ,
115
106
) AnalysisOrchestrator {
116
107
nopLogger := zerolog .Nop ()
117
- flow := scans.Flow {}
118
- _ = flow .UnmarshalJSON ([]byte (fmt .Sprintf (`{"name": "%s"}` , scans .IdeTest )))
119
108
120
109
a := & analysisOrchestrator {
121
110
httpClient : httpClient ,
@@ -134,306 +123,6 @@ func NewAnalysisOrchestrator(
134
123
return a
135
124
}
136
125
137
- func (a * analysisOrchestrator ) CreateWorkspace (ctx context.Context , orgId string , requestId string , target scan.Target , bundleHash string ) (string , error ) {
138
- method := "analysis.CreateWorkspace"
139
- logger := a .logger .With ().Str ("method" , method ).Logger ()
140
- logger .Debug ().Msg ("API: Creating the workspace" )
141
-
142
- span := a .instrumentor .StartSpan (ctx , method )
143
- defer a .instrumentor .Finish (span )
144
-
145
- tracker := a .trackerFactory .GenerateTracker ()
146
- tracker .Begin ("Creating file bundle workspace" , "" )
147
- defer tracker .End ("" )
148
-
149
- orgUUID := uuid .MustParse (orgId )
150
-
151
- if target == nil {
152
- return "" , fmt .Errorf ("target is nil" )
153
- }
154
-
155
- repositoryTargetPath := target .GetPath ()
156
- var repositoryTargetURL string
157
- if repositoryTarget , ok := target .(* scan.RepositoryTarget ); ok {
158
- repositoryTargetPath , repositoryTargetURL = repositoryTarget .GetPath (), repositoryTarget .GetRepositoryUrl ()
159
- }
160
-
161
- host := a .host (true )
162
- a .logger .Info ().Str ("host" , host ).Str ("path" , repositoryTargetPath ).Str ("repositoryUri" , repositoryTargetURL ).Msg ("creating workspace" )
163
-
164
- workspace , err := workspaceClient .NewClientWithResponses (host , workspaceClient .WithHTTPClient (a .httpClient ))
165
- if err != nil {
166
- a .errorReporter .CaptureError (err , observability.ErrorReporterOptions {ErrorDiagnosticPath : repositoryTargetPath })
167
- return "" , fmt .Errorf ("failed to connect to the workspace API %w" , err )
168
- }
169
-
170
- workspaceResponse , err := workspace .CreateWorkspaceWithApplicationVndAPIPlusJSONBodyWithResponse (ctx , orgUUID , & workspaceClient.CreateWorkspaceParams {
171
- Version : "2024-05-14~experimental" ,
172
- SnykRequestId : uuid .MustParse (requestId ),
173
- ContentType : "application/vnd.api+json" ,
174
- UserAgent : "cli" ,
175
- }, workspaceClient.CreateWorkspaceApplicationVndAPIPlusJSONRequestBody {
176
- Data : struct {
177
- Attributes struct {
178
- BundleId string `json:"bundle_id"`
179
- RepositoryUri string `json:"repository_uri"`
180
- RootFolderId string `json:"root_folder_id"`
181
- WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"`
182
- } `json:"attributes"`
183
- Type workspaces.WorkspacePostRequestDataType `json:"type"`
184
- }(struct {
185
- Attributes struct {
186
- BundleId string `json:"bundle_id"`
187
- RepositoryUri string `json:"repository_uri"`
188
- RootFolderId string `json:"root_folder_id"`
189
- WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"`
190
- }
191
- Type workspaces.WorkspacePostRequestDataType
192
- }{Attributes : struct {
193
- BundleId string `json:"bundle_id"`
194
- RepositoryUri string `json:"repository_uri"`
195
- RootFolderId string `json:"root_folder_id"`
196
- WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType `json:"workspace_type"`
197
- }(struct {
198
- BundleId string
199
- RepositoryUri string
200
- RootFolderId string
201
- WorkspaceType workspaces.WorkspacePostRequestDataAttributesWorkspaceType
202
- }{
203
- BundleId : bundleHash ,
204
- RepositoryUri : repositoryTargetURL ,
205
- WorkspaceType : "file_bundle_workspace" ,
206
- RootFolderId : target .GetPath (),
207
- }),
208
- Type : "workspace" ,
209
- }),
210
- })
211
- if err != nil {
212
- a .logger .Error ().Err (err ).Msg ("could not create workspace" )
213
- return "" , err
214
- }
215
-
216
- if workspaceResponse .ApplicationvndApiJSON201 == nil {
217
- var msg string
218
- switch workspaceResponse .StatusCode () {
219
- case 400 :
220
- msg = workspaceResponse .ApplicationvndApiJSON400 .Errors [0 ].Detail
221
- case 401 :
222
- msg = workspaceResponse .ApplicationvndApiJSON401 .Errors [0 ].Detail
223
- case 403 :
224
- msg = workspaceResponse .ApplicationvndApiJSON403 .Errors [0 ].Detail
225
- case 500 :
226
- msg = workspaceResponse .ApplicationvndApiJSON500 .Errors [0 ].Detail
227
- }
228
- return "" , errors .New (msg )
229
- }
230
-
231
- workspaceId := workspaceResponse .ApplicationvndApiJSON201 .Data .Id .String ()
232
- a .logger .Debug ().Str ("workspaceId" , workspaceId ).Msg ("finished creating workspace" )
233
- return workspaceId , nil
234
- }
235
-
236
- func (a * analysisOrchestrator ) RunAnalysis (ctx context.Context , orgId string , rootPath string , workspaceId string ) (* sarif.SarifResponse , error ) {
237
- return a .RunIncrementalAnalysis (ctx , orgId , rootPath , workspaceId , []string {})
238
- }
239
-
240
- func (a * analysisOrchestrator ) RunIncrementalAnalysis (ctx context.Context , orgId string , rootPath string , workspaceId string , limitToFiles []string ) (* sarif.SarifResponse , error ) {
241
- method := "analysis.RunAnalysis"
242
- logger := a .logger .With ().Str ("method" , method ).Logger ()
243
- logger .Debug ().Msg ("API: Creating the scan" )
244
-
245
- tracker := a .trackerFactory .GenerateTracker ()
246
- tracker .Begin ("Snyk Code analysis for " + rootPath , "Retrieving results..." )
247
-
248
- org := uuid .MustParse (orgId )
249
-
250
- host := a .host (false )
251
- a .logger .Debug ().Str ("host" , host ).Str ("workspaceId" , workspaceId ).Interface ("limitToFiles" , limitToFiles ).Msg ("starting scan" )
252
-
253
- client , err := orchestrationClient .NewClientWithResponses (host , orchestrationClient .WithHTTPClient (a .httpClient ))
254
- if err != nil {
255
- tracker .End (fmt .Sprintf ("Analysis failed: %v" , err ))
256
- return nil , fmt .Errorf ("failed to create orchestrationClient: %w" , err )
257
- }
258
-
259
- scanJobId , err := a .triggerScan (ctx , client , org , workspaceId , limitToFiles )
260
- if err != nil {
261
- tracker .End (fmt .Sprintf ("Analysis failed: %v" , err ))
262
- return nil , err
263
- }
264
-
265
- response , err := a .pollScanForFindings (ctx , client , org , * scanJobId )
266
- if err != nil {
267
- tracker .End (fmt .Sprintf ("Analysis failed: %v" , err ))
268
- return nil , err
269
- }
270
-
271
- tracker .End ("Analysis complete." )
272
- return response , nil
273
- }
274
-
275
- func (a * analysisOrchestrator ) triggerScan (
276
- ctx context.Context ,
277
- client * orchestrationClient.ClientWithResponses ,
278
- org uuid.UUID ,
279
- workspaceId string ,
280
- limitToFiles []string ,
281
- ) (* openapi_types.UUID , error ) {
282
- workspaceUUID := uuid .MustParse (workspaceId )
283
-
284
- scanOptions := & struct {
285
- LimitScanToFiles * []string `json:"limit_scan_to_files,omitempty"`
286
- }{
287
- LimitScanToFiles : & limitToFiles ,
288
- }
289
-
290
- if len (limitToFiles ) == 0 {
291
- scanOptions = nil
292
- }
293
-
294
- data := struct {
295
- Attributes struct {
296
- Flow scans.Flow `json:"flow"`
297
- ScanOptions * struct {
298
- LimitScanToFiles * []string `json:"limit_scan_to_files,omitempty"`
299
- } `json:"scan_options,omitempty"`
300
- WorkspaceId * openapi_types.UUID `json:"workspace_id,omitempty"`
301
- WorkspaceUrl string `json:"workspace_url"`
302
- } `json:"attributes"`
303
- Id * openapi_types.UUID `json:"id,omitempty"`
304
- Type scans.PostScanRequestDataType `json:"type"`
305
- }{
306
- Attributes : struct {
307
- Flow scans.Flow `json:"flow"`
308
- ScanOptions * struct {
309
- LimitScanToFiles * []string `json:"limit_scan_to_files,omitempty"`
310
- } `json:"scan_options,omitempty"`
311
- WorkspaceId * openapi_types.UUID `json:"workspace_id,omitempty"`
312
- WorkspaceUrl string `json:"workspace_url"`
313
- }{
314
- Flow : a .flow ,
315
- WorkspaceUrl : fmt .Sprintf ("http://workspace-service/workspaces/%s" , workspaceId ),
316
- WorkspaceId : & workspaceUUID ,
317
- ScanOptions : scanOptions ,
318
- },
319
- Type : "workspace" ,
320
- }
321
-
322
- createScanResponse , err := client .CreateScanWorkspaceJobForUserWithApplicationVndAPIPlusJSONBodyWithResponse (
323
- ctx ,
324
- org ,
325
- & orchestrationClient.CreateScanWorkspaceJobForUserParams {Version : "2024-02-16~experimental" },
326
- orchestrationClient.CreateScanWorkspaceJobForUserApplicationVndAPIPlusJSONRequestBody {Data : data })
327
-
328
- if err != nil {
329
- return nil , fmt .Errorf ("failed to trigger scan: %w" , err )
330
- }
331
-
332
- var scanJobId openapi_types.UUID
333
- var msg string
334
- switch createScanResponse .StatusCode () {
335
- case 201 :
336
- scanJobId = createScanResponse .ApplicationvndApiJSON201 .Data .Id
337
- a .logger .Debug ().Str ("workspaceId" , workspaceId ).Msg ("starting scan" )
338
- case 400 :
339
- msg = createScanResponse .ApplicationvndApiJSON400 .Errors [0 ].Detail
340
- case 401 :
341
- msg = createScanResponse .ApplicationvndApiJSON401 .Errors [0 ].Detail
342
- case 403 :
343
- msg = createScanResponse .ApplicationvndApiJSON403 .Errors [0 ].Detail
344
- case 404 :
345
- msg = createScanResponse .ApplicationvndApiJSON404 .Errors [0 ].Detail
346
- case 429 :
347
- msg = createScanResponse .ApplicationvndApiJSON429 .Errors [0 ].Detail
348
- case 500 :
349
- msg = createScanResponse .ApplicationvndApiJSON500 .Errors [0 ].Detail
350
- }
351
- if msg != "" {
352
- return nil , errors .New (msg )
353
- }
354
-
355
- return & scanJobId , nil
356
- }
357
-
358
- func (a * analysisOrchestrator ) pollScanForFindings (ctx context.Context , client * orchestrationClient.ClientWithResponses , org uuid.UUID , scanJobId openapi_types.UUID ) (* sarif.SarifResponse , error ) {
359
- method := "analysis.pollScanForFindings"
360
- logger := a .logger .With ().Str ("method" , method ).Logger ()
361
-
362
- pollingTicker := time .NewTicker (1 * time .Second )
363
- defer pollingTicker .Stop ()
364
- timeoutTimer := time .NewTimer (a .config .SnykCodeAnalysisTimeout ())
365
- defer timeoutTimer .Stop ()
366
- for {
367
- select {
368
- case <- timeoutTimer .C :
369
- msg := "Snyk Code analysis timed out"
370
- logger .Error ().Str ("scanJobId" , scanJobId .String ()).Msg (msg )
371
- return nil , errors .New (msg )
372
- case <- pollingTicker .C :
373
- findingsUrl , complete , err := a .retrieveFindingsURL (ctx , client , org , scanJobId )
374
- if err != nil {
375
- return nil , err
376
- }
377
- if ! complete {
378
- continue
379
- }
380
-
381
- findings , err := a .retrieveFindings (ctx , scanJobId , findingsUrl )
382
- if err != nil {
383
- return nil , err
384
- }
385
-
386
- return findings , nil
387
- }
388
- }
389
- }
390
-
391
- func (a * analysisOrchestrator ) retrieveFindingsURL (ctx context.Context , client * orchestrationClient.ClientWithResponses , org uuid.UUID , scanJobId openapi_types.UUID ) (string , bool , error ) {
392
- method := "analysis.retrieveFindingsURL"
393
- logger := a .logger .With ().Str ("method" , method ).Logger ()
394
- logger .Debug ().Msg ("retrieving findings URL" )
395
-
396
- httpResponse , err := client .GetScanWorkspaceJobForUserWithResponse (
397
- ctx ,
398
- org ,
399
- scanJobId ,
400
- & orchestrationClient.GetScanWorkspaceJobForUserParams {Version : "2024-02-16~experimental" },
401
- )
402
- if err != nil {
403
- logger .Err (err ).Str ("scanJobId" , scanJobId .String ()).Msg ("error requesting the ScanJobResult" )
404
- return "" , true , err
405
- }
406
-
407
- var msg string
408
- switch httpResponse .StatusCode () {
409
- case 200 :
410
- scanJobStatus := httpResponse .ApplicationvndApiJSON200 .Data .Attributes .Status
411
- if scanJobStatus == scans .ScanJobResultsAttributesStatusInProgress {
412
- return "" , false , nil
413
- } else {
414
- findingsUrl := ""
415
-
416
- if len (httpResponse .ApplicationvndApiJSON200 .Data .Attributes .Components ) > 0 && httpResponse .ApplicationvndApiJSON200 .Data .Attributes .Components [0 ].FindingsUrl != nil {
417
- findingsUrl = * httpResponse .ApplicationvndApiJSON200 .Data .Attributes .Components [0 ].FindingsUrl
418
- }
419
- return findingsUrl , true , nil
420
- }
421
- case 400 :
422
- msg = httpResponse .ApplicationvndApiJSON400 .Errors [0 ].Detail
423
- case 401 :
424
- msg = httpResponse .ApplicationvndApiJSON401 .Errors [0 ].Detail
425
- case 403 :
426
- msg = httpResponse .ApplicationvndApiJSON403 .Errors [0 ].Detail
427
- case 404 :
428
- msg = httpResponse .ApplicationvndApiJSON404 .Errors [0 ].Detail
429
- case 429 :
430
- msg = httpResponse .ApplicationvndApiJSON429 .Errors [0 ].Detail
431
- case 500 :
432
- msg = httpResponse .ApplicationvndApiJSON500 .Errors [0 ].Detail
433
- }
434
- return "" , true , errors .New (msg )
435
- }
436
-
437
126
func (a * analysisOrchestrator ) retrieveFindings (ctx context.Context , scanJobId uuid.UUID , findingsUrl string ) (* sarif.SarifResponse , error ) {
438
127
method := "analysis.retrieveFindings"
439
128
logger := a .logger .With ().Str ("method" , method ).Logger ()
0 commit comments