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

Make scanner respect .gitignore files #191

Merged
merged 8 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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: 3 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ignored
/yarn.lock
composer*
16 changes: 16 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)

PLATFORMS
x86_64-linux

DEPENDENCIES
ast

RUBY VERSION
ruby 3.0.2p107

BUNDLED WITH
2.2.28
51 changes: 51 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/ignored/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)

PLATFORMS
x86_64-linux

DEPENDENCIES
ast

RUBY VERSION
ruby 3.0.2p107

BUNDLED WITH
2.2.28
7 changes: 7 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/ignored/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
1 change: 1 addition & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/subdir/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Gemfile.lock
16 changes: 16 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/subdir/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
GEM
remote: https://rubygems.org/
specs:
ast (2.4.2)

PLATFORMS
x86_64-linux

DEPENDENCIES
ast

RUBY VERSION
ruby 3.0.2p107

BUNDLED WITH
2.2.28
51 changes: 51 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/subdir/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/subdir/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
7 changes: 7 additions & 0 deletions cmd/osv-scanner/fixtures/locks-gitignore/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
6 changes: 6 additions & 0 deletions cmd/osv-scanner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ func run(args []string, stdout, stderr io.Writer) int {
Usage: "check subdirectories",
Value: false,
},
&cli.BoolFlag{
Name: "ignore-gitignore",
another-rex marked this conversation as resolved.
Show resolved Hide resolved
Usage: "also scan files that would be ignored by .gitignore",
Value: false,
},
},
ArgsUsage: "[directory1 directory2...]",
Action: func(context *cli.Context) error {
Expand All @@ -106,6 +111,7 @@ func run(args []string, stdout, stderr io.Writer) int {
DockerContainerNames: context.StringSlice("docker"),
Recursive: context.Bool("recursive"),
SkipGit: context.Bool("skip-git"),
IgnoreGitignore: context.Bool("ignore-gitignore"),
ConfigOverridePath: context.String("config"),
DirectoryPaths: context.Args().Slice(),
}, r)
Expand Down
30 changes: 30 additions & 0 deletions cmd/osv-scanner/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,36 @@ func TestRun(t *testing.T) {
`,
wantStderr: "",
},
// .gitignored files
{
name: "",
args: []string{"", "--recursive", "./fixtures/locks-gitignore"},
wantExitCode: 0,
wantStdout: `
Scanning dir ./fixtures/locks-gitignore
Scanned %%/fixtures/locks-gitignore/Gemfile.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/subdir/yarn.lock file and found 1 packages
`,
wantStderr: "",
},
// ignoring .gitignore
{
name: "",
args: []string{"", "--recursive", "--ignore-gitignore", "./fixtures/locks-gitignore"},
wantExitCode: 0,
wantStdout: `
Scanning dir ./fixtures/locks-gitignore
Scanned %%/fixtures/locks-gitignore/Gemfile.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/composer.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/ignored/Gemfile.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/ignored/yarn.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/subdir/Gemfile.lock file and found 1 packages
another-rex marked this conversation as resolved.
Show resolved Hide resolved
Scanned %%/fixtures/locks-gitignore/subdir/composer.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/subdir/yarn.lock file and found 1 packages
Scanned %%/fixtures/locks-gitignore/yarn.lock file and found 1 packages
`,
wantStderr: "",
},
// output with json
{
name: "",
Expand Down
45 changes: 43 additions & 2 deletions pkg/osvscanner/osvscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type ScannerActions struct {
GitCommits []string
Recursive bool
SkipGit bool
IgnoreGitignore bool
DockerContainerNames []string
ConfigOverridePath string
}
Expand All @@ -42,8 +43,13 @@ var VulnerabilitiesFoundErr = errors.New("vulnerabilities found")
// - Any lockfiles with scanLockfile
// - Any SBOM files with scanSBOMFile
// - Any git repositories with scanGit
func scanDir(r *output.Reporter, query *osv.BatchedQuery, dir string, skipGit bool, recursive bool) error {
func scanDir(r *output.Reporter, query *osv.BatchedQuery, dir string, skipGit bool, recursive bool, useGitIgnore bool) error {
if useGitIgnore && !canCheckIgnore() {
r.PrintError("Command \"git check-ignore\" not found. Cannot parse .gitignore files.\n")
useGitIgnore = false
}
root := true

return filepath.WalkDir(dir, func(path string, info os.DirEntry, err error) error {
if err != nil {
r.PrintText(fmt.Sprintf("Failed to walk %s: %v\n", path, err))
Expand All @@ -55,6 +61,14 @@ func scanDir(r *output.Reporter, query *osv.BatchedQuery, dir string, skipGit bo
return err
}

if useGitIgnore && isGitIgnored(filepath.Dir(path), info.Name()) {
if info.IsDir() {
return filepath.SkipDir
}

return nil
}

if !skipGit && info.IsDir() && info.Name() == ".git" {
err := scanGit(r, query, filepath.Dir(path)+"/")
if err != nil {
Expand Down Expand Up @@ -87,6 +101,33 @@ func scanDir(r *output.Reporter, query *osv.BatchedQuery, dir string, skipGit bo
})
}

// canCheckIgnore checks if the command `git check-ignore` will work on the system,
// which is used to check .gitignore files
func canCheckIgnore() bool {
cmd := exec.Command("git", "check-ignore")
michaelkedar marked this conversation as resolved.
Show resolved Hide resolved
// Expect the command to exit with code 128 for "no path specified" or "not a git repository"
// Any other exit code means the command does not work
if err := cmd.Run(); err != nil {
var exitErr *exec.ExitError
return errors.As(err, &exitErr) && exitErr.ExitCode() == 128
}

return false
}

// isGitIgnored checks if file/directory is ignored by a .gitignore file.
// returns true if it is ignored
func isGitIgnored(dir string, filename string) bool {
// https://git-scm.com/docs/git-check-ignore
// Exit status 0 means the file is ignored
// Exit status 1 means it is not ignored
// Exit status 128 means the file is not in a git repo (not ignored)
cmd := exec.Command("git", "-C", dir, "check-ignore", "-q", filename)
err := cmd.Run()

return err == nil
}

// scanLockfile will load, identify, and parse the lockfile path passed in, and add the dependencies specified
// within to `query`
func scanLockfile(r *output.Reporter, query *osv.BatchedQuery, path string, parseAs string) error {
Expand Down Expand Up @@ -353,7 +394,7 @@ func DoScan(actions ScannerActions, r *output.Reporter) (models.VulnerabilityRes

for _, dir := range actions.DirectoryPaths {
r.PrintText(fmt.Sprintf("Scanning dir %s\n", dir))
err := scanDir(r, &query, dir, actions.SkipGit, actions.Recursive)
err := scanDir(r, &query, dir, actions.SkipGit, actions.Recursive, !actions.IgnoreGitignore)
if err != nil {
return models.VulnerabilityResults{}, err
}
Expand Down