syft/syft/pkg/cataloger/java/package_url.go
Will Murphy 1217ed2307 WIP: possible improvement to group ID guessing
Signed-off-by: Will Murphy <will.murphy@anchore.com>
2023-09-27 17:24:08 -04:00

146 lines
4.0 KiB
Go

package java
import (
"strings"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
)
// PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec)
func packageURL(name, version string, metadata pkg.JavaMetadata) string {
var groupID = name
if gID := groupIDFromJavaMetadata(name, metadata); gID != "" {
groupID = gID
}
pURL := packageurl.NewPackageURL(
packageurl.TypeMaven, // TODO: should we filter down by package types here?
groupID,
name,
version,
nil, // TODO: there are probably several qualifiers that can be specified here
"")
return pURL.ToString()
}
// GroupIDFromJavaPackage returns the authoritative group ID for a Java package.
// The order of precedence is:
// 1. The group ID from the POM properties
// 2. The group ID from the POM project
// 3. The group ID from a select map of known group IDs
// 4. The group ID from the Java manifest
func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupID string) {
if groupID = groupIDFromPomProperties(metadata.PomProperties); groupID != "" {
return groupID
}
if groupID = groupIDFromPomProject(metadata.PomProject); groupID != "" {
return groupID
}
if groupID = groupIDFromKnownPackageList(pkgName); groupID != "" {
return groupID
}
if groupID = groupIDFromJavaManifest(metadata.Manifest, pkgName); groupID != "" {
return groupID
}
return groupID
}
func groupIDFromKnownPackageList(pkgName string) (groupID string) {
if groupID, ok := cpe.DefaultArtifactIDToGroupID[pkgName]; ok {
return groupID
}
return groupID
}
func groupIDFromJavaManifest(manifest *pkg.JavaManifest, pkgName string) (groupID string) {
if manifest == nil {
return groupID
}
groupIDS := cpe.GetManifestFieldGroupIDs(manifest, cpe.PrimaryJavaManifestGroupIDFields, pkgName)
// assumes that primaryJavaManifestNameFields are ordered by priority
if len(groupIDS) != 0 {
return groupIDS[0]
}
groupIDS = cpe.GetManifestFieldGroupIDs(manifest, cpe.SecondaryJavaManifestGroupIDFields, pkgName)
if len(groupIDS) != 0 {
return groupIDS[0]
}
return groupID
}
func groupIDFromPomProperties(properties *pkg.PomProperties) (groupID string) {
if properties == nil {
return groupID
}
if properties.GroupID != "" {
return cleanGroupID(properties.GroupID)
}
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
if looksLikeGroupID(properties.ArtifactID) {
// there is a strong indication that the artifact ID is really a group ID
return cleanGroupID(properties.ArtifactID)
}
return groupID
}
func groupIDFromPomProject(project *pkg.PomProject) (groupID string) {
if project == nil {
return groupID
}
// check the project details
if project.GroupID != "" {
return cleanGroupID(project.GroupID)
}
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
if looksLikeGroupID(project.ArtifactID) {
// there is a strong indication that the artifact ID is really a group ID
return cleanGroupID(project.ArtifactID)
}
// let's check the parent details
// if the current project does not have a group ID, but the parent does, we'll use the parent's group ID
if project.Parent != nil {
if project.Parent.GroupID != "" {
return cleanGroupID(project.Parent.GroupID)
}
// sometimes the publisher puts the group ID in the artifact ID field unintentionally
if looksLikeGroupID(project.Parent.ArtifactID) {
// there is a strong indication that the artifact ID is really a group ID
return cleanGroupID(project.Parent.ArtifactID)
}
}
return groupID
}
func looksLikeGroupID(value string) bool {
return strings.Contains(value, ".")
}
func cleanGroupID(groupID string) string {
return strings.TrimSpace(removeOSCIDirectives(groupID))
}
func removeOSCIDirectives(groupID string) string {
// for example:
// org.bar;uses:=“org.foo” -> org.bar
// more about OSGI directives see https://spring.io/blog/2008/10/20/understanding-the-osgi-uses-directive/
return strings.Split(groupID, ";")[0]
}