github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/internal/gitignore.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package internal 16 17 import ( 18 "bufio" 19 "os" 20 "path" 21 "strings" 22 23 "github.com/go-git/go-git/v5/plumbing/format/gitignore" 24 scalibrfs "github.com/google/osv-scalibr/fs" 25 ) 26 27 // GitignorePattern is a list of patterns found inside a .gitignore file. 28 type GitignorePattern gitignore.Matcher 29 30 // EmptyGitignore returns an empty matcher that doesn't match on any pattern. 31 func EmptyGitignore() GitignorePattern { 32 return gitignore.NewMatcher(nil) 33 } 34 35 // GitignoreMatch returns whether the specified file path should be ignored 36 // according to the specified .gitignore patterns. 37 func GitignoreMatch(gitignores []GitignorePattern, filePath []string, isDir bool) bool { 38 for _, p := range gitignores { 39 if p != nil && p.Match(filePath, isDir) { 40 return true 41 } 42 } 43 return false 44 } 45 46 // ParseDirForGitignore parses .gitignore patterns found in the 47 // specified directory. 48 func ParseDirForGitignore(fs scalibrfs.FS, dirPath string) (GitignorePattern, error) { 49 dirPath = strings.TrimSuffix(dirPath, "/") 50 filePath := path.Join(dirPath, ".gitignore") 51 f, err := fs.Open(filePath) 52 if err != nil { 53 if os.IsNotExist(err) { 54 return nil, nil 55 } 56 return nil, err 57 } 58 defer f.Close() 59 scanner := bufio.NewScanner(f) 60 ps := []gitignore.Pattern{} 61 pathTokens := strings.Split(dirPath, "/") 62 for scanner.Scan() { 63 s := scanner.Text() 64 if !strings.HasPrefix(s, "#") && len(strings.TrimSpace(s)) > 0 { 65 ps = append(ps, gitignore.ParsePattern(s, pathTokens)) 66 } 67 } 68 return gitignore.NewMatcher(ps), nil 69 } 70 71 // ParseParentGitignores parses all .gitignore patterns between the current dir 72 // and the scan root, excluding the current directory. 73 func ParseParentGitignores(fs scalibrfs.FS, dirPath string) ([]GitignorePattern, error) { 74 var filePath strings.Builder 75 result := []GitignorePattern{} 76 components := strings.Split(dirPath, "/") 77 for _, dir := range components[:len(components)-1] { 78 filePath.WriteString(dir + "/") 79 gitignores, err := ParseDirForGitignore(fs, filePath.String()) 80 if err != nil { 81 return nil, err 82 } 83 result = append(result, gitignores) 84 } 85 return result, nil 86 }