github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/java/gradlelockfile/gradlelockfile.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 gradlelockfile extracts pom.xml files. 16 package gradlelockfile 17 18 import ( 19 "bufio" 20 "context" 21 "fmt" 22 "path/filepath" 23 "slices" 24 "strings" 25 26 "github.com/google/osv-scalibr/extractor" 27 "github.com/google/osv-scalibr/extractor/filesystem" 28 "github.com/google/osv-scalibr/extractor/filesystem/language/java/javalockfile" 29 "github.com/google/osv-scalibr/inventory" 30 "github.com/google/osv-scalibr/plugin" 31 "github.com/google/osv-scalibr/purl" 32 ) 33 34 const ( 35 // Name is the unique name of this extractor. 36 Name = "java/gradlelockfile" 37 38 gradleLockFileCommentPrefix = "#" 39 gradleLockFileEmptyPrefix = "empty=" 40 ) 41 42 func isGradleLockFileDepLine(line string) bool { 43 ret := strings.HasPrefix(line, gradleLockFileCommentPrefix) || 44 strings.HasPrefix(line, gradleLockFileEmptyPrefix) 45 46 return !ret 47 } 48 49 func parseToGradlePackageDetail(line string) (*extractor.Package, error) { 50 parts := strings.SplitN(line, ":", 3) 51 if len(parts) < 3 { 52 return nil, fmt.Errorf("invalid line in gradle lockfile: %s", line) 53 } 54 55 group, artifact, version := parts[0], parts[1], parts[2] 56 if !strings.Contains(version, "=") { 57 return nil, fmt.Errorf("invalid line in gradle lockfile: %s", line) 58 } 59 version = strings.SplitN(version, "=", 2)[0] 60 61 return &extractor.Package{ 62 Name: fmt.Sprintf("%s:%s", group, artifact), 63 Version: version, 64 PURLType: purl.TypeMaven, 65 Metadata: &javalockfile.Metadata{ 66 ArtifactID: artifact, 67 GroupID: group, 68 }, 69 }, nil 70 } 71 72 // Extractor extracts Maven packages from Gradle files. 73 type Extractor struct{} 74 75 // New returns a new instance of the extractor. 76 func New() filesystem.Extractor { return &Extractor{} } 77 78 // Name of the extractor 79 func (e Extractor) Name() string { return Name } 80 81 // Version of the extractor 82 func (e Extractor) Version() int { return 0 } 83 84 // Requirements of the extractor 85 func (e Extractor) Requirements() *plugin.Capabilities { 86 return &plugin.Capabilities{} 87 } 88 89 // FileRequired returns true if the specified file matches Gradle lockfile patterns. 90 func (e Extractor) FileRequired(api filesystem.FileAPI) bool { 91 base := filepath.Base(api.Path()) 92 93 return slices.Contains([]string{"buildscript-gradle.lockfile", "gradle.lockfile"}, base) 94 } 95 96 // Extract extracts packages from Gradle files passed through the scan input. 97 func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) (inventory.Inventory, error) { 98 packages := make([]*extractor.Package, 0) 99 scanner := bufio.NewScanner(input.Reader) 100 101 for scanner.Scan() { 102 lockLine := strings.TrimSpace(scanner.Text()) 103 if !isGradleLockFileDepLine(lockLine) { 104 continue 105 } 106 107 pkg, err := parseToGradlePackageDetail(lockLine) 108 if err != nil { 109 continue 110 } 111 112 pkg.Locations = []string{input.Path} 113 114 packages = append(packages, pkg) 115 } 116 117 if err := scanner.Err(); err != nil { 118 return inventory.Inventory{}, fmt.Errorf("failed to read: %w", err) 119 } 120 121 return inventory.Inventory{Packages: packages}, nil 122 } 123 124 var _ filesystem.Extractor = Extractor{}