Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
)

// replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security dev
replace github.com/jfrog/jfrog-cli-security => ../jfrog-cli-security
// Using attiasas/convert_include_targets branch for IncludeTargets feature

// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 dev

Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,6 @@ github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20251211075913-35ebcd308e93 h1:r
github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20251211075913-35ebcd308e93/go.mod h1:7cCaRhXorlbyXZgiW5bplCExFxlnROaG21K12d8inpQ=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251210085744-f8481d179ac5 h1:GYE67ubwl+ZRw3CcXFUi49EwwQp6k+qS8sX0QuHDHO8=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20251210085744-f8481d179ac5/go.mod h1:BMoGi2rG0udCCeaghqlNgiW3fTmT+TNnfTnBoWFYgcg=
github.com/jfrog/jfrog-cli-security v1.24.2 h1:nyI0lNYR8i6yZYeBDsBJnURYsMnFKEmt7QH4vaNxtGM=
github.com/jfrog/jfrog-cli-security v1.24.2/go.mod h1:3FXD5IkKtdQOm9CZk6cR7q0iC6PaGMnjqzZqRcQp2r0=
github.com/jfrog/jfrog-client-go v1.55.1-0.20251217080430-c92b763b7465 h1:Ff3BlNPndrAfa1xFI/ORFzfWTxQxF0buWG61PEJwd3U=
github.com/jfrog/jfrog-client-go v1.55.1-0.20251217080430-c92b763b7465/go.mod h1:WQ5Y+oKYyHFAlCbHN925bWhnShTd2ruxZ6YTpb76fpU=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
Expand Down
10 changes: 8 additions & 2 deletions packagehandlers/commonpackagehandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ func getFixedPackage(impactedPackage string, versionOperator string, suggestedFi
return
}

// Recursively scans the current directory for descriptor files based on the provided list of suffixes, while excluding paths that match the specified exclusion patterns.
// Scans the current directory for descriptor files based on the provided list of suffixes, while excluding paths that match the specified exclusion patterns.
// Only scans the current directory level (non-recursive) to avoid conflicts with auto-detected subdirectory targets that will be processed separately.
// The patternsToExclude must be provided as regexp patterns. For instance, if the pattern ".*node_modules.*" is provided, any paths containing "node_modules" will be excluded from the result.
// Returns a slice of all discovered descriptor files, represented as absolute paths.
// Returns a slice of all discovered descriptor files in the current directory, represented as absolute paths.
func (cph *CommonPackageHandler) GetAllDescriptorFilesFullPaths(descriptorFilesSuffixes []string, patternsToExclude ...string) (descriptorFilesFullPaths []string, err error) {
if len(descriptorFilesSuffixes) == 0 {
return
Expand All @@ -111,6 +112,11 @@ func (cph *CommonPackageHandler) GetAllDescriptorFilesFullPaths(descriptorFilesS
return fmt.Errorf("an error has occurred when attempting to access or traverse the file system: %w", innerErr)
}

// Skip subdirectories
if d.IsDir() && path != "." {
return filepath.SkipDir
}

for _, regexpCompiler := range regexpPatternsCompilers {
if match := regexpCompiler.FindString(path); match != "" {
return filepath.SkipDir
Expand Down
2 changes: 1 addition & 1 deletion packagehandlers/packagehandlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ func TestGetAllDescriptorFilesFullPaths(t *testing.T) {
{
testProjectRepo: "gradle",
suffixesToSearch: []string{groovyDescriptorFileSuffix, kotlinDescriptorFileSuffix},
expectedResultSuffixes: []string{filepath.Join("innerProjectForTest", "build.gradle.kts"), "build.gradle"},
expectedResultSuffixes: []string{"build.gradle"},
},
// This test case verifies that paths containing excluded patterns are omitted from the output
{
Expand Down
62 changes: 50 additions & 12 deletions scanrepository/scanrepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func (cfp *ScanRepositoryCmd) setCommandPrerequisites(repository *utils.Reposito
}

func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) (int, error) {
var fixNeeded bool
var isFixNeeded bool
totalFindings := 0
// A map that contains the full project paths as a keys
// The value is a map of vulnerable package names -> the scanDetails of the vulnerable packages.
Expand Down Expand Up @@ -203,22 +203,40 @@ func (cfp *ScanRepositoryCmd) scanAndFixProject(repository *utils.Repository) (i
if repository.DetectionOnly {
continue
}
// Prepare the vulnerabilities map for each working dir path
currPathVulnerabilities, err := cfp.getVulnerabilitiesMap(scanResults)
if err != nil {
if err = utils.CreateErrorIfPartialResultsDisabled(cfp.scanDetails.AllowPartialResults(), fmt.Sprintf("An error occurred while preparing the vulnerabilities map for '%s' working directory. Fixes will be skipped for this working directory", fullPathWd), err); err != nil {
return totalFindings, err

for _, target := range scanResults.Targets {
target.ScaResults.Descriptors
targetPath := target.Target
convertor := conversion.NewCommandResultsConvertor(conversion.ResultConvertParams{
IncludeVulnerabilities: scanResults.IncludesVulnerabilities(),
HasViolationContext: scanResults.HasViolationContext(),
IncludeTargets: []string{targetPath},
})
simpleJsonResult, err := convertor.ConvertToSimpleJson(scanResults)
if err != nil {
if err = utils.CreateErrorIfPartialResultsDisabled(cfp.scanDetails.AllowPartialResults(), fmt.Sprintf("An error occurred while preparing the vulnerabilities map for '%s' working directory. Fixes will be skipped for this working directory", targetPath), err); err != nil {
return totalFindings, err
}
continue
}
continue
}
if len(currPathVulnerabilities) > 0 {
fixNeeded = true

currPathVulnerabilities, err := cfp.createVulnerabilitiesMapFromSimpleJson(simpleJsonResult)
if err != nil {
if err = utils.CreateErrorIfPartialResultsDisabled(cfp.scanDetails.AllowPartialResults(), fmt.Sprintf("An error occurred while preparing the vulnerabilities map for '%s' working directory. Fixes will be skipped for this working directory", targetPath), err); err != nil {
return totalFindings, err
}
continue
}
if len(currPathVulnerabilities) > 0 {
isFixNeeded = true
log.Debug(fmt.Sprintf("Found %d fixable vulnerabilities in '%s': %s", len(currPathVulnerabilities), targetPath, strings.Join(maps.Keys(currPathVulnerabilities), ", ")))
}
vulnerabilitiesByPathMap[targetPath] = currPathVulnerabilities
}
vulnerabilitiesByPathMap[fullPathWd] = currPathVulnerabilities
}
if repository.DetectionOnly {
log.Info(fmt.Sprintf("This command is running in detection mode only. To enable automatic fixing of issues, set the '%s' environment variable to 'false'.", utils.DetectionOnlyEnv))
} else if fixNeeded {
} else if isFixNeeded {
return totalFindings, cfp.fixVulnerablePackages(repository, vulnerabilitiesByPathMap)
}
return totalFindings, nil
Expand Down Expand Up @@ -598,6 +616,26 @@ func (cfp *ScanRepositoryCmd) createVulnerabilitiesMap(scanResults *results.Secu
return vulnerabilitiesMap, nil
}

func (cfp *ScanRepositoryCmd) createVulnerabilitiesMapFromSimpleJson(simpleJsonResult formats.SimpleJsonResults) (map[string]*utils.VulnerabilityDetails, error) {
vulnerabilitiesMap := map[string]*utils.VulnerabilityDetails{}
var err error

if len(simpleJsonResult.Vulnerabilities) > 0 {
for i := range simpleJsonResult.Vulnerabilities {
if err = cfp.addVulnerabilityToFixVersionsMap(&simpleJsonResult.Vulnerabilities[i], vulnerabilitiesMap); err != nil {
return nil, err
}
}
} else if len(simpleJsonResult.SecurityViolations) > 0 {
for i := range simpleJsonResult.SecurityViolations {
if err = cfp.addVulnerabilityToFixVersionsMap(&simpleJsonResult.SecurityViolations[i], vulnerabilitiesMap); err != nil {
return nil, err
}
}
}
return vulnerabilitiesMap, nil
}

func (cfp *ScanRepositoryCmd) addVulnerabilityToFixVersionsMap(vulnerability *formats.VulnerabilityOrViolationRow, vulnerabilitiesMap map[string]*utils.VulnerabilityDetails) error {
if len(vulnerability.FixedVersions) == 0 {
return nil
Expand Down
68 changes: 68 additions & 0 deletions scanrepository/scanrepository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,74 @@ func TestCreateVulnerabilitiesMap(t *testing.T) {
}
}

func TestMultiTargetVulnerabilitiesMap(t *testing.T) {
cfp := &ScanRepositoryCmd{}

scanResults := &results.SecurityCommandResults{
ResultsMetaData: results.ResultsMetaData{ResultContext: results.ResultContext{IncludeVulnerabilities: true}},
Targets: []*results.TargetResults{
{
ScanTarget: results.ScanTarget{Target: "project1"},
ScaResults: &results.ScaScanResults{
DeprecatedXrayResults: []services.ScanResponse{{
Vulnerabilities: []services.Vulnerability{{
Cves: []services.Cve{{Id: "CVE-1"}},
Severity: "High",
Components: map[string]services.Component{
"pkg1": {
FixedVersions: []string{"1.0.0"},
ImpactPaths: [][]services.ImpactPathNode{{{ComponentId: "root"}, {ComponentId: "pkg1"}}},
},
},
}},
}},
},
},
{
ScanTarget: results.ScanTarget{Target: "project2"},
ScaResults: &results.ScaScanResults{
DeprecatedXrayResults: []services.ScanResponse{{
Vulnerabilities: []services.Vulnerability{{
Cves: []services.Cve{{Id: "CVE-2"}},
Severity: "Critical",
Components: map[string]services.Component{
"pkg2": {
FixedVersions: []string{"2.0.0"},
ImpactPaths: [][]services.ImpactPathNode{{{ComponentId: "root"}, {ComponentId: "pkg2"}}},
},
},
}},
}},
},
},
},
}

convertor1 := conversion.NewCommandResultsConvertor(conversion.ResultConvertParams{
IncludeVulnerabilities: true,
IncludeTargets: []string{"project1"},
})
simpleJson1, err := convertor1.ConvertToSimpleJson(scanResults)
assert.NoError(t, err)
vulnsMap1, err := cfp.createVulnerabilitiesMapFromSimpleJson(simpleJson1)
assert.NoError(t, err)
assert.Len(t, vulnsMap1, 1)
assert.Contains(t, vulnsMap1, "pkg1")
assert.NotContains(t, vulnsMap1, "pkg2")

convertor2 := conversion.NewCommandResultsConvertor(conversion.ResultConvertParams{
IncludeVulnerabilities: true,
IncludeTargets: []string{"project2"},
})
simpleJson2, err := convertor2.ConvertToSimpleJson(scanResults)
assert.NoError(t, err)
vulnsMap2, err := cfp.createVulnerabilitiesMapFromSimpleJson(simpleJson2)
assert.NoError(t, err)
assert.Len(t, vulnsMap2, 1)
assert.Contains(t, vulnsMap2, "pkg2")
assert.NotContains(t, vulnsMap2, "pkg1")
}

// Verifies unsupported packages return specific error
// Other logic is implemented inside each package-handler.
func TestUpdatePackageToFixedVersion(t *testing.T) {
Expand Down
Loading