Skip to content

Commit e00b60b

Browse files
authored
Add entitlment snippet (#711)
1 parent 70fb0e6 commit e00b60b

17 files changed

Lines changed: 82 additions & 45 deletions

File tree

commands/audit/audit.go

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,10 @@ func shouldIncludeVulnerabilities(includeVulnerabilities bool, watches []string,
174174

175175
func shouldIncludeSnippetDetection(params *AuditParams) bool {
176176
if profile := params.GetConfigProfile(); profile != nil && len(profile.Modules) > 0 {
177-
if profile.Modules[0].ScanConfig.ScaScannerConfig.EnableSnippetDetection {
178-
return true
177+
for _, module := range profile.Modules {
178+
if module.ScanConfig.ScaScannerConfig.EnableSnippetDetection {
179+
return true
180+
}
179181
}
180182
}
181183
if params.resultsContext.IncludeSnippetDetection {
@@ -282,14 +284,14 @@ func (auditCmd *AuditCommand) Run() (err error) {
282284

283285
func (auditCmd *AuditCommand) getResultWriter(cmdResults *results.SecurityCommandResults) *output.ResultsWriter {
284286
var messages []string
285-
if !cmdResults.EntitledForJas {
287+
if !cmdResults.Entitlements.Jas {
286288
messages = []string{coreutils.PrintTitle("In addition to SCA, the ‘jf audit’ command supports the following Advanced Security scans: 'Contextual Analysis', 'Secrets Detection', 'IaC', and ‘SAST’.\nThese scans are available within Advanced Security license. Read more - ") + coreutils.PrintLink(utils.JasInfoURL)}
287289
}
288290
if cmdResults.ResultsPlatformUrl != "" && auditCmd.gitContext != nil {
289291
messages = append(messages, output.GetCommandResultsPlatformUrlMessage(cmdResults, true))
290292
}
291293
var tableNotes []string
292-
if cmdResults.EntitledForJas && cmdResults.HasViolationContext() && len(cmdResults.ResultContext.GitRepoHttpsCloneUrl) == 0 {
294+
if cmdResults.Entitlements.Jas && cmdResults.HasViolationContext() && len(cmdResults.ResultContext.GitRepoHttpsCloneUrl) == 0 {
293295
tableNotes = []string{"Note: The following vulnerability violations are NOT supported by this audit:\n- Secrets\n- Infrastructure as Code (IaC)\n- Static Application Security Testing (SAST)"}
294296
}
295297
return output.NewResultsWriter(cmdResults).
@@ -377,7 +379,6 @@ func getScanLogicOptions(params *AuditParams) (bomGenOptions []bom.SbomGenerator
377379
xrayplugin.WithBinaryPath(params.CustomBomGenBinaryPath()),
378380
xrayplugin.WithIgnorePatterns(params.Exclusions()),
379381
xrayplugin.WithSpecificTechnologies(params.Technologies()),
380-
xrayplugin.WithSnippetDetection(shouldIncludeSnippetDetection(params)),
381382
}
382383
// Scan Strategies Options
383384
scanGraphParams, err := params.ToXrayScanGraphParams()
@@ -418,17 +419,29 @@ func initAuditCmdResults(params *AuditParams) (cmdResults *results.SecurityComma
418419
entitledForJas, err := isEntitledForJas(xrayManager, params)
419420
if err != nil {
420421
return cmdResults.AddGeneralError(err, false)
421-
} else {
422-
cmdResults.SetEntitledForJas(entitledForJas)
423422
}
423+
cmdResults.SetEntitledForJas(entitledForJas)
424424
if entitledForJas {
425+
// Validate required installed software
425426
if utils.IsJASRequested(cmdResults.CmdType, params.ScansToPerform()...) {
426427
if err = jas.ValidateRequiredInstalledSoftware(); err != nil {
427428
return cmdResults.AddGeneralError(err, false)
428429
}
429430
}
431+
// Validate secret validation entitlement
430432
cmdResults.SetSecretValidation(jas.CheckForSecretValidation(xrayManager, params.GetXrayVersion(), slices.Contains(params.ScansToPerform(), utils.SecretTokenValidationScan)))
431433
}
434+
// Snippet detection requires JAS entitlement and also the Snippet Detection feature is enabled in Xray.
435+
if shouldIncludeSnippetDetection(params) {
436+
entitledForSnippetDetection, err := isEntitledForSnippetDetection(entitledForJas, xrayManager, params)
437+
if err != nil {
438+
return cmdResults.AddGeneralError(err, false)
439+
}
440+
if !entitledForSnippetDetection {
441+
return cmdResults.AddGeneralError(fmt.Errorf("snippet detection is requested but the JFrog instance is not entitled for it"), false)
442+
}
443+
cmdResults.SetEntitledForSnippetDetection(entitledForSnippetDetection)
444+
}
432445
return
433446
}
434447

@@ -440,6 +453,14 @@ func isEntitledForJas(xrayManager *xray.XrayServicesManager, auditParams *AuditP
440453
return jas.IsEntitledForJas(xrayManager, auditParams.GetXrayVersion())
441454
}
442455

456+
func isEntitledForSnippetDetection(isEntitledForJas bool, xrayManager *xray.XrayServicesManager, auditParams *AuditParams) (entitled bool, err error) {
457+
if !isEntitledForJas {
458+
return false, nil
459+
}
460+
// Snippet detection requires JAS entitlement and also the Snippet Detection feature is enabled in Xray.
461+
return xrayutils.IsEntitled(xrayManager, auditParams.GetXrayVersion(), xrayplugin.SnippetDetectionFeatureId)
462+
}
463+
443464
func populateScanTargets(cmdResults *results.SecurityCommandResults, params *AuditParams) {
444465
// Populate the scan targets based on the provided parameters.
445466
detectScanTargets(cmdResults, params)
@@ -458,7 +479,10 @@ func populateScanTargets(cmdResults *results.SecurityCommandResults, params *Aud
458479
// No need to generate the SBOM if we are not going to use it.
459480
continue
460481
}
461-
bom.GenerateSbomForTarget(params.BomGenerator().WithOptions(buildinfo.WithDescriptors(targetResult.GetDescriptors())),
482+
bom.GenerateSbomForTarget(params.BomGenerator().WithOptions(
483+
buildinfo.WithDescriptors(targetResult.GetDescriptors()),
484+
xrayplugin.WithSnippetDetection(shouldIncludeSnippetDetection(params)),
485+
),
462486
bom.SbomGeneratorParams{
463487
Target: targetResult,
464488
AllowPartialResults: params.AllowPartialResults(),
@@ -616,7 +640,7 @@ func addScaScansToRunner(auditParallelRunner *utils.SecurityParallelRunner, audi
616640
}
617641

618642
func addJasScansToRunner(auditParallelRunner *utils.SecurityParallelRunner, auditParams *AuditParams, scanResults *results.SecurityCommandResults, isNewFlow bool) (jasScanner *jas.JasScanner, generalError error) {
619-
if !scanResults.EntitledForJas {
643+
if !scanResults.Entitlements.Jas {
620644
log.Info("Advanced Security is not enabled on this system, so Advanced Security scans were skipped...")
621645
return
622646
}

commands/git/audit/gitaudit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func RunGitAudit(params GitAuditParams) (scanResults *results.SecurityCommandRes
136136

137137
func (gaCmd *GitAuditCommand) getResultWriter(cmdResults *results.SecurityCommandResults) *output.ResultsWriter {
138138
var messages []string
139-
if !cmdResults.EntitledForJas {
139+
if !cmdResults.Entitlements.Jas {
140140
messages = []string{coreutils.PrintTitle("In addition to SCA, the ‘jf git audit’ command supports the following Advanced Security scans: 'Contextual Analysis', 'Secrets Detection', 'IaC', and ‘SAST’.\nThese scans are available within Advanced Security license. Read more - ") + coreutils.PrintLink(utils.JasInfoURL)}
141141
}
142142
if cmdResults.ResultsPlatformUrl != "" {

commands/scan/scan.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ func (scanCmd *ScanCommand) initScanCmdResults(cmdType utils.CommandType) (xrayM
352352
func (scanCmd *ScanCommand) prepareForScan(cmdResults *results.SecurityCommandResults, xrayManager *xrayClient.XrayServicesManager) (err error) {
353353
// Download (if needed) the analyzer manager in a background routine.
354354
AnalyzerErrGroup := new(errgroup.Group)
355-
if cmdResults.EntitledForJas && utils.IsJASRequested(cmdResults.CmdType, scanCmd.scansToPerform...) {
355+
if cmdResults.Entitlements.Jas && utils.IsJASRequested(cmdResults.CmdType, scanCmd.scansToPerform...) {
356356
if scanCmd.customAnalyzerManagerPath == "" {
357357
AnalyzerErrGroup.Go(func() error {
358358
return jas.DownloadAnalyzerManagerIfNeeded(0)
@@ -444,7 +444,7 @@ func (scanCmd *ScanCommand) createIndexerHandlerFunc(file *spec.File, cmdResults
444444
}
445445
// SCA scan
446446
targetCompId, graphScanResults, err := scanCmd.RunBinaryScaScan(file.Target, cmdResults, targetResults, deprecatedGraph, scanThreadId)
447-
if err != nil || !cmdResults.EntitledForJas {
447+
if err != nil || !cmdResults.Entitlements.Jas {
448448
return
449449
}
450450
// Run Jas scans

git_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,8 @@ func TestGitAuditJasSkipNotApplicableCvesViolations(t *testing.T) {
271271
xrayVersion, xscVersion, "",
272272
validations.ValidationParams{
273273
Violations: &validations.ViolationCount{
274-
ValidateScan: &validations.ScanCount{Sca: 10, Sast: 2, Secrets: 2},
275-
ValidateApplicabilityStatus: &validations.ApplicabilityStatusCount{NotApplicable: 5, NotCovered: 5, Inactive: 2},
274+
ValidateScan: &validations.ScanCount{Sca: 11, Sast: 2, Secrets: 2},
275+
ValidateApplicabilityStatus: &validations.ApplicabilityStatusCount{NotApplicable: 5, NotCovered: 6, Inactive: 2},
276276
},
277277
ExactResultsMatch: true,
278278
},
@@ -299,8 +299,8 @@ func TestGitAuditJasSkipNotApplicableCvesViolations(t *testing.T) {
299299
xrayVersion, xscVersion, "",
300300
validations.ValidationParams{
301301
Violations: &validations.ViolationCount{
302-
ValidateScan: &validations.ScanCount{Sca: 5, Sast: 2, Secrets: 2},
303-
ValidateApplicabilityStatus: &validations.ApplicabilityStatusCount{NotCovered: 5, Inactive: 2},
302+
ValidateScan: &validations.ScanCount{Sca: 6, Sast: 2, Secrets: 2},
303+
ValidateApplicabilityStatus: &validations.ApplicabilityStatusCount{NotCovered: 6, Inactive: 2},
304304
},
305305
ExactResultsMatch: true,
306306
},

policy/enforcer/policyenforcer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ func locateJasVulnerabilityInfo(cmdResults *results.SecurityCommandResults, jasT
312312
log.Debug(fmt.Sprintf("Skipping %s violation search for target %s with no Jas results", jasType, target.ScanTarget))
313313
continue
314314
}
315-
if err := results.ForEachJasIssue(target.JasResults.GetVulnerabilitiesResults(jasType), cmdResults.EntitledForJas,
315+
if err := results.ForEachJasIssue(target.JasResults.GetVulnerabilitiesResults(jasType), cmdResults.Entitlements.Jas,
316316
func(run *sarif.Run, rule *sarif.ReportingDescriptor, severity severityutils.Severity, result *sarif.Result, location *sarif.Location) error {
317317
if !found && isMatchingJasViolation(id, jasType, rule, location, run.Invocations, violation) {
318318
// Found a relevant issue (JAS Violations only provide abbreviation and file name, no region so we match only by those)

policy/local/localconvertor.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,24 +50,24 @@ func (d *DeprecatedViolationGenerator) GenerateViolations(cmdResults *results.Se
5050
for _, target := range cmdResults.Targets {
5151
// SCA violations (from DeprecatedXrayResults)
5252
if target.ScaResults != nil {
53-
if e := d.generateScaViolations(target, &convertedViolations, cmdResults.EntitledForJas); e != nil {
53+
if e := d.generateScaViolations(target, &convertedViolations, cmdResults.Entitlements.Jas); e != nil {
5454
err = errors.Join(err, fmt.Errorf("failed to convert SCA violations for target %s: %w", target.Target, e))
5555
}
5656
}
5757
// JAS violations (from JasResults)
5858
if target.JasResults != nil {
5959
if len(target.JasResults.JasViolations.SecretsScanResults) > 0 {
60-
if e := results.ForEachJasIssue(target.JasResults.JasViolations.SecretsScanResults, cmdResults.EntitledForJas, convertJasViolationsToPolicyViolations(&convertedViolations, jasutils.Secrets)); e != nil {
60+
if e := results.ForEachJasIssue(target.JasResults.JasViolations.SecretsScanResults, cmdResults.Entitlements.Jas, convertJasViolationsToPolicyViolations(&convertedViolations, jasutils.Secrets)); e != nil {
6161
err = errors.Join(err, fmt.Errorf("failed to convert JAS Secret violations for target %s: %w", target.Target, e))
6262
}
6363
}
6464
if len(target.JasResults.JasViolations.IacScanResults) > 0 {
65-
if e := results.ForEachJasIssue(target.JasResults.JasViolations.IacScanResults, cmdResults.EntitledForJas, convertJasViolationsToPolicyViolations(&convertedViolations, jasutils.IaC)); e != nil {
65+
if e := results.ForEachJasIssue(target.JasResults.JasViolations.IacScanResults, cmdResults.Entitlements.Jas, convertJasViolationsToPolicyViolations(&convertedViolations, jasutils.IaC)); e != nil {
6666
err = errors.Join(err, fmt.Errorf("failed to convert JAS IaC violations for target %s: %w", target.Target, e))
6767
}
6868
}
6969
if len(target.JasResults.JasViolations.SastScanResults) > 0 {
70-
if e := results.ForEachJasIssue(target.JasResults.JasViolations.SastScanResults, cmdResults.EntitledForJas, convertJasViolationsToPolicyViolations(&convertedViolations, jasutils.Sast)); e != nil {
70+
if e := results.ForEachJasIssue(target.JasResults.JasViolations.SastScanResults, cmdResults.Entitlements.Jas, convertJasViolationsToPolicyViolations(&convertedViolations, jasutils.Sast)); e != nil {
7171
err = errors.Join(err, fmt.Errorf("failed to convert JAS SAST violations for target %s: %w", target.Target, e))
7272
}
7373
}

sca/bom/buildinfo/technologies/pnpm/pnpm_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func TestBuildDependencyTreeLimitedDepth(t *testing.T) {
4343
name: "With transitive dependencies",
4444
treeDepth: "1",
4545
expectedUniqueDeps: []string{
46-
"npm://axios:1.13.6",
46+
"npm://axios:1.14.0",
4747
"npm://balaganjs:1.0.0",
4848
"npm://yargs:13.3.0",
4949
"npm://zen-website:1.0.0",
@@ -53,7 +53,7 @@ func TestBuildDependencyTreeLimitedDepth(t *testing.T) {
5353
Nodes: []*xrayUtils.GraphNode{
5454
{
5555
Id: "npm://balaganjs:1.0.0",
56-
Nodes: []*xrayUtils.GraphNode{{Id: "npm://axios:1.13.6"}, {Id: "npm://yargs:13.3.0"}},
56+
Nodes: []*xrayUtils.GraphNode{{Id: "npm://axios:1.14.0"}, {Id: "npm://yargs:13.3.0"}},
5757
},
5858
},
5959
},

sca/bom/xrayplugin/xraylibbom.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import (
1616
"github.com/jfrog/jfrog-client-go/utils/log"
1717
)
1818

19+
// SnippetDetectionFeatureId is "curation" because snippet detection is gated by the curation entitlement on the Xray server.
20+
const SnippetDetectionFeatureId = "curation"
21+
1922
type XrayLibBomGenerator struct {
2023
binaryPath string
2124
snippetDetection bool

utils/results/conversion/convertor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func parseCommandResults[T interface{}](params ResultConvertParams, parser Resul
123123
if err = parseScaResults(params, parser, cmdResults.CmdType, targetScansResults); err != nil {
124124
return
125125
}
126-
if !cmdResults.EntitledForJas {
126+
if !cmdResults.Entitlements.Jas {
127127
continue
128128
}
129129
if err = parseJasResults(params, parser, targetScansResults); err != nil {

utils/results/conversion/cyclonedxparser/cyclonedxparser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func (cdc *CmdResultsCycloneDxConverter) Get() (bom *cdxutils.FullBOM, err error
7373
}
7474

7575
func (cdc *CmdResultsCycloneDxConverter) Reset(metadata results.ResultsMetaData, statusCodes results.ResultsStatus, multipleTargets bool) (err error) {
76-
cdc.entitledForJas = metadata.EntitledForJas
76+
cdc.entitledForJas = metadata.Entitlements.Jas
7777
cdc.gitContext = metadata.GitContext
7878
cdc.xrayVersion = metadata.XrayVersion
7979
// Reset the BOM

0 commit comments

Comments
 (0)