diff --git a/extractor/filesystem/language/java/pomxml/pomxml.go b/extractor/filesystem/language/java/pomxml/pomxml.go index b8350cf1..1ea29332 100644 --- a/extractor/filesystem/language/java/pomxml/pomxml.go +++ b/extractor/filesystem/language/java/pomxml/pomxml.go @@ -23,12 +23,12 @@ import ( "regexp" "strings" + "deps.dev/util/maven" "golang.org/x/exp/maps" "github.com/google/osv-scalibr/extractor" "github.com/google/osv-scalibr/extractor/filesystem" "github.com/google/osv-scalibr/extractor/filesystem/language/java/javalockfile" - "github.com/google/osv-scalibr/log" "github.com/google/osv-scalibr/plugin" "github.com/google/osv-scalibr/purl" ) @@ -41,21 +41,10 @@ const ( // "Constant" at the top to compile this regex only once. var ( versionRequirementReg = regexp.MustCompile(`[[(]?(.*?)(?:,|[)\]]|$)`) - interpolationReg = regexp.MustCompile(`\${(.+)}`) ) -type mavenLockDependency struct { - XMLName xml.Name `xml:"dependency"` - GroupID string `xml:"groupId"` - ArtifactID string `xml:"artifactId"` - Version string `xml:"version"` - Scope string `xml:"scope"` - Type string `xml:"type"` - Classifier string `xml:"classifier"` -} - -func (mld mavenLockDependency) parseResolvedVersion(version string) string { - results := versionRequirementReg.FindStringSubmatch(version) +func parseResolvedVersion(version maven.String) string { + results := versionRequirementReg.FindStringSubmatch(string(version)) // First capture group will always exist, but might be empty, therefore the slice will always // have a length of 2. if results == nil || results[1] == "" { @@ -65,75 +54,6 @@ func (mld mavenLockDependency) parseResolvedVersion(version string) string { return results[1] } -func (mld mavenLockDependency) resolveVersionValue(lockfile mavenLockFile) string { - // results will always either be nil or have a length of 2 - results := interpolationReg.FindStringSubmatch(mld.Version) - - // no interpolation, so just return the version as-is - if results == nil { - return mld.Version - } - if val, ok := lockfile.Properties.m[results[1]]; ok { - return val - } - - log.Errorf( - "Failed to resolve version of %s: property \"%s\" could not be found for \"%s\"\n", - mld.GroupID+":"+mld.ArtifactID, - results[1], - lockfile.GroupID+":"+lockfile.ArtifactID, - ) - - return "0" -} - -func (mld mavenLockDependency) ResolveVersion(lockfile mavenLockFile) string { - version := mld.resolveVersionValue(lockfile) - - return mld.parseResolvedVersion(version) -} - -type mavenLockFile struct { - XMLName xml.Name `xml:"project"` - ModelVersion string `xml:"modelVersion"` - GroupID string `xml:"groupId"` - ArtifactID string `xml:"artifactId"` - Properties mavenLockProperties `xml:"properties"` - Dependencies []mavenLockDependency `xml:"dependencies>dependency"` - ManagedDependencies []mavenLockDependency `xml:"dependencyManagement>dependencies>dependency"` -} - -type mavenLockProperties struct { - m map[string]string -} - -func (p *mavenLockProperties) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { - p.m = map[string]string{} - - for { - t, err := d.Token() - if err != nil { - return err - } - - switch tt := t.(type) { - case xml.StartElement: - var s string - - if err := d.DecodeElement(&s, &tt); err != nil { - return fmt.Errorf("%w", err) - } - - p.m[tt.Name.Local] = s - - case xml.EndElement: - if tt.Name == start.Name { - return nil - } - } - } -} - // Extractor extracts Maven packages from pom.xml files. type Extractor struct{} @@ -158,58 +78,41 @@ func (e Extractor) FileRequired(api filesystem.FileAPI) bool { // Extract extracts packages from pom.xml files passed through the scan input. func (e Extractor) Extract(ctx context.Context, input *filesystem.ScanInput) ([]*extractor.Inventory, error) { - var parsedLockfile *mavenLockFile - - err := xml.NewDecoder(input.Reader).Decode(&parsedLockfile) + var project *maven.Project + err := xml.NewDecoder(input.Reader).Decode(&project) if err != nil { return nil, fmt.Errorf("could not extract from %s: %w", input.Path, err) } + if err := project.Interpolate(); err != nil { + return nil, fmt.Errorf("failed to interpolate pom.xml %s: %w", input.Path, err) + } details := map[string]*extractor.Inventory{} - for _, lockPackage := range parsedLockfile.ManagedDependencies { - finalName := lockPackage.GroupID + ":" + lockPackage.ArtifactID - metadata := javalockfile.Metadata{ - ArtifactID: lockPackage.ArtifactID, - GroupID: lockPackage.GroupID, - DepGroupVals: []string{}, - } - - pkgDetails := &extractor.Inventory{ - Name: finalName, - Version: lockPackage.ResolveVersion(*parsedLockfile), - Locations: []string{input.Path}, - Metadata: &metadata, + for _, dep := range project.Dependencies { + g, a, found := strings.Cut(dep.Name(), ":") + if !found { + return nil, fmt.Errorf("invalid package name: %s", dep.Name()) } - if scope := strings.TrimSpace(lockPackage.Scope); scope != "" && scope != "compile" { - // Only append non-default scope (compile is the default scope). - metadata.DepGroupVals = []string{scope} - } - details[finalName] = pkgDetails - } - - // standard dependencies take precedent over managed dependencies - for _, lockPackage := range parsedLockfile.Dependencies { - finalName := lockPackage.GroupID + ":" + lockPackage.ArtifactID metadata := javalockfile.Metadata{ - ArtifactID: lockPackage.ArtifactID, - GroupID: lockPackage.GroupID, - Type: lockPackage.Type, - Classifier: lockPackage.Classifier, + ArtifactID: a, + GroupID: g, + Type: string(dep.Type), + Classifier: string(dep.Classifier), DepGroupVals: []string{}, } pkgDetails := &extractor.Inventory{ - Name: finalName, - Version: lockPackage.ResolveVersion(*parsedLockfile), + Name: dep.Name(), + Version: parseResolvedVersion(dep.Version), Locations: []string{input.Path}, Metadata: &metadata, } - if scope := strings.TrimSpace(lockPackage.Scope); scope != "" && scope != "compile" { + if scope := strings.TrimSpace(string(dep.Scope)); scope != "" && scope != "compile" { // Only append non-default scope (compile is the default scope). metadata.DepGroupVals = []string{scope} } - details[finalName] = pkgDetails + details[dep.Name()] = pkgDetails } return maps.Values(details), nil diff --git a/extractor/filesystem/language/java/pomxml/pomxml_test.go b/extractor/filesystem/language/java/pomxml/pomxml_test.go index f1a37136..d5172f55 100644 --- a/extractor/filesystem/language/java/pomxml/pomxml_test.go +++ b/extractor/filesystem/language/java/pomxml/pomxml_test.go @@ -165,16 +165,6 @@ func TestExtractor_Extract(t *testing.T) { DepGroupVals: []string{}, }, }, - { - Name: "com.google.code.findbugs:jsr305", - Version: "3.0.2", - Locations: []string{"testdata/with-dependency-management.xml"}, - Metadata: &javalockfile.Metadata{ - ArtifactID: "jsr305", - GroupID: "com.google.code.findbugs", - DepGroupVals: []string{}, - }, - }, }, }, { diff --git a/extractor/filesystem/language/java/pomxml/testdata/interpolation.xml b/extractor/filesystem/language/java/pomxml/testdata/interpolation.xml index eb783c8e..e9760a02 100644 --- a/extractor/filesystem/language/java/pomxml/testdata/interpolation.xml +++ b/extractor/filesystem/language/java/pomxml/testdata/interpolation.xml @@ -26,15 +26,11 @@ my.package ${my.package.version} - - - - - org.mine - ranged-package - ${version-range} - - - + + org.mine + ranged-package + ${version-range} + +