@@ -4,20 +4,26 @@ import (
44 "errors"
55 "fmt"
66
7- "github.com/jfrog/gofrog/log"
87 jfrogappsconfig "github.com/jfrog/jfrog-apps-config/go"
8+ "github.com/jfrog/jfrog-cli-core/v2/utils/config"
99 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
10+ "github.com/jfrog/jfrog-cli-security/commands/audit/sca"
1011 "github.com/jfrog/jfrog-cli-security/jas"
1112 "github.com/jfrog/jfrog-cli-security/jas/applicability"
1213 "github.com/jfrog/jfrog-cli-security/jas/runner"
1314 "github.com/jfrog/jfrog-cli-security/jas/secrets"
1415 "github.com/jfrog/jfrog-cli-security/utils"
16+ "github.com/jfrog/jfrog-cli-security/utils/results"
17+ "github.com/jfrog/jfrog-cli-security/utils/results/output"
18+ "github.com/jfrog/jfrog-cli-security/utils/techutils"
1519 "github.com/jfrog/jfrog-cli-security/utils/xray/scangraph"
1620 "github.com/jfrog/jfrog-cli-security/utils/xsc"
1721 "golang.org/x/exp/slices"
1822
1923 xrayutils "github.com/jfrog/jfrog-cli-security/utils/xray"
2024 clientutils "github.com/jfrog/jfrog-client-go/utils"
25+ "github.com/jfrog/jfrog-client-go/utils/io/fileutils"
26+ "github.com/jfrog/jfrog-client-go/utils/log"
2127 "github.com/jfrog/jfrog-client-go/xray"
2228 "github.com/jfrog/jfrog-client-go/xray/services"
2329 xscservices "github.com/jfrog/jfrog-client-go/xsc/services"
@@ -130,11 +136,10 @@ func (auditCmd *AuditCommand) Run() (err error) {
130136 }
131137 }
132138 var messages []string
133- if ! auditResults .ExtendedScanResults . EntitledForJas {
134- messages = []string {coreutils .PrintTitle ("The ‘jf audit’ command also supports JFrog Advanced Security features, such as 'Contextual Analysis', 'Secret Detection', 'IaC Scan' and ‘SAST’.\n This feature isn't enabled on your system. Read more - " ) + coreutils .PrintLink ("https://jfrog.com/xray/" )}
139+ if ! auditResults .EntitledForJas {
140+ messages = []string {coreutils .PrintTitle ("The ‘jf audit’ command also supports JFrog Advanced Security features, such as 'Contextual Analysis', 'Secret Detection', 'IaC Scan' and ‘SAST’.\n This feature isn't enabled on your system. Read more - " ) + coreutils .PrintLink (utils . JasInfoURL )}
135141 }
136- if err = utils .NewResultsWriter (auditResults ).
137- SetIsMultipleRootProject (auditResults .IsMultipleProject ()).
142+ if err = output .NewResultsWriter (auditResults ).
138143 SetHasViolationContext (auditCmd .HasViolationContext ()).
139144 SetIncludeVulnerabilities (auditCmd .IncludeVulnerabilities ).
140145 SetIncludeLicenses (auditCmd .IncludeLicenses ).
@@ -146,13 +151,13 @@ func (auditCmd *AuditCommand) Run() (err error) {
146151 return
147152 }
148153
149- if auditResults .ScansErr != nil {
150- return auditResults . ScansErr
154+ if err = auditResults .GetErrors (); err != nil {
155+ return
151156 }
152157
153158 // Only in case Xray's context was given (!auditCmd.IncludeVulnerabilities), and the user asked to fail the build accordingly, do so.
154- if auditCmd .Fail && ! auditCmd .IncludeVulnerabilities && utils .CheckIfFailBuild (auditResults .GetScaScansXrayResults ()) {
155- err = utils .NewFailBuildError ()
159+ if auditCmd .Fail && ! auditCmd .IncludeVulnerabilities && results .CheckIfFailBuild (auditResults .GetScaScansXrayResults ()) {
160+ err = results .NewFailBuildError ()
156161 }
157162 return
158163}
@@ -168,9 +173,8 @@ func (auditCmd *AuditCommand) HasViolationContext() bool {
168173// Runs an audit scan based on the provided auditParams.
169174// Returns an audit Results object containing all the scan results.
170175// If the current server is entitled for JAS, the advanced security results will be included in the scan results.
171- func RunAudit (auditParams * AuditParams ) (results * utils.Results , err error ) {
172- // Initialize Results struct
173- results = utils .NewAuditResults (utils .SourceCode )
176+ func RunAudit (auditParams * AuditParams ) (cmdResults * results.SecurityCommandResults , err error ) {
177+ // Prepare
174178 serverDetails , err := auditParams .ServerDetails ()
175179 if err != nil {
176180 return
@@ -182,27 +186,36 @@ func RunAudit(auditParams *AuditParams) (results *utils.Results, err error) {
182186 if err = clientutils .ValidateMinimumVersion (clientutils .Xray , auditParams .xrayVersion , scangraph .GraphScanMinXrayVersion ); err != nil {
183187 return
184188 }
185- results .XrayVersion = auditParams .xrayVersion
186- results .ExtendedScanResults .EntitledForJas , err = isEntitledForJas (xrayManager , auditParams )
189+ entitledForJas , err := isEntitledForJas (xrayManager , auditParams )
187190 if err != nil {
188191 return
189192 }
190- results .ExtendedScanResults .SecretValidation = jas .CheckForSecretValidation (xrayManager , auditParams .xrayVersion , slices .Contains (auditParams .AuditBasicParams .ScansToPerform (), utils .SecretTokenValidationScan ))
191- results .MultiScanId = auditParams .commonGraphScanParams .MultiScanId
192- auditParallelRunner := utils .CreateSecurityParallelRunner (auditParams .threads )
193- auditParallelRunner .ErrWg .Add (1 )
194- jfrogAppsConfig , err := jas .CreateJFrogAppsConfig (auditParams .workingDirs )
193+ // Initialize Results struct
194+ cmdResults = initCmdResults (
195+ entitledForJas ,
196+ jas .CheckForSecretValidation (xrayManager , auditParams .xrayVersion , slices .Contains (auditParams .AuditBasicParams .ScansToPerform (), utils .SecretTokenValidationScan )),
197+ auditParams ,
198+ )
199+ jfrogAppsConfig , err := jas .CreateJFrogAppsConfig (cmdResults .GetTargetsPaths ())
195200 if err != nil {
196- return results , fmt .Errorf ("failed to create JFrogAppsConfig: %s" , err .Error ())
201+ return cmdResults , fmt .Errorf ("failed to create JFrogAppsConfig: %s" , err .Error ())
197202 }
203+ // Initialize the parallel runner
204+ auditParallelRunner := utils .CreateSecurityParallelRunner (auditParams .threads )
205+ auditParallelRunner .ErrWg .Add (1 )
206+ // Add the JAS scans to the parallel runner
198207 var jasScanner * jas.JasScanner
199208 var jasScanErr error
200- if jasScanner , jasScanErr = RunJasScans (auditParallelRunner , auditParams , results , jfrogAppsConfig ); jasScanErr != nil {
209+ if jasScanner , jasScanErr = RunJasScans (auditParallelRunner , auditParams , cmdResults , jfrogAppsConfig ); jasScanErr != nil {
201210 auditParallelRunner .AddErrorToChan (jasScanErr )
202211 }
212+ if auditParams .Progress () != nil {
213+ auditParams .Progress ().SetHeadlineMsg ("Scanning for issues" )
214+ }
203215 // The sca scan doesn't require the analyzer manager, so it can run separately from the analyzer manager download routine.
204- if scaScanErr := buildDepTreeAndRunScaScan (auditParallelRunner , auditParams , results ); scaScanErr != nil {
205- auditParallelRunner .AddErrorToChan (scaScanErr )
216+ if scaScanErr := buildDepTreeAndRunScaScan (auditParallelRunner , auditParams , cmdResults ); scaScanErr != nil {
217+ // If error to be caught, we add it to the auditParallelRunner error queue and continue. The error need not be returned
218+ _ = createErrorIfPartialResultsDisabled (auditParams , auditParallelRunner , fmt .Sprintf ("An error has occurred during SCA scan process. SCA scan is skipped for the following directories: %s." , auditParams .workingDirs ), scaScanErr )
206219 }
207220 go func () {
208221 auditParallelRunner .ScaScansWg .Wait ()
@@ -219,12 +232,9 @@ func RunAudit(auditParams *AuditParams) (results *utils.Results, err error) {
219232 go func () {
220233 defer auditParallelRunner .ErrWg .Done ()
221234 for e := range auditParallelRunner .ErrorsQueue {
222- results . ScansErr = errors .Join (results . ScansErr , e )
235+ cmdResults . Error = errors .Join (cmdResults . Error , e )
223236 }
224237 }()
225- if auditParams .Progress () != nil {
226- auditParams .Progress ().SetHeadlineMsg ("Scanning for issues" )
227- }
228238 auditParallelRunner .Runner .Run ()
229239 auditParallelRunner .ErrWg .Wait ()
230240 return
@@ -238,8 +248,8 @@ func isEntitledForJas(xrayManager *xray.XrayServicesManager, auditParams *AuditP
238248 return jas .IsEntitledForJas (xrayManager , auditParams .xrayVersion )
239249}
240250
241- func RunJasScans (auditParallelRunner * utils.SecurityParallelRunner , auditParams * AuditParams , results * utils. Results , jfrogAppsConfig * jfrogappsconfig.JFrogAppsConfig ) (jasScanner * jas.JasScanner , err error ) {
242- if ! results . ExtendedScanResults .EntitledForJas {
251+ func RunJasScans (auditParallelRunner * utils.SecurityParallelRunner , auditParams * AuditParams , scanResults * results. SecurityCommandResults , jfrogAppsConfig * jfrogappsconfig.JFrogAppsConfig ) (jasScanner * jas.JasScanner , err error ) {
252+ if ! scanResults .EntitledForJas {
243253 log .Info ("Not entitled for JAS, skipping advance security scans..." )
244254 return
245255 }
@@ -248,7 +258,9 @@ func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams
248258 err = fmt .Errorf ("failed to get server details: %s" , err .Error ())
249259 return
250260 }
251- jasScanner , err = jas .CreateJasScanner (jfrogAppsConfig , serverDetails , auditParams .minSeverityFilter , jas .GetAnalyzerManagerXscEnvVars (auditParams .commonGraphScanParams .MultiScanId , results .ExtendedScanResults .SecretValidation , results .GetScaScannedTechnologies ()... ), auditParams .Exclusions ()... )
261+ auditParallelRunner .ResultsMu .Lock ()
262+ jasScanner , err = jas .CreateJasScanner (serverDetails , scanResults .SecretValidation , auditParams .minSeverityFilter , jas .GetAnalyzerManagerXscEnvVars (auditParams .commonGraphScanParams .MultiScanId , scanResults .GetTechnologies ()... ), auditParams .Exclusions ()... )
263+ auditParallelRunner .ResultsMu .Unlock ()
252264 if err != nil {
253265 err = fmt .Errorf ("failed to create jas scanner: %s" , err .Error ())
254266 return
@@ -258,22 +270,115 @@ func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams
258270 }
259271 auditParallelRunner .JasWg .Add (1 )
260272 if _ , jasErr := auditParallelRunner .Runner .AddTaskWithError (func (threadId int ) error {
261- return downloadAnalyzerManagerAndRunScanners (auditParallelRunner , jasScanner , results , auditParams , threadId )
273+ return downloadAnalyzerManagerAndRunScanners (auditParallelRunner , scanResults , serverDetails , auditParams , jasScanner , jfrogAppsConfig , threadId )
262274 }, auditParallelRunner .AddErrorToChan ); jasErr != nil {
263275 auditParallelRunner .AddErrorToChan (fmt .Errorf ("failed to create AM downloading task, skipping JAS scans...: %s" , jasErr .Error ()))
264276 }
265277 return
266278}
267279
268- func downloadAnalyzerManagerAndRunScanners (auditParallelRunner * utils.SecurityParallelRunner , scanner * jas.JasScanner , scanResults * utils.Results , auditParams * AuditParams , threadId int ) (err error ) {
280+ func downloadAnalyzerManagerAndRunScanners (auditParallelRunner * utils.SecurityParallelRunner , scanResults * results.SecurityCommandResults ,
281+ serverDetails * config.ServerDetails , auditParams * AuditParams , scanner * jas.JasScanner , jfrogAppsConfig * jfrogappsconfig.JFrogAppsConfig , threadId int ) (err error ) {
269282 defer func () {
270283 auditParallelRunner .JasWg .Done ()
271284 }()
272285 if err = jas .DownloadAnalyzerManagerIfNeeded (threadId ); err != nil {
273286 return fmt .Errorf ("%s failed to download analyzer manager: %s" , clientutils .GetLogMsgPrefix (threadId , false ), err .Error ())
274287 }
275- if err = runner .AddJasScannersTasks (auditParallelRunner , scanResults , auditParams .DirectDependencies (), auditParams .thirdPartyApplicabilityScan , scanner , applicability .ApplicabilityScannerType , secrets .SecretsScannerType , auditParallelRunner .AddErrorToChan , auditParams .ScansToPerform (), auditParams .configProfile , auditParams .scanResultsOutputDir ); err != nil {
276- return fmt .Errorf ("%s failed to run JAS scanners: %s" , clientutils .GetLogMsgPrefix (threadId , false ), err .Error ())
288+ // Run JAS scanners for each scan target
289+ for _ , scan := range scanResults .Targets {
290+ module := jas .GetModule (scan .Target , jfrogAppsConfig )
291+ if module == nil {
292+ scan .AddError (fmt .Errorf ("can't find module for path %s" , scan .Target ))
293+ continue
294+ }
295+ params := runner.JasRunnerParams {
296+ Runner : auditParallelRunner ,
297+ ServerDetails : serverDetails ,
298+ Scanner : scanner ,
299+ Module : * module ,
300+ ConfigProfile : auditParams .configProfile ,
301+ ScansToPreform : auditParams .ScansToPerform (),
302+ SecretsScanType : secrets .SecretsScannerType ,
303+ DirectDependencies : auditParams .DirectDependencies (),
304+ ThirdPartyApplicabilityScan : auditParams .thirdPartyApplicabilityScan ,
305+ ApplicableScanType : applicability .ApplicabilityScannerType ,
306+ ScanResults : scan ,
307+ TargetOutputDir : auditParams .scanResultsOutputDir ,
308+ }
309+ if err = runner .AddJasScannersTasks (params ); err != nil {
310+ return fmt .Errorf ("%s failed to run JAS scanners: %s" , clientutils .GetLogMsgPrefix (threadId , false ), err .Error ())
311+ }
277312 }
278313 return
279314}
315+
316+ func initCmdResults (entitledForJas , secretValidation bool , params * AuditParams ) (cmdResults * results.SecurityCommandResults ) {
317+ cmdResults = results .NewCommandResults (utils .SourceCode , params .xrayVersion , entitledForJas , secretValidation ).SetMultiScanId (params .commonGraphScanParams .MultiScanId )
318+ detectScanTargets (cmdResults , params )
319+ if params .IsRecursiveScan () && len (params .workingDirs ) == 1 && len (cmdResults .Targets ) == 0 {
320+ // No SCA targets were detected, add the root directory as a target for JAS scans.
321+ cmdResults .NewScanResults (results.ScanTarget {Target : params .workingDirs [0 ]})
322+ }
323+ scanInfo , err := coreutils .GetJsonIndent (cmdResults )
324+ if err != nil {
325+ return
326+ }
327+ log .Info (fmt .Sprintf ("Preforming scans on %d targets:\n %s" , len (cmdResults .Targets ), scanInfo ))
328+ return
329+ }
330+
331+ func detectScanTargets (cmdResults * results.SecurityCommandResults , params * AuditParams ) {
332+ for _ , requestedDirectory := range params .workingDirs {
333+ if ! fileutils .IsPathExists (requestedDirectory , false ) {
334+ log .Warn ("The working directory" , requestedDirectory , "doesn't exist. Skipping SCA scan..." )
335+ continue
336+ }
337+ // Detect descriptors and technologies in the requested directory.
338+ techToWorkingDirs , err := techutils .DetectTechnologiesDescriptors (requestedDirectory , params .IsRecursiveScan (), params .Technologies (), getRequestedDescriptors (params ), sca .GetExcludePattern (params .AuditBasicParams ))
339+ if err != nil {
340+ log .Warn ("Couldn't detect technologies in" , requestedDirectory , "directory." , err .Error ())
341+ continue
342+ }
343+ // Create scans to preform
344+ for tech , workingDirs := range techToWorkingDirs {
345+ if tech == techutils .Dotnet {
346+ // We detect Dotnet and Nuget the same way, if one detected so does the other.
347+ // We don't need to scan for both and get duplicate results.
348+ continue
349+ }
350+ if len (workingDirs ) == 0 {
351+ // Requested technology (from params) descriptors/indicators were not found, scan only requested directory for this technology.
352+ cmdResults .NewScanResults (results.ScanTarget {Target : requestedDirectory , Technology : tech })
353+ }
354+ for workingDir , descriptors := range workingDirs {
355+ // Add scan for each detected working directory.
356+ cmdResults .NewScanResults (results.ScanTarget {Target : workingDir , Technology : tech }).SetDescriptors (descriptors ... )
357+ }
358+ }
359+ }
360+ }
361+
362+ // This function checks if partial results are allowed. If so we log the error and continue.
363+ // If partial results are not allowed and a SecurityParallelRunner is provided we add the error to its error queue and return without an error, since the errors will be later collected from the queue.
364+ // If partial results are not allowed and a SecurityParallelRunner is not provided we return the error.
365+ func createErrorIfPartialResultsDisabled (auditParams * AuditParams , auditParallelRunner * utils.SecurityParallelRunner , extraMassageForLog string , err error ) error {
366+ if err == nil {
367+ return nil
368+ }
369+
370+ if auditParams .AllowPartialResults () {
371+ if extraMassageForLog == "" {
372+ extraMassageForLog = "An error has occurred during the audit scans"
373+ }
374+ log .Warn (fmt .Sprintf ("%s\n Since partial results are allowed, the error is skipped: %s" , extraMassageForLog , err .Error ()))
375+ return nil
376+ }
377+
378+ // When SecurityParallelRunner is provided we add the error to the queue, otherwise we return the error
379+ if auditParallelRunner != nil {
380+ auditParallelRunner .AddErrorToChan (err )
381+ return nil
382+ }
383+ return err
384+ }
0 commit comments