Skip to content

Commit 58331a3

Browse files
committed
Merge remote-tracking branch 'upstream/dev' into add_pnpm_log_if_err_install
2 parents de51f30 + 2f78589 commit 58331a3

File tree

108 files changed

+16891
-7504
lines changed

Some content is hidden

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

108 files changed

+16891
-7504
lines changed

audit_test.go

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

cli/docs/flags.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ const (
121121
WorkingDirs = "working-dirs"
122122
OutputDir = "output-dir"
123123
SkipAutoInstall = "skip-auto-install"
124+
AllowPartialResults = "allow-partial-results"
124125

125126
// Unique curation flags
126127
CurationOutput = "curation-format"
@@ -157,7 +158,7 @@ var commandFlags = map[string][]string{
157158
url, user, password, accessToken, ServerId, InsecureTls, Project, Watches, RepoPath, Licenses, OutputFormat, ExcludeTestDeps,
158159
useWrapperAudit, DepType, RequirementsFile, Fail, ExtendedTable, WorkingDirs, ExclusionsAudit, Mvn, Gradle, Npm,
159160
Pnpm, Yarn, Go, Nuget, Pip, Pipenv, Poetry, MinSeverity, FixableOnly, ThirdPartyContextualAnalysis, Threads,
160-
Sca, Iac, Sast, Secrets, WithoutCA, ScanVuln, SecretValidation, OutputDir, SkipAutoInstall,
161+
Sca, Iac, Sast, Secrets, WithoutCA, ScanVuln, SecretValidation, OutputDir, SkipAutoInstall, AllowPartialResults,
161162
},
162163
CurationAudit: {
163164
CurationOutput, WorkingDirs, Threads, RequirementsFile,
@@ -232,9 +233,10 @@ var flagsMap = map[string]components.Flag{
232233
"Set to false if you wish to not use the gradle or maven wrapper.",
233234
components.WithBoolDefaultValue(true),
234235
),
235-
WorkingDirs: components.NewStringFlag(WorkingDirs, "A comma-separated list of relative working directories, to determine audit targets locations."),
236-
OutputDir: components.NewStringFlag(OutputDir, "Target directory to save partial results to.", components.SetHiddenStrFlag()),
237-
SkipAutoInstall: components.NewBoolFlag(SkipAutoInstall, "Set to true to skip auto-install of dependencies in un-built modules. Currently supported for Yarn and NPM only.", components.SetHiddenBoolFlag()),
236+
WorkingDirs: components.NewStringFlag(WorkingDirs, "A comma-separated list of relative working directories, to determine audit targets locations."),
237+
OutputDir: components.NewStringFlag(OutputDir, "Target directory to save partial results to.", components.SetHiddenStrFlag()),
238+
SkipAutoInstall: components.NewBoolFlag(SkipAutoInstall, "Set to true to skip auto-install of dependencies in un-built modules. Currently supported for Yarn and NPM only.", components.SetHiddenBoolFlag()),
239+
AllowPartialResults: components.NewBoolFlag(AllowPartialResults, "Set to true to allow partial results and continuance of the scan in case of certain errors.", components.SetHiddenBoolFlag()),
238240
ExclusionsAudit: components.NewStringFlag(
239241
Exclusions,
240242
"List of exclusions separated by semicolons, utilized to skip sub-projects from undergoing an audit. These exclusions may incorporate the * and ? wildcards.",

cli/scancommands.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,8 @@ func CreateAuditCmd(c *components.Context) (*audit.AuditCommand, error) {
478478
SetFixableOnly(c.GetBoolFlagValue(flags.FixableOnly)).
479479
SetThirdPartyApplicabilityScan(c.GetBoolFlagValue(flags.ThirdPartyContextualAnalysis)).
480480
SetScansResultsOutputDir(scansOutputDir).
481-
SetSkipAutoInstall(c.GetBoolFlagValue(flags.SkipAutoInstall))
481+
SetSkipAutoInstall(c.GetBoolFlagValue(flags.SkipAutoInstall)).
482+
SetAllowPartialResults(c.GetBoolFlagValue(flags.AllowPartialResults))
482483

483484
if c.GetStringFlagValue(flags.Watches) != "" {
484485
auditCmd.SetWatches(splitByCommaAndTrim(c.GetStringFlagValue(flags.Watches)))

commands/audit/audit.go

Lines changed: 139 additions & 34 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,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\nSince 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

Comments
 (0)