Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit SCA recursive scan #994

Merged
merged 33 commits into from
Nov 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
f45589f
Start implement recursive scan
attiasas Oct 15, 2023
aa27d55
continue to implement recursive scan
attiasas Oct 16, 2023
c7405ab
fix
attiasas Oct 18, 2023
253e7e5
Merge remote-tracking branch 'upstream/dev' into recursive_audit_scan
attiasas Oct 18, 2023
eed6fd2
implement recursive and descriptors
attiasas Oct 19, 2023
ccb5f11
continue to implement
attiasas Oct 19, 2023
af1cd57
Merge remote-tracking branch 'upstream/dev' into recursive_audit_scan
attiasas Oct 19, 2023
9d4a8a2
done implementation
attiasas Oct 22, 2023
f8795ad
Merge remote-tracking branch 'upstream/dev' into recursive_audit_scan
attiasas Oct 22, 2023
d463fa1
fix static
attiasas Oct 22, 2023
c98d5dc
Merge remote-tracking branch 'upstream/dev' into recursive_audit_scan
attiasas Oct 22, 2023
d284a15
fix test
attiasas Oct 22, 2023
c13dadf
clean up
attiasas Oct 22, 2023
d97299e
add parameters
attiasas Oct 22, 2023
4d71df5
add new params
attiasas Oct 22, 2023
9c66599
format
attiasas Oct 22, 2023
ee1bd2a
fix requested descriptor indicator
attiasas Oct 22, 2023
0f177ec
Merge remote-tracking branch 'upstream/dev' into recursive_audit_scan
attiasas Oct 22, 2023
15992e3
add tests
attiasas Oct 24, 2023
09b8e91
format
attiasas Oct 24, 2023
8ca6ad7
use new on other scan results
attiasas Oct 24, 2023
0c5fa70
remove recursive flag
attiasas Oct 24, 2023
989332a
fix ismultipleProject
attiasas Oct 24, 2023
1772407
use relative filter
attiasas Oct 25, 2023
3a02b25
review changes
attiasas Nov 1, 2023
8cf8e95
Merge remote-tracking branch 'upstream/dev' into recursive_audit_scan
attiasas Nov 1, 2023
242e1bb
fix static
attiasas Nov 1, 2023
c7e983b
fix gosec
attiasas Nov 1, 2023
75b8bf2
update client go to dev
attiasas Nov 1, 2023
273bbee
review changes add tests
attiasas Nov 2, 2023
9090909
cleanup
attiasas Nov 2, 2023
d1a100a
review changes
attiasas Nov 2, 2023
f1246d9
fix tests
attiasas Nov 2, 2023
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
2 changes: 1 addition & 1 deletion artifactory/commands/buildinfo/adddependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ func collectPatternMatchingFiles(addDepsParams *specutils.CommonParams, rootPath
return nil, err
}

paths, err := fspatterns.ListFiles(rootPath, addDepsParams.IsRecursive(), addDepsParams.IsIncludeDirs(), true, excludePathPattern)
paths, err := fspatterns.ListFiles(rootPath, addDepsParams.IsRecursive(), addDepsParams.IsIncludeDirs(), false, true, excludePathPattern)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,6 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
)

// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20231003120621-90e9d7ea05e9
replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20231101142932-422f20520a28

replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20231031143744-13f94ab07bbc
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,8 @@ github.com/jfrog/gofrog v1.3.1 h1:QqAwQXCVReT724uga1AYqG/ZyrNQ6f+iTxmzkb+YFQk=
github.com/jfrog/gofrog v1.3.1/go.mod h1:IFMc+V/yf7rA5WZ74CSbXe+Lgf0iApEQLxRZVzKRUR0=
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
github.com/jfrog/jfrog-client-go v1.34.3 h1:kDfw3FUQQvOsTKFqonIgLlziez6CSX80xCYZIH9YYcg=
github.com/jfrog/jfrog-client-go v1.34.3/go.mod h1:fuxhYzWEkA16+ZV5cP/BJUGjA3SXVKbBoDmb8ZS6J4g=
github.com/jfrog/jfrog-client-go v1.28.1-0.20231101142932-422f20520a28 h1:CeuORbXaa9E+jDTT/DX1Ozuo8HGzDO7B8PIs0O35MNo=
github.com/jfrog/jfrog-client-go v1.28.1-0.20231101142932-422f20520a28/go.mod h1:fuxhYzWEkA16+ZV5cP/BJUGjA3SXVKbBoDmb8ZS6J4g=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down
219 changes: 212 additions & 7 deletions utils/coreutils/techutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ package coreutils

import (
"fmt"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"os"
"path/filepath"
"strings"

"github.com/jfrog/gofrog/datastructures"
"github.com/jfrog/jfrog-client-go/artifactory/services/fspatterns"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"

"golang.org/x/exp/maps"
"golang.org/x/text/cases"
"golang.org/x/text/language"

Expand Down Expand Up @@ -110,22 +115,25 @@ var technologiesData = map[Technology]TechData{
Poetry: {
packageType: Pypi,
indicators: []string{"pyproject.toml", "poetry.lock"},
packageDescriptors: []string{"pyproject.toml"},
packageInstallationCommand: "add",
packageVersionOperator: "==",
applicabilityScannable: true,
},
Nuget: {
indicators: []string{".sln", ".csproj"},
formal: "NuGet",
indicators: []string{".sln", ".csproj"},
packageDescriptors: []string{".sln", ".csproj"},
formal: "NuGet",
// .NET CLI is used for NuGet projects
execCommand: "dotnet",
packageInstallationCommand: "add",
// packageName -v packageVersion
packageVersionOperator: " -v ",
},
Dotnet: {
indicators: []string{".sln", ".csproj"},
formal: ".NET",
indicators: []string{".sln", ".csproj"},
packageDescriptors: []string{".sln", ".csproj"},
formal: ".NET",
},
}

Expand Down Expand Up @@ -179,7 +187,11 @@ func DetectedTechnologiesList() (technologies []string) {
if errorutils.CheckError(err) != nil {
return
}
detectedTechnologies, err := DetectTechnologies(wd, false, false)
return detectedTechnologiesListInPath(wd, false)
}

func detectedTechnologiesListInPath(path string, recursive bool) (technologies []string) {
detectedTechnologies, err := DetectTechnologies(path, false, recursive)
if err != nil {
return
}
Expand All @@ -191,6 +203,198 @@ func DetectedTechnologiesList() (technologies []string) {
return techStringsList
}

// If recursive is true, the search will not be limited to files in the root path.
// If requestedTechs is empty, all technologies will be checked.
// If excludePathPattern is not empty, files/directories that match the wildcard pattern will be excluded from the search.
func DetectTechnologiesDescriptors(path string, recursive bool, requestedTechs []string, requestedDescriptors map[Technology][]string, excludePathPattern string) (technologiesDetected map[Technology]map[string][]string, err error) {
filesList, err := fspatterns.ListFiles(path, recursive, false, true, true, excludePathPattern)
if err != nil {
return
}
workingDirectoryToIndicators, excludedTechAtWorkingDir := mapFilesToRelevantWorkingDirectories(filesList, requestedDescriptors)
var strJson string
if strJson, err = GetJsonIndent(workingDirectoryToIndicators); err != nil {
return
} else if len(workingDirectoryToIndicators) > 0 {
log.Debug(fmt.Sprintf("mapped %d working directories with indicators/descriptors:\n%s", len(workingDirectoryToIndicators), strJson))
}
technologiesDetected = mapWorkingDirectoriesToTechnologies(workingDirectoryToIndicators, excludedTechAtWorkingDir, ToTechnologies(requestedTechs), requestedDescriptors)
if len(technologiesDetected) > 0 {
log.Debug(fmt.Sprintf("Detected %d technologies at %s: %s.", len(technologiesDetected), path, maps.Keys(technologiesDetected)))
}
return
}

// Map files to relevant working directories according to the technologies' indicators/descriptors and requested descriptors.
attiasas marked this conversation as resolved.
Show resolved Hide resolved
// files: The file paths to map.
// requestedDescriptors: Special requested descriptors (for example in Pip requirement.txt can have different path) for each technology.
// Returns:
// 1. workingDirectoryToIndicators: A map of working directories to the files that are relevant to the technologies.
// wd1: [wd1/indicator, wd1/descriptor]
// wd/wd2: [wd/wd2/indicator]
// 2. excludedTechAtWorkingDir: A map of working directories to the technologies that are excluded from the working directory.
// wd1: [tech1, tech2]
// wd/wd2: [tech1]
func mapFilesToRelevantWorkingDirectories(files []string, requestedDescriptors map[Technology][]string) (workingDirectoryToIndicators map[string][]string, excludedTechAtWorkingDir map[string][]Technology) {
workingDirectoryToIndicatorsSet := make(map[string]*datastructures.Set[string])
excludedTechAtWorkingDir = make(map[string][]Technology)
for _, path := range files {
directory := filepath.Dir(path)

for tech, techData := range technologiesData {
// Check if the working directory contains indicators/descriptors for the technology
relevant := isIndicator(path, techData) || isDescriptor(path, techData) || isRequestedDescriptor(path, requestedDescriptors[tech])
if relevant {
if _, exist := workingDirectoryToIndicatorsSet[directory]; !exist {
workingDirectoryToIndicatorsSet[directory] = datastructures.MakeSet[string]()
}
workingDirectoryToIndicatorsSet[directory].Add(path)
}
// Check if the working directory contains a file/directory with a name that ends with an excluded suffix
if isExclude(path, techData) {
excludedTechAtWorkingDir[directory] = append(excludedTechAtWorkingDir[directory], tech)
}
}
}
workingDirectoryToIndicators = make(map[string][]string)
for wd, indicators := range workingDirectoryToIndicatorsSet {
workingDirectoryToIndicators[wd] = indicators.ToSlice()
}
return
}

func isDescriptor(path string, techData TechData) bool {
for _, descriptor := range techData.packageDescriptors {
if strings.HasSuffix(path, descriptor) {
return true
}
}
return false
}

func isRequestedDescriptor(path string, requestedDescriptors []string) bool {
for _, requestedDescriptor := range requestedDescriptors {
if strings.HasSuffix(path, requestedDescriptor) {
return true
}
}
return false
}

func isIndicator(path string, techData TechData) bool {
for _, indicator := range techData.indicators {
if strings.HasSuffix(path, indicator) {
return true
}
}
return false
}

func isExclude(path string, techData TechData) bool {
for _, exclude := range techData.exclude {
if strings.HasSuffix(path, exclude) {
return true
}
}
return false
}

// Map working directories to technologies according to the given workingDirectoryToIndicators map files.
// workingDirectoryToIndicators: A map of working directories to the files inside the directory that are relevant to the technologies.
// excludedTechAtWorkingDir: A map of working directories to the technologies that are excluded from the working directory.
// requestedTechs: The technologies to check, if empty all technologies will be checked.
// requestedDescriptors: Special requested descriptors (for example in Pip requirement.txt can have different path) for each technology to detect.
func mapWorkingDirectoriesToTechnologies(workingDirectoryToIndicators map[string][]string, excludedTechAtWorkingDir map[string][]Technology, requestedTechs []Technology, requestedDescriptors map[Technology][]string) (technologiesDetected map[Technology]map[string][]string) {
// Get the relevant technologies to check
technologies := requestedTechs
if len(technologies) == 0 {
technologies = GetAllTechnologiesList()
}
technologiesDetected = make(map[Technology]map[string][]string)
// Map working directories to technologies
for _, tech := range technologies {
techWorkingDirs := getTechInformationFromWorkingDir(tech, workingDirectoryToIndicators, excludedTechAtWorkingDir, requestedDescriptors)
if len(techWorkingDirs) > 0 {
// Found indicators of the technology, add to detected.
technologiesDetected[tech] = techWorkingDirs
}
}
for _, tech := range requestedTechs {
if _, exist := technologiesDetected[tech]; !exist {
// Requested (forced with flag) technology and not found any indicators/descriptors in detection, add as detected.
log.Warn(fmt.Sprintf("Requested technology %s but not found any indicators/descriptors in detection.", tech))
technologiesDetected[tech] = map[string][]string{}
}
}
return
}

func getTechInformationFromWorkingDir(tech Technology, workingDirectoryToIndicators map[string][]string, excludedTechAtWorkingDir map[string][]Technology, requestedDescriptors map[Technology][]string) (techWorkingDirs map[string][]string) {
attiasas marked this conversation as resolved.
Show resolved Hide resolved
techWorkingDirs = make(map[string][]string)
for wd, indicators := range workingDirectoryToIndicators {
descriptorsAtWd := []string{}
foundIndicator := false
if isTechExcludedInWorkingDir(tech, wd, excludedTechAtWorkingDir) {
// Exclude this technology from this working directory
continue
}
// Check if the working directory contains indicators/descriptors for the technology
for _, path := range indicators {
if isDescriptor(path, technologiesData[tech]) || isRequestedDescriptor(path, requestedDescriptors[tech]) {
descriptorsAtWd = append(descriptorsAtWd, path)
}
if isIndicator(path, technologiesData[tech]) || isRequestedDescriptor(path, requestedDescriptors[tech]) {
foundIndicator = true
}
}
if foundIndicator {
// Found indicators of the technology in the current working directory, add to detected.
techWorkingDirs[wd] = descriptorsAtWd
}
}
// Don't allow working directory if sub directory already exists as key for the same technology
techWorkingDirs = cleanSubDirectories(techWorkingDirs)
return
}

func isTechExcludedInWorkingDir(tech Technology, wd string, excludedTechAtWorkingDir map[string][]Technology) bool {
if excludedTechs, exist := excludedTechAtWorkingDir[wd]; exist {
for _, excludedTech := range excludedTechs {
if excludedTech == tech {
return true
}
}
}
return false
}

// Remove sub directories keys from the given workingDirectoryToFiles map.
// Keys: [dir/dir, dir/directory] -> [dir/dir, dir/directory]
// Keys: [dir, directory] -> [dir, directory]
// Keys: [dir/dir2, dir/dir2/dir3, dir/dir2/dir3/dir4] -> [dir/dir2]
// Values of removed sub directories will be added to the root directory.
func cleanSubDirectories(workingDirectoryToFiles map[string][]string) (result map[string][]string) {
attiasas marked this conversation as resolved.
Show resolved Hide resolved
result = make(map[string][]string)
for wd, files := range workingDirectoryToFiles {
root := getExistingRootDir(wd, workingDirectoryToFiles)
result[root] = append(result[root], files...)
}
return
}

// Get the root directory of the given path according to the given workingDirectoryToIndicators map.
func getExistingRootDir(path string, workingDirectoryToIndicators map[string][]string) (root string) {
attiasas marked this conversation as resolved.
Show resolved Hide resolved
root = path
for wd := range workingDirectoryToIndicators {
parentWd := filepath.Dir(wd)
parentRoot := filepath.Dir(root)
if parentRoot != parentWd && strings.HasPrefix(root, wd) {
root = wd
}
}
return
}

// DetectTechnologies tries to detect all technologies types according to the files in the given path.
// 'isCiSetup' will limit the search of possible techs to Maven, Gradle, and npm.
// 'recursive' will determine if the search will be limited to files in the root path or not.
Expand All @@ -205,6 +409,7 @@ func DetectTechnologies(path string, isCiSetup, recursive bool) (map[Technology]
if err != nil {
return nil, err
}
log.Info(fmt.Sprintf("Scanning %d file(s):%s", len(filesList), filesList))
detectedTechnologies := detectTechnologiesByFilePaths(filesList, isCiSetup)
return detectedTechnologies, nil
}
Expand Down
Loading
Loading