Skip to content

Commit 2f78589

Browse files
authored
Refactor commands output formats (#96)
1 parent dd9520d commit 2f78589

File tree

102 files changed

+16632
-5997
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+16632
-5997
lines changed

audit_test.go

Lines changed: 161 additions & 91 deletions
Large diffs are not rendered by default.

commands/audit/audit.go

Lines changed: 113 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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’.\nThis 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’.\nThis 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,26 +186,34 @@ 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 {
216+
if scaScanErr := buildDepTreeAndRunScaScan(auditParallelRunner, auditParams, cmdResults); scaScanErr != nil {
205217
// If error to be caught, we add it to the auditParallelRunner error queue and continue. The error need not be returned
206218
_ = 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)
207219
}
@@ -220,12 +232,9 @@ func RunAudit(auditParams *AuditParams) (results *utils.Results, err error) {
220232
go func() {
221233
defer auditParallelRunner.ErrWg.Done()
222234
for e := range auditParallelRunner.ErrorsQueue {
223-
results.ScansErr = errors.Join(results.ScansErr, e)
235+
cmdResults.Error = errors.Join(cmdResults.Error, e)
224236
}
225237
}()
226-
if auditParams.Progress() != nil {
227-
auditParams.Progress().SetHeadlineMsg("Scanning for issues")
228-
}
229238
auditParallelRunner.Runner.Run()
230239
auditParallelRunner.ErrWg.Wait()
231240
return
@@ -239,8 +248,8 @@ func isEntitledForJas(xrayManager *xray.XrayServicesManager, auditParams *AuditP
239248
return jas.IsEntitledForJas(xrayManager, auditParams.xrayVersion)
240249
}
241250

242-
func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams *AuditParams, results *utils.Results, jfrogAppsConfig *jfrogappsconfig.JFrogAppsConfig) (jasScanner *jas.JasScanner, err error) {
243-
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 {
244253
log.Info("Not entitled for JAS, skipping advance security scans...")
245254
return
246255
}
@@ -249,7 +258,9 @@ func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams
249258
err = fmt.Errorf("failed to get server details: %s", err.Error())
250259
return
251260
}
252-
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()
253264
if err != nil {
254265
err = fmt.Errorf("failed to create jas scanner: %s", err.Error())
255266
return
@@ -259,26 +270,95 @@ func RunJasScans(auditParallelRunner *utils.SecurityParallelRunner, auditParams
259270
}
260271
auditParallelRunner.JasWg.Add(1)
261272
if _, jasErr := auditParallelRunner.Runner.AddTaskWithError(func(threadId int) error {
262-
return downloadAnalyzerManagerAndRunScanners(auditParallelRunner, jasScanner, results, auditParams, threadId)
273+
return downloadAnalyzerManagerAndRunScanners(auditParallelRunner, scanResults, serverDetails, auditParams, jasScanner, jfrogAppsConfig, threadId)
263274
}, auditParallelRunner.AddErrorToChan); jasErr != nil {
264275
auditParallelRunner.AddErrorToChan(fmt.Errorf("failed to create AM downloading task, skipping JAS scans...: %s", jasErr.Error()))
265276
}
266277
return
267278
}
268279

269-
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) {
270282
defer func() {
271283
auditParallelRunner.JasWg.Done()
272284
}()
273285
if err = jas.DownloadAnalyzerManagerIfNeeded(threadId); err != nil {
274286
return fmt.Errorf("%s failed to download analyzer manager: %s", clientutils.GetLogMsgPrefix(threadId, false), err.Error())
275287
}
276-
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 {
277-
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+
}
312+
}
313+
return
314+
}
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
278326
}
327+
log.Info(fmt.Sprintf("Preforming scans on %d targets:\n%s", len(cmdResults.Targets), scanInfo))
279328
return
280329
}
281330

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+
282362
// This function checks if partial results are allowed. If so we log the error and continue.
283363
// 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.
284364
// If partial results are not allowed and a SecurityParallelRunner is not provided we return the error.

0 commit comments

Comments
 (0)