Skip to content

Commit 6834cec

Browse files
Merge branch 'main' into adar-cntr-custom-images-dir-fix
2 parents f6c56e9 + 0ff253e commit 6834cec

File tree

7 files changed

+352
-4
lines changed

7 files changed

+352
-4
lines changed

CODEOWNERS

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
# Code ownership rules
22

3-
# Only this user can approve changes to the CODEOWNERS file
4-
/CODEOWNERS @cx-anurag-dalke
5-
63
# Each line is a file pattern followed by one or more owners
74

85
# Specify the default owners for the entire repository

internal/commands/scan.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,7 @@ func scanCreateSubCommand(
870870

871871
// reading sbom-only flag
872872
createScanCmd.PersistentFlags().Bool(commonParams.SbomFlag, false, "Scan only the specified SBOM file (supported formats xml or json)")
873+
createScanCmd.PersistentFlags().Bool(commonParams.GitIgnoreFileFilterFlag, false, commonParams.GitIgnoreFileFilterUsage)
873874

874875
return createScanCmd
875876
}
@@ -1808,6 +1809,7 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
18081809

18091810
scaResolverParams, scaResolver := getScaResolverFlags(cmd)
18101811
isSbom, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag)
1812+
isGitIgnoreFilter, _ := cmd.Flags().GetBool(commonParams.GitIgnoreFileFilterFlag)
18111813
var directoryPath string
18121814
if isSbom {
18131815
sbomFile, _ := cmd.Flags().GetString(commonParams.SourcesFlag)
@@ -1824,6 +1826,29 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW
18241826
}
18251827
} else {
18261828
zipFilePath, directoryPath, err = definePathForZipFileOrDirectory(cmd)
1829+
if isGitIgnoreFilter {
1830+
gitIgnoreFilter, err := getGitignorePatterns(directoryPath, zipFilePath)
1831+
if err != nil {
1832+
return "", "", err
1833+
}
1834+
1835+
if len(gitIgnoreFilter) > 0 {
1836+
if sourceDirFilter == "" {
1837+
sourceDirFilter = gitIgnoreFilter[0]
1838+
for i := 1; i < len(gitIgnoreFilter); i++ {
1839+
if !strings.Contains(sourceDirFilter, gitIgnoreFilter[i]) {
1840+
sourceDirFilter += "," + gitIgnoreFilter[i]
1841+
}
1842+
}
1843+
} else {
1844+
for _, pattern := range gitIgnoreFilter {
1845+
if !strings.Contains(sourceDirFilter, pattern) {
1846+
sourceDirFilter += "," + pattern
1847+
}
1848+
}
1849+
}
1850+
}
1851+
}
18271852
}
18281853

18291854
if zipFilePath != "" && scaResolverPath != "" {
@@ -3272,3 +3297,97 @@ func isValidJSONOrXML(path string) (bool, error) {
32723297

32733298
return true, nil
32743299
}
3300+
3301+
func getGitignorePatterns(directoryPath, zipFilePath string) ([]string, error) {
3302+
var data []byte
3303+
var err error
3304+
if directoryPath != "" {
3305+
gitignorePath := filepath.Join(directoryPath, ".gitignore")
3306+
if _, err := os.Stat(gitignorePath); os.IsNotExist(err) {
3307+
return nil, fmt.Errorf(".gitignore file not found in directory: %s", directoryPath)
3308+
}
3309+
data, err = os.ReadFile(gitignorePath)
3310+
if err != nil {
3311+
return nil, err
3312+
}
3313+
}
3314+
if zipFilePath != "" {
3315+
data, err = readGitIgnoreFromZip(zipFilePath)
3316+
if err != nil {
3317+
return nil, err
3318+
}
3319+
}
3320+
lines := strings.Split(string(data), "\n")
3321+
var patterns []string
3322+
for _, line := range lines {
3323+
line = strings.TrimSpace(line)
3324+
3325+
// This condition skips lines that are empty, comments.
3326+
// Excluding the lines that contain negotiation characters like !, which are used to negate patterns
3327+
if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") {
3328+
continue
3329+
}
3330+
// Convert build/** to build for path.Match() supported patterns
3331+
if strings.HasSuffix(line, "/**") {
3332+
line = strings.TrimSuffix(line, "/**")
3333+
}
3334+
// Convert **/temp/ to temp for path.Match() supported patterns
3335+
if strings.HasPrefix(line, "**/") {
3336+
line = strings.TrimPrefix(line, "**/")
3337+
}
3338+
// Convert temp/ to temp for path.Match() supported patterns
3339+
if strings.HasSuffix(line, "/") {
3340+
line = strings.TrimSuffix(line, "/")
3341+
}
3342+
3343+
// Convert LoginController[!0-3].java to LoginController[^0-3].java for path.Match() supported patterns
3344+
if strings.Contains(line, "!") {
3345+
line = strings.ReplaceAll(line, "!", "^")
3346+
}
3347+
patterns = append(patterns, "!"+line)
3348+
}
3349+
return patterns, nil
3350+
}
3351+
3352+
func readGitIgnoreFromZip(zipPath string) ([]byte, error) {
3353+
r, err := zip.OpenReader(zipPath)
3354+
if err != nil {
3355+
return []byte(""), fmt.Errorf("failed to open zip: %s", zipPath)
3356+
}
3357+
defer r.Close()
3358+
3359+
rootFolder := ""
3360+
if len(r.File) > 0 {
3361+
parts := strings.Split(r.File[0].Name, "/")
3362+
if len(parts) > 1 {
3363+
rootFolder = parts[0]
3364+
}
3365+
}
3366+
expectedGitignorePath := rootFolder + "/.gitignore"
3367+
3368+
for _, f := range r.File {
3369+
if f.Name != expectedGitignorePath {
3370+
continue
3371+
}
3372+
rc, err := f.Open()
3373+
if err != nil {
3374+
return []byte(""), fmt.Errorf("failed to open .gitignore inside zip: %w", err)
3375+
}
3376+
3377+
// Read file content
3378+
data, err := io.ReadAll(rc)
3379+
if err != nil {
3380+
err := rc.Close()
3381+
if err != nil {
3382+
return nil, err
3383+
}
3384+
return []byte(""), fmt.Errorf("failed to read .gitignore content inside zip : %w", err)
3385+
}
3386+
// Close with error handling
3387+
if err := rc.Close(); err != nil {
3388+
logger.PrintfIfVerbose("Error closing .gitignore reader: %v", err)
3389+
}
3390+
return data, nil
3391+
}
3392+
return []byte(""), fmt.Errorf(".gitignore not found in zip: %s", zipPath)
3393+
}

internal/commands/scan_test.go

Lines changed: 189 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io"
1010
"log"
1111
"os"
12+
"path/filepath"
1213
"reflect"
1314
"strings"
1415
"testing"
@@ -2810,5 +2811,192 @@ func TestCreateScan_VerifyNoMinimalZipCreation_WithContainerImagesFlag(t *testin
28102811

28112812
// Before the fix, this would have triggered createMinimalZipFile
28122813
// After the fix, it should process directory contents normally
2813-
execCmdNilAssertion(t, baseArgs...)
2814+
execCmdNilAssertion(t, baseArgs...)
2815+
}
2816+
2817+
func TestGetGitignorePatterns_DirPath_GitIgnore_NotFound(t *testing.T) {
2818+
dir := t.TempDir()
2819+
_, err := getGitignorePatterns(dir, "")
2820+
assert.ErrorContains(t, err, ".gitignore file not found in directory")
2821+
}
2822+
2823+
func TestGetGitignorePatterns_DirPath_GitIgnore_PermissionDenied(t *testing.T) {
2824+
dir := t.TempDir()
2825+
err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(""), 0000)
2826+
if err != nil {
2827+
t.Fatalf("Failed to write .gitignore: %v", err)
2828+
}
2829+
_, err = getGitignorePatterns(dir, "")
2830+
assert.ErrorContains(t, err, "permission denied")
2831+
}
2832+
2833+
func TestGetGitignorePatterns_DirPath_GitIgnore_EmptyPatternList(t *testing.T) {
2834+
dir := t.TempDir()
2835+
err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(""), 0644)
2836+
if err != nil {
2837+
t.Fatalf("Failed to write .gitignore: %v", err)
2838+
}
2839+
gitIgnoreFilter, err := getGitignorePatterns(dir, "")
2840+
if err != nil {
2841+
t.Fatalf("Error in fetching pattern from .gitignore file: %v", err)
2842+
}
2843+
assert.Assert(t, len(gitIgnoreFilter) == 0, "Expected no patterns from empty .gitignore file")
2844+
}
2845+
2846+
func TestGetGitignorePatterns_DirPath_GitIgnore_PatternList(t *testing.T) {
2847+
dir := t.TempDir()
2848+
err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(`src
2849+
src/
2850+
**/vullib
2851+
**/admin/
2852+
vulnerability/**
2853+
application-jira.yml
2854+
*.yml
2855+
LoginController[0-1].java
2856+
LoginController[!0-3].java
2857+
LoginController[01].java
2858+
LoginController[!456].java
2859+
?pplication-jira.yml
2860+
a*cation-jira.yml`), 0644)
2861+
if err != nil {
2862+
t.Fatalf("Failed to write .gitignore: %v", err)
2863+
}
2864+
gitIgnoreFilter, err := getGitignorePatterns(dir, "")
2865+
if err != nil {
2866+
t.Fatalf("Error in fetching pattern from .gitignore file: %v", err)
2867+
}
2868+
assert.Assert(t, len(gitIgnoreFilter) > 0, "Expected patterns from .gitignore file")
2869+
}
2870+
2871+
func TestGetGitignorePatterns_ZipPath_GitIgnore_FailedToOpenZipFIle(t *testing.T) {
2872+
dir := t.TempDir()
2873+
zipPath := filepath.Join(dir, "example.zip")
2874+
2875+
// Create the zip file
2876+
zipFile, err := os.Create(zipPath)
2877+
if err != nil {
2878+
t.Fatalf("Failed to create zip file: %v", err)
2879+
}
2880+
defer func(zipFile *os.File) {
2881+
err := zipFile.Close()
2882+
if err != nil {
2883+
t.Fatalf("Failed to close zip file: %v", err)
2884+
}
2885+
}(zipFile)
2886+
_, err = getGitignorePatterns("", zipPath)
2887+
assert.ErrorContains(t, err, "failed to open zip")
2888+
}
2889+
2890+
func TestGetGitignorePatterns_ZipPath_GitIgnore_NotFound(t *testing.T) {
2891+
dir := t.TempDir()
2892+
zipPath := filepath.Join(dir, "example.zip")
2893+
2894+
// Create the zip file
2895+
zipFile, err := os.Create(zipPath)
2896+
if err != nil {
2897+
t.Fatalf("Failed to create zip file: %v", err)
2898+
}
2899+
defer func(zipFile *os.File) {
2900+
err := zipFile.Close()
2901+
if err != nil {
2902+
t.Fatalf("Failed to close zip file: %v", err)
2903+
}
2904+
}(zipFile)
2905+
2906+
// Create a zip writer
2907+
zipWriter := zip.NewWriter(zipFile)
2908+
err = zipWriter.Close()
2909+
if err != nil {
2910+
return
2911+
}
2912+
2913+
_, err = getGitignorePatterns("", zipPath)
2914+
assert.ErrorContains(t, err, ".gitignore not found in zip")
2915+
}
2916+
2917+
func TestGetGitignorePatterns_ZipPath_GitIgnore_EmptyPatternList(t *testing.T) {
2918+
dir := t.TempDir()
2919+
zipPath := filepath.Join(dir, "example.zip")
2920+
2921+
// Create the zip file
2922+
zipFile, err := os.Create(zipPath)
2923+
if err != nil {
2924+
t.Fatalf("Failed to create zip file: %v", err)
2925+
}
2926+
defer func(zipFile *os.File) {
2927+
err := zipFile.Close()
2928+
if err != nil {
2929+
t.Fatalf("Failed to close zip file: %v", err)
2930+
}
2931+
}(zipFile)
2932+
2933+
// Create a zip writer
2934+
zipWriter := zip.NewWriter(zipFile)
2935+
2936+
// Add a file to the zip archive
2937+
fileInZip, err := zipWriter.Create("example" + "/.gitignore")
2938+
if err != nil {
2939+
t.Fatalf("Failed to add file to zip: %v", err)
2940+
}
2941+
2942+
_, err = fileInZip.Write([]byte(""))
2943+
if err != nil {
2944+
t.Fatalf("Failed to write data to zip: %v", err)
2945+
}
2946+
err = zipWriter.Close()
2947+
if err != nil {
2948+
return
2949+
}
2950+
2951+
gitIgnoreFilter, _ := getGitignorePatterns("", zipPath)
2952+
assert.Assert(t, len(gitIgnoreFilter) == 0, "Expected no patterns from empty .gitignore file")
2953+
}
2954+
2955+
func TestGetGitignorePatterns_ZipPath_GitIgnore_PatternList(t *testing.T) {
2956+
dir := t.TempDir()
2957+
zipPath := filepath.Join(dir, "example.zip")
2958+
2959+
// Create the zip file
2960+
zipFile, err := os.Create(zipPath)
2961+
if err != nil {
2962+
t.Fatalf("Failed to create zip file: %v", err)
2963+
}
2964+
defer func(zipFile *os.File) {
2965+
err := zipFile.Close()
2966+
if err != nil {
2967+
t.Fatalf("Failed to close zip file: %v", err)
2968+
}
2969+
}(zipFile)
2970+
2971+
// Create a zip writer
2972+
zipWriter := zip.NewWriter(zipFile)
2973+
2974+
// Add a file to the zip archive
2975+
fileInZip, err := zipWriter.Create("example" + "/.gitignore")
2976+
if err != nil {
2977+
t.Fatalf("Failed to add file to zip: %v", err)
2978+
}
2979+
_, err = fileInZip.Write([]byte(`src
2980+
src/
2981+
**/vullib
2982+
**/admin/
2983+
vulnerability/**
2984+
application-jira.yml
2985+
*.yml
2986+
LoginController[0-1].java
2987+
LoginController[!0-3].java
2988+
LoginController[01].java
2989+
LoginController[!456].java
2990+
?pplication-jira.yml
2991+
a*cation-jira.yml`))
2992+
if err != nil {
2993+
t.Fatalf("Failed to write data to zip: %v", err)
2994+
}
2995+
err = zipWriter.Close()
2996+
if err != nil {
2997+
return
2998+
}
2999+
3000+
gitIgnoreFilter, _ := getGitignorePatterns("", zipPath)
3001+
assert.Assert(t, len(gitIgnoreFilter) > 0, "Expected patterns from .gitignore file")
28143002
}

internal/params/flags.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ const (
169169
LogFileUsage = "Saves logs to the specified file path only"
170170
LogFileConsoleFlag = "log-file-console"
171171
LogFileConsoleUsage = "Saves logs to the specified file path as well as to the console"
172+
GitIgnoreFileFilterFlag = "use-gitignore"
173+
GitIgnoreFileFilterUsage = "Exclude files and directories from the scan based on the patterns defined in the directory's .gitignore file"
172174
// INDIVIDUAL FILTER FLAGS
173175
SastFilterFlag = "sast-filter"
174176
SastFilterUsage = "SAST filter"

internal/wrappers/predicates-http.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,22 @@ func (r ResultsPredicatesHTTPWrapper) PredicateSeverityAndState(predicate *Predi
102102
_ = resp.Body.Close()
103103
}()
104104

105+
// in case of ne/pne when mandatory comment arent provided, cli is not transforming error message
106+
responseMap := make(map[string]interface{})
107+
if err := json.NewDecoder(resp.Body).Decode(&responseMap); err != nil {
108+
logger.PrintIfVerbose(fmt.Sprintf("failed to read the response, %v", err.Error()))
109+
} else {
110+
if val, ok := responseMap["code"].(float64); ok {
111+
if val == 4002 && responseMap["message"] != nil {
112+
if errMsg, ok := responseMap["message"].(string); ok {
113+
if errMsg == "A comment is required to make changes to the result state" {
114+
return nil, errors.Errorf(errMsg)
115+
}
116+
}
117+
}
118+
}
119+
}
120+
105121
switch resp.StatusCode {
106122
case http.StatusBadRequest, http.StatusInternalServerError:
107123
return nil, errors.Errorf("Predicate bad request.")
13 KB
Binary file not shown.

0 commit comments

Comments
 (0)