Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 6 additions & 0 deletions pkg/utils/file_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,12 @@ func (fw *FileFilter) GetFilteredFiles(filesCh chan string, globs []string) chan
// buildGlobs iterates a list of ignore filesToFilter and returns a list of glob patterns that can be used to test for ignored filesToFilter
func (fw *FileFilter) buildGlobs(ignoreFiles []string) ([]string, error) {
var globs = make([]string, 0)
globPatternMatcher := gitignore.CompileIgnoreLines()
for _, ignoreFile := range ignoreFiles {
if globPatternMatcher.MatchesPath(ignoreFile) {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipped root policy rule files

Medium Severity

buildGlobs skips reading a rule file when MatchesPath is true for that file’s path. That also drops later .snyk or .dcignore files at the repo root when an earlier .gitignore ignores them, even though their parent directory is not excluded. Those policy excludes no longer enter globs, so scanning can miss paths that used to be filtered.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fe47ad7. Configure here.

}

var content []byte
content, err := os.ReadFile(ignoreFile)
if err != nil {
Expand All @@ -162,6 +167,7 @@ func (fw *FileFilter) buildGlobs(ignoreFiles []string) ([]string, error) {
parsedRules := parseIgnoreFile(content, filepath.Dir(ignoreFile))
globs = append(globs, parsedRules...)
}
globPatternMatcher = gitignore.CompileIgnoreLines(globs...)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: This is a proper performance issue.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compiling is a very CPU cost intensive operation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PeterSchafer it seems it is a real issue as nested .gitignore are treated as a flat list. It diverges from the .gitignore expected behavior

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably this current approach is not good as you said, but we might wanna check what is being ignored and ignore nested ones

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe a strategy building a "tree" where we can prune the branches is the best approach

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recompiles matcher every ignore file

Medium Severity

Each loop iteration in buildGlobs calls gitignore.CompileIgnoreLines on the entire growing globs slice. CompileIgnoreLines is expensive, so repos with many nested ignore files pay roughly quadratic CPU during rule collection compared to the previous single-pass append.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fe47ad7. Configure here.

}

return globs, nil
Expand Down
29 changes: 27 additions & 2 deletions pkg/utils/file_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,31 @@ func TestFileFilter_GetFilteredFiles_ignoreRuleScenarios(t *testing.T) {
excluded: []string{"root.txt", "pkg/pkg.txt", "pkg/root.txt"},
kept: []string{"pkg/keep.txt"},
},
{
name: "ignore file below ignored parent cannot reinclude files",
files: map[string]string{
".gitignore": "node_modules\n",
"node_modules/pkg/.gitignore": "!index.js\n",
"node_modules/pkg/index.js": "x",
"node_modules/pkg/other.js": "x",
"src/index.js": "x",
},
ruleFiles: []string{".gitignore"},
excluded: []string{"node_modules/pkg/index.js", "node_modules/pkg/other.js"},
kept: []string{"src/index.js"},
},
Comment on lines +609 to +621

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems for .gitignore inside node_modules is not something common / expected to have. But this reveals a potential issue regarding nested directories / experimental repos inside real repos, etc.

Nice finding!

{
name: "gitignore below snyk-excluded parent cannot reinclude files",
files: map[string]string{
".snyk": "version: v1.25.1\nexclude:\n global:\n - node_modules\n",
"node_modules/pkg/.gitignore": "!index.js\n",
"node_modules/pkg/index.js": "x",
"src/index.js": "x",
},
ruleFiles: []string{".gitignore", ".snyk"},
excluded: []string{"node_modules/pkg/index.js"},
kept: []string{"src/index.js"},
},

// --- C2. Special characters in the ignore rule pattern itself ---
// git treats parentheses/spaces in a gitignore pattern as literal (fnmatch), so a folder
Expand Down Expand Up @@ -992,7 +1017,7 @@ func testCases(t *testing.T) []fileFilterTestCase {
"a/.gitignore": "!*.txt",
},
filesToFilter: []string{"a/file2.js"},
expectedFiles: []string{"file1.js", "a/file1.txt", ".gitignore", "a/.gitignore"},
expectedFiles: []string{"file1.js", ".gitignore", "a/.gitignore"},
},
{
name: "Supports .dcignore rule file",
Expand All @@ -1003,7 +1028,7 @@ func testCases(t *testing.T) []fileFilterTestCase {
"a/.dcignore": "!*.txt",
},
filesToFilter: []string{"a/file2.js"},
expectedFiles: []string{"file1.js", "a/file1.txt", ".dcignore", "a/.dcignore"},
expectedFiles: []string{"file1.js", ".dcignore", "a/.dcignore"},
},
{
name: "Supports .snyk style exclude rules",
Expand Down