From a4d1c04c04b27f08717dfd2649ec012cf5e6e8fd Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Mon, 4 Aug 2025 09:30:35 +0530 Subject: [PATCH 1/5] Added code for .gitignore file filter --- internal/commands/scan.go | 111 +++++++++++++++ internal/commands/scan_test.go | 253 +++++++++++++++++++++++++++++++++ internal/params/flags.go | 2 + test/integration/scan_test.go | 13 ++ 4 files changed, 379 insertions(+) diff --git a/internal/commands/scan.go b/internal/commands/scan.go index 144c3d169..bdbad6c71 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -870,6 +870,7 @@ func scanCreateSubCommand( // reading sbom-only flag createScanCmd.PersistentFlags().Bool(commonParams.SbomFlag, false, "Scan only the specified SBOM file (supported formats xml or json)") + createScanCmd.PersistentFlags().Bool(commonParams.GitIgnoreFileFilterFlag, false, commonParams.GitIgnoreFileFilterUsage) return createScanCmd } @@ -1747,6 +1748,7 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW scaResolverParams, scaResolver := getScaResolverFlags(cmd) isSbom, _ := cmd.PersistentFlags().GetBool(commonParams.SbomFlag) + isGitIgnoreFilter, _ := cmd.Flags().GetBool(commonParams.GitIgnoreFileFilterFlag) var directoryPath string if isSbom { sbomFile, _ := cmd.Flags().GetString(commonParams.SourcesFlag) @@ -1763,6 +1765,29 @@ func getUploadURLFromSource(cmd *cobra.Command, uploadsWrapper wrappers.UploadsW } } else { zipFilePath, directoryPath, err = definePathForZipFileOrDirectory(cmd) + if isGitIgnoreFilter { + gitIgnoreFilter, err := getGitignorePatterns(directoryPath, zipFilePath) + if err != nil { + return "", "", err + } + + if len(gitIgnoreFilter) > 0 { + if sourceDirFilter == "" { + sourceDirFilter = gitIgnoreFilter[0] + for i := 1; i < len(gitIgnoreFilter); i++ { + if !strings.Contains(sourceDirFilter, gitIgnoreFilter[i]) { + sourceDirFilter += "," + gitIgnoreFilter[i] + } + } + } else { + for _, pattern := range gitIgnoreFilter { + if !strings.Contains(sourceDirFilter, pattern) { + sourceDirFilter += "," + pattern + } + } + } + } + } } if zipFilePath != "" && scaResolverPath != "" { @@ -3249,3 +3274,89 @@ func isValidJSONOrXML(path string) (bool, error) { return true, nil } + +func getGitignorePatterns(directoryPath, zipFilePath string) ([]string, error) { + var data []byte + var err error + if directoryPath != "" { + gitignorePath := filepath.Join(directoryPath, ".gitignore") + if _, err := os.Stat(gitignorePath); os.IsNotExist(err) { + return nil, fmt.Errorf(".gitignore file not found in directory: %s", directoryPath) + } + data, err = os.ReadFile(gitignorePath) + if err != nil { + return nil, err + } + } + if zipFilePath != "" { + data, err = readGitIgnoreFromZip(zipFilePath) + if err != nil { + return nil, err + } + } + lines := strings.Split(string(data), "\n") + var patterns []string + for _, line := range lines { + line = strings.TrimSpace(line) + + //This condition skips lines that are empty, comments. + // Excluding the lines that contain negotiation characters like !, which are used to negate patterns + if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") { + continue + } + // Convert build/** to build for path.Match() supported patterns + if strings.HasSuffix(line, "/**") { + line = strings.TrimSuffix(line, "/**") + } + // Convert **/temp/ to temp for path.Match() supported patterns + if strings.HasPrefix(line, "**/") { + line = strings.TrimPrefix(line, "**/") + } + // Convert temp/ to temp for path.Match() supported patterns + if strings.HasSuffix(line, "/") { + line = strings.TrimSuffix(line, "/") + } + + // Convert LoginController[!0-3].java to LoginController[^0-3].java for path.Match() supported patterns + if strings.Contains(line, "!") { + line = strings.ReplaceAll(line, "!", "^") + } + patterns = append(patterns, "!"+line) + } + return patterns, nil +} + +func readGitIgnoreFromZip(zipPath string) ([]byte, error) { + r, err := zip.OpenReader(zipPath) + if err != nil { + return []byte(""), fmt.Errorf("failed to open zip: %s", zipPath) + } + defer r.Close() + + rootFolder := "" + if len(r.File) > 0 { + parts := strings.Split(r.File[0].Name, "/") + if len(parts) > 1 { + rootFolder = parts[0] + } + } + expectedGitignorePath := rootFolder + "/.gitignore" + + for _, f := range r.File { + if f.Name == expectedGitignorePath { + rc, err := f.Open() + if err != nil { + return []byte(""), fmt.Errorf("failed to open .gitignore inside zip: %w", err) + } + defer rc.Close() + + // Read file content + data, err := io.ReadAll(rc) + if err != nil { + return []byte(""), fmt.Errorf("failed to read .gitignore content inside zip : %w", err) + } + return data, nil + } + } + return []byte(""), fmt.Errorf(".gitignore not found in zip: %s", zipPath) +} diff --git a/internal/commands/scan_test.go b/internal/commands/scan_test.go index 6402e796c..1c3e8fc70 100644 --- a/internal/commands/scan_test.go +++ b/internal/commands/scan_test.go @@ -9,6 +9,7 @@ import ( "io" "log" "os" + "path/filepath" "reflect" "strings" "testing" @@ -2544,3 +2545,255 @@ func Test_CreateScanWithSbomFlag(t *testing.T) { assert.ErrorContains(t, err, "Failed creating a scan: Input in bad format: failed to read file:") } + +func TestGetGitignorePatterns_DirPath_GitIgnore_NotFound(t *testing.T) { + dir := t.TempDir() + _, err := getGitignorePatterns(dir, "") + assert.ErrorContains(t, err, ".gitignore file not found in directory") +} + +func TestGetGitignorePatterns_DirPath_GitIgnore_PermissionDenied(t *testing.T) { + dir := t.TempDir() + err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(""), 0000) + if err != nil { + t.Fatalf("Failed to write .gitignore: %v", err) + } + _, err = getGitignorePatterns(dir, "") + assert.ErrorContains(t, err, "permission denied") +} + +func TestGetGitignorePatterns_DirPath_GitIgnore_EmptyPatternList(t *testing.T) { + dir := t.TempDir() + err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(""), 0644) + if err != nil { + t.Fatalf("Failed to write .gitignore: %v", err) + } + gitIgnoreFilter, err := getGitignorePatterns(dir, "") + if err != nil { + t.Fatalf("Error in fetching pattern from .gitignore file: %v", err) + } + assert.Assert(t, len(gitIgnoreFilter) == 0, "Expected no patterns from empty .gitignore file") +} + +func TestGetGitignorePatterns_DirPath_GitIgnore_PatternList(t *testing.T) { + dir := t.TempDir() + gitignoreContent := `src +src/ +**/vullib +**/admin/ +vulnerability/** +application-jira.yml +*.yml +LoginController[0-1].java +LoginController[!0-3].java +LoginController[01].java +LoginController[!456].java +?pplication-jira.yml +a*cation-jira.yml` + err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(gitignoreContent), 0644) + if err != nil { + t.Fatalf("Failed to write .gitignore: %v", err) + } + gitIgnoreFilter, err := getGitignorePatterns(dir, "") + if err != nil { + t.Fatalf("Error in fetching pattern from .gitignore file: %v", err) + } + assert.Assert(t, len(gitIgnoreFilter) > 0, "Expected patterns from .gitignore file") +} + +func TestGetGitignorePatterns_ZipPath_GitIgnore_FailedToOpenZipFIle(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "example.zip") + + // Create the zip file + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + defer func(zipFile *os.File) { + err := zipFile.Close() + if err != nil { + t.Fatalf("Failed to close zip file: %v", err) + } + }(zipFile) + _, err = getGitignorePatterns("", zipPath) + assert.ErrorContains(t, err, "failed to open zip") +} + +func TestGetGitignorePatterns_ZipPath_GitIgnore_NotFound(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "example.zip") + + // Create the zip file + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + defer func(zipFile *os.File) { + err := zipFile.Close() + if err != nil { + t.Fatalf("Failed to close zip file: %v", err) + } + }(zipFile) + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + err = zipWriter.Close() + if err != nil { + return + } + + _, err = getGitignorePatterns("", zipPath) + assert.ErrorContains(t, err, ".gitignore not found in zip") +} + +func TestGetGitignorePatterns_ZipPath_GitIgnore_EmptyPatternList(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "example.zip") + + // Create the zip file + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + defer func(zipFile *os.File) { + err := zipFile.Close() + if err != nil { + t.Fatalf("Failed to close zip file: %v", err) + } + }(zipFile) + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + + // Add a file to the zip archive + fileInZip, err := zipWriter.Create("example" + "/.gitignore") + if err != nil { + t.Fatalf("Failed to add file to zip: %v", err) + } + + _, err = fileInZip.Write([]byte("")) + if err != nil { + t.Fatalf("Failed to write data to zip: %v", err) + } + err = zipWriter.Close() + if err != nil { + return + } + + gitIgnoreFilter, err := getGitignorePatterns("", zipPath) + assert.Assert(t, len(gitIgnoreFilter) == 0, "Expected no patterns from empty .gitignore file") + +} + +func TestGetGitignorePatterns_ZipPath_GitIgnore_PatternList(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "example.zip") + + // Create the zip file + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + defer func(zipFile *os.File) { + err := zipFile.Close() + if err != nil { + t.Fatalf("Failed to close zip file: %v", err) + } + }(zipFile) + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + + // Add a file to the zip archive + fileInZip, err := zipWriter.Create("example" + "/.gitignore") + if err != nil { + t.Fatalf("Failed to add file to zip: %v", err) + } + + gitignoreContent := `src +src/ +**/vullib +**/admin/ +vulnerability/** +application-jira.yml +*.yml +LoginController[0-1].java +LoginController[!0-3].java +LoginController[01].java +LoginController[!456].java +?pplication-jira.yml +a*cation-jira.yml` + _, err = fileInZip.Write([]byte(gitignoreContent)) + if err != nil { + t.Fatalf("Failed to write data to zip: %v", err) + } + err = zipWriter.Close() + if err != nil { + return + } + + gitIgnoreFilter, err := getGitignorePatterns("", zipPath) + assert.Assert(t, len(gitIgnoreFilter) > 0, "Expected patterns from .gitignore file") + +} + +func TestGetGitignorePatterns_ZipPath_GitIgnore_PermissionDenied(t *testing.T) { + dir := t.TempDir() + zipPath := filepath.Join(dir, "example.zip") + + // Create the zip file + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("Failed to create zip file: %v", err) + } + defer func(zipFile *os.File) { + err := zipFile.Close() + if err != nil { + t.Fatalf("Failed to close zip file: %v", err) + } + }(zipFile) + + // Create a zip writer + zipWriter := zip.NewWriter(zipFile) + + // Set up a header with 0000 permission for .gitignore + header := &zip.FileHeader{ + Name: "example/.gitignore", + Method: zip.Deflate, + } + header.SetMode(0) // UNIX permission 0000 — no access for anyone + + // Add a file to the zip archive + fileInZip, err := zipWriter.CreateHeader(header) + if err != nil { + t.Fatalf("Failed to add file to zip: %v", err) + } + + gitignoreContent := `src +src/ +**/vullib +**/admin/ +vulnerability/** +application-jira.yml +*.yml +LoginController[0-1].java +LoginController[!0-3].java +LoginController[01].java +LoginController[!456].java +?pplication-jira.yml +a*cation-jira.yml` + + // Write content to the file in the zip archive + _, err = fileInZip.Write([]byte(gitignoreContent)) + if err != nil { + t.Fatalf("Failed to write data to zip: %v", err) + } + err = zipWriter.Close() + if err != nil { + return + } + + _, err = getGitignorePatterns("", zipPath) + assert.ErrorContains(t, err, "permission denied") +} diff --git a/internal/params/flags.go b/internal/params/flags.go index 1800cde07..b2d0ef7fa 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -169,6 +169,8 @@ const ( LogFileUsage = "Saves logs to the specified file path only" LogFileConsoleFlag = "log-file-console" LogFileConsoleUsage = "Saves logs to the specified file path as well as to the console" + GitIgnoreFileFilterFlag = "gitignore-file-filter" + GitIgnoreFileFilterUsage = "Exclude files and directories listed in the .gitignore file" // INDIVIDUAL FILTER FLAGS SastFilterFlag = "sast-filter" SastFilterUsage = "SAST filter" diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index 95d20ec41..21045ccfe 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -2544,3 +2544,16 @@ func TestCreateScan_SbomScanForNotExistingFile(t *testing.T) { assert.ErrorContains(t, err, "Failed creating a scan: Input in bad format: failed to read file:") } + +func TestCreateScanFilterGitIgnoreFile(t *testing.T) { + args := []string{ + "scan", "create", + flag(params.ProjectName), getProjectNameForScanTests(), + flag(params.BranchFlag), "dummy_branch", + flag(params.SourcesFlag), "data/insecure.zip", + flag(params.GitIgnoreFileFilterFlag), + } + + err, _ := executeCommand(t, args...) + assert.ErrorContains(t, err, ".gitignore not found in zip") +} From 7e88a2816318a290a5f75ba466d6a815d4344499 Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Mon, 4 Aug 2025 09:54:17 +0530 Subject: [PATCH 2/5] Lint issue --- internal/commands/scan.go | 30 +++++++++++++++++++----------- internal/commands/scan_test.go | 6 ++---- internal/params/flags.go | 4 ++-- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/internal/commands/scan.go b/internal/commands/scan.go index bdbad6c71..525c6db6a 100644 --- a/internal/commands/scan.go +++ b/internal/commands/scan.go @@ -3299,7 +3299,7 @@ func getGitignorePatterns(directoryPath, zipFilePath string) ([]string, error) { for _, line := range lines { line = strings.TrimSpace(line) - //This condition skips lines that are empty, comments. + // This condition skips lines that are empty, comments. // Excluding the lines that contain negotiation characters like !, which are used to negate patterns if line == "" || strings.HasPrefix(line, "#") || strings.HasPrefix(line, "!") { continue @@ -3343,20 +3343,28 @@ func readGitIgnoreFromZip(zipPath string) ([]byte, error) { expectedGitignorePath := rootFolder + "/.gitignore" for _, f := range r.File { - if f.Name == expectedGitignorePath { - rc, err := f.Open() - if err != nil { - return []byte(""), fmt.Errorf("failed to open .gitignore inside zip: %w", err) - } - defer rc.Close() + if f.Name != expectedGitignorePath { + continue + } + rc, err := f.Open() + if err != nil { + return []byte(""), fmt.Errorf("failed to open .gitignore inside zip: %w", err) + } - // Read file content - data, err := io.ReadAll(rc) + // Read file content + data, err := io.ReadAll(rc) + if err != nil { + err := rc.Close() if err != nil { - return []byte(""), fmt.Errorf("failed to read .gitignore content inside zip : %w", err) + return nil, err } - return data, nil + return []byte(""), fmt.Errorf("failed to read .gitignore content inside zip : %w", err) + } + // Close with error handling + if err := rc.Close(); err != nil { + logger.PrintfIfVerbose("Error closing .gitignore reader: %v", err) } + return data, nil } return []byte(""), fmt.Errorf(".gitignore not found in zip: %s", zipPath) } diff --git a/internal/commands/scan_test.go b/internal/commands/scan_test.go index 1c3e8fc70..42e838da0 100644 --- a/internal/commands/scan_test.go +++ b/internal/commands/scan_test.go @@ -2681,9 +2681,8 @@ func TestGetGitignorePatterns_ZipPath_GitIgnore_EmptyPatternList(t *testing.T) { return } - gitIgnoreFilter, err := getGitignorePatterns("", zipPath) + gitIgnoreFilter, _ := getGitignorePatterns("", zipPath) assert.Assert(t, len(gitIgnoreFilter) == 0, "Expected no patterns from empty .gitignore file") - } func TestGetGitignorePatterns_ZipPath_GitIgnore_PatternList(t *testing.T) { @@ -2733,9 +2732,8 @@ a*cation-jira.yml` return } - gitIgnoreFilter, err := getGitignorePatterns("", zipPath) + gitIgnoreFilter, _ := getGitignorePatterns("", zipPath) assert.Assert(t, len(gitIgnoreFilter) > 0, "Expected patterns from .gitignore file") - } func TestGetGitignorePatterns_ZipPath_GitIgnore_PermissionDenied(t *testing.T) { diff --git a/internal/params/flags.go b/internal/params/flags.go index b2d0ef7fa..a9040efa5 100644 --- a/internal/params/flags.go +++ b/internal/params/flags.go @@ -169,8 +169,8 @@ const ( LogFileUsage = "Saves logs to the specified file path only" LogFileConsoleFlag = "log-file-console" LogFileConsoleUsage = "Saves logs to the specified file path as well as to the console" - GitIgnoreFileFilterFlag = "gitignore-file-filter" - GitIgnoreFileFilterUsage = "Exclude files and directories listed in the .gitignore file" + GitIgnoreFileFilterFlag = "use-gitignore" + GitIgnoreFileFilterUsage = "Exclude files and directories from the scan based on the patterns defined in the directory's .gitignore file" // INDIVIDUAL FILTER FLAGS SastFilterFlag = "sast-filter" SastFilterUsage = "SAST filter" From ae8e0da884ff2e9e2bffa730f33753e5c3c19305 Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Mon, 4 Aug 2025 10:15:46 +0530 Subject: [PATCH 3/5] Fix the lint issue --- internal/commands/scan_test.go | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/internal/commands/scan_test.go b/internal/commands/scan_test.go index 42e838da0..c4a896112 100644 --- a/internal/commands/scan_test.go +++ b/internal/commands/scan_test.go @@ -2577,7 +2577,7 @@ func TestGetGitignorePatterns_DirPath_GitIgnore_EmptyPatternList(t *testing.T) { func TestGetGitignorePatterns_DirPath_GitIgnore_PatternList(t *testing.T) { dir := t.TempDir() - gitignoreContent := `src + err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(`src src/ **/vullib **/admin/ @@ -2589,8 +2589,7 @@ LoginController[!0-3].java LoginController[01].java LoginController[!456].java ?pplication-jira.yml -a*cation-jira.yml` - err := os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(gitignoreContent), 0644) +a*cation-jira.yml`), 0644) if err != nil { t.Fatalf("Failed to write .gitignore: %v", err) } @@ -2709,8 +2708,7 @@ func TestGetGitignorePatterns_ZipPath_GitIgnore_PatternList(t *testing.T) { if err != nil { t.Fatalf("Failed to add file to zip: %v", err) } - - gitignoreContent := `src + _, err = fileInZip.Write([]byte(`src src/ **/vullib **/admin/ @@ -2722,8 +2720,7 @@ LoginController[!0-3].java LoginController[01].java LoginController[!456].java ?pplication-jira.yml -a*cation-jira.yml` - _, err = fileInZip.Write([]byte(gitignoreContent)) +a*cation-jira.yml`)) if err != nil { t.Fatalf("Failed to write data to zip: %v", err) } @@ -2768,7 +2765,8 @@ func TestGetGitignorePatterns_ZipPath_GitIgnore_PermissionDenied(t *testing.T) { t.Fatalf("Failed to add file to zip: %v", err) } - gitignoreContent := `src + // Write content to the file in the zip archive + _, err = fileInZip.Write([]byte(`src src/ **/vullib **/admin/ @@ -2780,10 +2778,7 @@ LoginController[!0-3].java LoginController[01].java LoginController[!456].java ?pplication-jira.yml -a*cation-jira.yml` - - // Write content to the file in the zip archive - _, err = fileInZip.Write([]byte(gitignoreContent)) +a*cation-jira.yml`)) if err != nil { t.Fatalf("Failed to write data to zip: %v", err) } From 95b2ef80cf092c0bf8049479b8f3483a31f173c5 Mon Sep 17 00:00:00 2001 From: Sumit Morchhale Date: Mon, 4 Aug 2025 12:39:37 +0530 Subject: [PATCH 4/5] Added one more integration test --- internal/commands/scan_test.go | 58 -------------------- test/integration/data/sources-gitignore.zip | Bin 0 -> 13276 bytes test/integration/scan_test.go | 15 ++++- 3 files changed, 14 insertions(+), 59 deletions(-) create mode 100644 test/integration/data/sources-gitignore.zip diff --git a/internal/commands/scan_test.go b/internal/commands/scan_test.go index c4a896112..ab1e5c179 100644 --- a/internal/commands/scan_test.go +++ b/internal/commands/scan_test.go @@ -2732,61 +2732,3 @@ a*cation-jira.yml`)) gitIgnoreFilter, _ := getGitignorePatterns("", zipPath) assert.Assert(t, len(gitIgnoreFilter) > 0, "Expected patterns from .gitignore file") } - -func TestGetGitignorePatterns_ZipPath_GitIgnore_PermissionDenied(t *testing.T) { - dir := t.TempDir() - zipPath := filepath.Join(dir, "example.zip") - - // Create the zip file - zipFile, err := os.Create(zipPath) - if err != nil { - t.Fatalf("Failed to create zip file: %v", err) - } - defer func(zipFile *os.File) { - err := zipFile.Close() - if err != nil { - t.Fatalf("Failed to close zip file: %v", err) - } - }(zipFile) - - // Create a zip writer - zipWriter := zip.NewWriter(zipFile) - - // Set up a header with 0000 permission for .gitignore - header := &zip.FileHeader{ - Name: "example/.gitignore", - Method: zip.Deflate, - } - header.SetMode(0) // UNIX permission 0000 — no access for anyone - - // Add a file to the zip archive - fileInZip, err := zipWriter.CreateHeader(header) - if err != nil { - t.Fatalf("Failed to add file to zip: %v", err) - } - - // Write content to the file in the zip archive - _, err = fileInZip.Write([]byte(`src -src/ -**/vullib -**/admin/ -vulnerability/** -application-jira.yml -*.yml -LoginController[0-1].java -LoginController[!0-3].java -LoginController[01].java -LoginController[!456].java -?pplication-jira.yml -a*cation-jira.yml`)) - if err != nil { - t.Fatalf("Failed to write data to zip: %v", err) - } - err = zipWriter.Close() - if err != nil { - return - } - - _, err = getGitignorePatterns("", zipPath) - assert.ErrorContains(t, err, "permission denied") -} diff --git a/test/integration/data/sources-gitignore.zip b/test/integration/data/sources-gitignore.zip new file mode 100644 index 0000000000000000000000000000000000000000..a7de79335001570f6cdf9f84928f67e4a5893b53 GIT binary patch literal 13276 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 Date: Mon, 4 Aug 2025 18:35:43 +0530 Subject: [PATCH 5/5] Added source for integration test --- test/integration/scan_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/scan_test.go b/test/integration/scan_test.go index a052b81e4..c211f4890 100644 --- a/test/integration/scan_test.go +++ b/test/integration/scan_test.go @@ -2563,7 +2563,7 @@ func TestCreateScanFilterGitIgnoreFile_GitIgnoreExist(t *testing.T) { "scan", "create", flag(params.ProjectName), getProjectNameForScanTests(), flag(params.BranchFlag), "dummy_branch", - flag(params.SourcesFlag), "sources-gitignore.zip", + flag(params.SourcesFlag), "data/sources-gitignore.zip", flag(params.GitIgnoreFileFilterFlag), }