mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
fix(java): resolve Groovy 4+ group IDs to org.apache.groovy (#5313)
* fix(java): resolve Groovy 4+ group IDs to org.apache.groovy Groovy 4.0 moved from the Codehaus coordinates (org.codehaus.groovy) to the Apache Software Foundation (org.apache.groovy). JARs that ship no pom metadata fall through to the static artifact-to-group map, which carries the 3.x-era org.codehaus.groovy entries for the whole groovy family, so 4.x artifacts were attributed to an obsolete group even though their manifest identifies the Apache vendor. Where the static map attributes an artifact to the historical group, a major-version migration resolves groovy 4.x and newer to org.apache.groovy, while 3.x and earlier keep their historical group. The Eclipse-hosted groovy-eclipse-* artifacts, which share the prefix but kept their Codehaus coordinates, are excluded. Fixes #5311 Signed-off-by: texasich <101962694+texasich@users.noreply.github.com> * refactor(java): share version-aware groovy group ID lookup with CPE generation - move the artifact-to-group lookup behind `cpegenerate.ArtifactIDToGroupID(artifactID, version)` so the PURL and CPE paths agree; the exported mutable map is now unexported - version-dependent entries are expressed as a `groupIDMigration{before, after, atMajor}` (groovy moved to `org.apache.groovy` at 4.0); unconditional entries, including `groovy-eclipse-*` and apache-only 4.0+ modules, stay in the plain map - add apache-only modules (`groovy-ginq`, `groovy-contracts`, `groovy-callsite`, ...) that otherwise took the group from their per-module `Automatic-Module-Name` This changes CPEs for pom-less groovy 4+ jars (all real ones): vendors are now `apache` (e.g. `cpe:2.3:a:apache:groovy:4.0.33`) instead of `codehaus`, matching the PURL, jars with pom metadata, and NVD. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: texasich <101962694+texasich@users.noreply.github.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -182,6 +182,24 @@ func TestGeneratePackageCPEs(t *testing.T) {
|
|||||||
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// groovy moved to org.apache.groovy at 4.0 and real jars ship no pom metadata, so the known package
|
||||||
|
// list drives the vendor. See https://github.com/anchore/syft/issues/5311
|
||||||
|
name: "groovy 4 module without pom metadata",
|
||||||
|
p: pkg.Package{
|
||||||
|
Name: "groovy-json",
|
||||||
|
Version: "4.0.33",
|
||||||
|
FoundBy: "some-analyzer",
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg,
|
||||||
|
Metadata: pkg.JavaArchive{Manifest: &pkg.JavaManifest{}},
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"cpe:2.3:a:apache:groovy-json:4.0.33:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:apache:groovy_json:4.0.33:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:apache:groovy:4.0.33:*:*:*:*:*:*:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "java language with groupID",
|
name: "java language with groupID",
|
||||||
p: pkg.Package{
|
p: pkg.Package{
|
||||||
|
|||||||
@@ -190,16 +190,16 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return GroupIDsFromJavaMetadata(p.Name, metadata)
|
return GroupIDsFromJavaMetadata(p.Name, p.Version, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GroupIDsFromJavaMetadata returns the possible group IDs for a Java package
|
// GroupIDsFromJavaMetadata returns the possible group IDs for a Java package
|
||||||
// This function is similar to GroupIDFromJavaPackage, but returns all possible group IDs and is less strict
|
// This function is similar to GroupIDFromJavaPackage, but returns all possible group IDs and is less strict
|
||||||
// It is used as a way to generate possible candidates for CPE matching.
|
// It is used as a way to generate possible candidates for CPE matching.
|
||||||
func GroupIDsFromJavaMetadata(pkgName string, metadata pkg.JavaArchive) (groupIDs []string) {
|
func GroupIDsFromJavaMetadata(pkgName, version string, metadata pkg.JavaArchive) (groupIDs []string) {
|
||||||
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
|
groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...)
|
||||||
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
|
groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...)
|
||||||
groupIDs = append(groupIDs, groupIDsFromJavaManifest(pkgName, metadata.Manifest)...)
|
groupIDs = append(groupIDs, groupIDsFromJavaManifest(pkgName, version, metadata.Manifest)...)
|
||||||
|
|
||||||
return groupIDs
|
return groupIDs
|
||||||
}
|
}
|
||||||
@@ -253,8 +253,8 @@ func addGroupIDsFromGroupIDsAndArtifactID(groupID, artifactID string) (groupIDs
|
|||||||
return groupIDs
|
return groupIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []string {
|
func groupIDsFromJavaManifest(pkgName, version string, manifest *pkg.JavaManifest) []string {
|
||||||
if groupID, ok := DefaultArtifactIDToGroupID[pkgName]; ok {
|
if groupID, ok := ArtifactIDToGroupID(pkgName, version); ok {
|
||||||
return []string{groupID}
|
return []string{groupID}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@ func GetManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (grou
|
|||||||
|
|
||||||
func cleanGroupID(groupID string) string {
|
func cleanGroupID(groupID string) string {
|
||||||
groupID = strings.TrimSpace(strings.Split(removeOSCIDirectives(groupID), "#")[0])
|
groupID = strings.TrimSpace(strings.Split(removeOSCIDirectives(groupID), "#")[0])
|
||||||
if replacement, ok := GroupIDCorrections[groupID]; ok {
|
if replacement, ok := groupIDCorrections[groupID]; ok {
|
||||||
return replacement
|
return replacement
|
||||||
}
|
}
|
||||||
return groupID
|
return groupID
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package cpegenerate
|
package cpegenerate
|
||||||
|
|
||||||
var GroupIDCorrections = map[string]string{
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var groupIDCorrections = map[string]string{
|
||||||
"org.lz4.java": "org.lz4",
|
"org.lz4.java": "org.lz4",
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultArtifactIDToGroupID = map[string]string{
|
// defaultArtifactIDToGroupID holds artifacts that always map to the same group ID. Look up group IDs with
|
||||||
|
// ArtifactIDToGroupID instead of reading this map directly, since migratedArtifactIDToGroupID takes precedence.
|
||||||
|
var defaultArtifactIDToGroupID = map[string]string{
|
||||||
"ant": "org.apache.ant",
|
"ant": "org.apache.ant",
|
||||||
"ant-antlr": "org.apache.ant",
|
"ant-antlr": "org.apache.ant",
|
||||||
"ant-antunit": "org.apache.ant",
|
"ant-antunit": "org.apache.ant",
|
||||||
@@ -1832,104 +1839,83 @@ var DefaultArtifactIDToGroupID = map[string]string{
|
|||||||
"commons-jelly-tags-velocity": "commons-jelly",
|
"commons-jelly-tags-velocity": "commons-jelly",
|
||||||
"commons-jelly-tags-xml": "commons-jelly",
|
"commons-jelly-tags-xml": "commons-jelly",
|
||||||
"commons-jelly-tags-xmlunit": "commons-jelly",
|
"commons-jelly-tags-xmlunit": "commons-jelly",
|
||||||
"groovy": "org.codehaus.groovy",
|
// the Eclipse-hosted groovy-eclipse-* artifacts stayed on codehaus past 4.0, and the org.apache.groovy modules
|
||||||
"groovy-all": "org.codehaus.groovy",
|
// below were added in 4.0+ and only exist under apache. See https://github.com/anchore/syft/issues/5311
|
||||||
"groovy-all-jdk14": "org.codehaus.groovy",
|
"groovy-eclipse-batch": "org.codehaus.groovy",
|
||||||
"groovy-all-minimal": "org.codehaus.groovy",
|
"groovy-eclipse-compiler": "org.codehaus.groovy",
|
||||||
"groovy-all-tests": "org.codehaus.groovy",
|
"groovy-callsite": "org.apache.groovy",
|
||||||
"groovy-ant": "org.codehaus.groovy",
|
"groovy-concurrent-java": "org.apache.groovy",
|
||||||
"groovy-astbuilder": "org.codehaus.groovy",
|
"groovy-contracts": "org.apache.groovy",
|
||||||
"groovy-backports-compat23": "org.codehaus.groovy",
|
"groovy-csv": "org.apache.groovy",
|
||||||
"groovy-binary": "org.codehaus.groovy",
|
"groovy-ginq": "org.apache.groovy",
|
||||||
"groovy-bom": "org.codehaus.groovy",
|
"groovy-grape-ivy": "org.apache.groovy",
|
||||||
"groovy-bsf": "org.codehaus.groovy",
|
"groovy-grape-maven": "org.apache.groovy",
|
||||||
"groovy-cli-commons": "org.codehaus.groovy",
|
"groovy-http-builder": "org.apache.groovy",
|
||||||
"groovy-cli-picocli": "org.codehaus.groovy",
|
"groovy-macro-library": "org.apache.groovy",
|
||||||
"groovy-console": "org.codehaus.groovy",
|
"groovy-markdown": "org.apache.groovy",
|
||||||
"groovy-datetime": "org.codehaus.groovy",
|
"groovy-reactor": "org.apache.groovy",
|
||||||
"groovy-dateutil": "org.codehaus.groovy",
|
"groovy-rxjava": "org.apache.groovy",
|
||||||
"groovy-docgenerator": "org.codehaus.groovy",
|
"groovy-test-junit6": "org.apache.groovy",
|
||||||
"groovy-eclipse-batch": "org.codehaus.groovy",
|
"groovy-toml": "org.apache.groovy",
|
||||||
"groovy-eclipse-compiler": "org.codehaus.groovy",
|
"groovy-typecheckers": "org.apache.groovy",
|
||||||
"groovy-groovydoc": "org.codehaus.groovy",
|
"kafka-clients": "org.apache.kafka",
|
||||||
"groovy-groovysh": "org.codehaus.groovy",
|
"kafka-examples": "org.apache.kafka",
|
||||||
"groovy-jaxb": "org.codehaus.groovy",
|
"kafka-group-coordinator": "org.apache.kafka",
|
||||||
"groovy-jdk14": "org.codehaus.groovy",
|
"kafka-hadoop-consumer": "org.apache.kafka",
|
||||||
"groovy-jmx": "org.codehaus.groovy",
|
"kafka-hadoop-producer": "org.apache.kafka",
|
||||||
"groovy-json": "org.codehaus.groovy",
|
"kafka-java-examples": "org.apache.kafka",
|
||||||
"groovy-json-direct": "org.codehaus.groovy",
|
"kafka-log4j-appender": "org.apache.kafka",
|
||||||
"groovy-jsr223": "org.codehaus.groovy",
|
"kafka-metadata": "org.apache.kafka",
|
||||||
"groovy-macro": "org.codehaus.groovy",
|
"kafka-perf_2.10": "org.apache.kafka",
|
||||||
"groovy-nio": "org.codehaus.groovy",
|
"kafka-perf_2.8.0": "org.apache.kafka",
|
||||||
"groovy-servlet": "org.codehaus.groovy",
|
"kafka-perf_2.8.2": "org.apache.kafka",
|
||||||
"groovy-sql": "org.codehaus.groovy",
|
"kafka-perf_2.9.1": "org.apache.kafka",
|
||||||
"groovy-swing": "org.codehaus.groovy",
|
"kafka-perf_2.9.2": "org.apache.kafka",
|
||||||
"groovy-templates": "org.codehaus.groovy",
|
"kafka-raft": "org.apache.kafka",
|
||||||
"groovy-test": "org.codehaus.groovy",
|
"kafka-server-common": "org.apache.kafka",
|
||||||
"groovy-test-junit5": "org.codehaus.groovy",
|
"kafka-shell": "org.apache.kafka",
|
||||||
"groovy-testng": "org.codehaus.groovy",
|
"kafka-storage": "org.apache.kafka",
|
||||||
"groovy-tests-vm8": "org.codehaus.groovy",
|
"kafka-storage-api": "org.apache.kafka",
|
||||||
"groovy-xml": "org.codehaus.groovy",
|
"kafka-streams": "org.apache.kafka",
|
||||||
"groovy-xmlrpc": "org.codehaus.groovy",
|
"kafka-streams-examples": "org.apache.kafka",
|
||||||
"groovy-yaml": "org.codehaus.groovy",
|
"kafka-streams-scala_2.11": "org.apache.kafka",
|
||||||
"kafka-clients": "org.apache.kafka",
|
"kafka-streams-scala_2.12": "org.apache.kafka",
|
||||||
"kafka-examples": "org.apache.kafka",
|
"kafka-streams-scala_2.13": "org.apache.kafka",
|
||||||
"kafka-group-coordinator": "org.apache.kafka",
|
"kafka-streams-test-utils": "org.apache.kafka",
|
||||||
"kafka-hadoop-consumer": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-0100": "org.apache.kafka",
|
||||||
"kafka-hadoop-producer": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-0101": "org.apache.kafka",
|
||||||
"kafka-java-examples": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-0102": "org.apache.kafka",
|
||||||
"kafka-log4j-appender": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-0110": "org.apache.kafka",
|
||||||
"kafka-metadata": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-10": "org.apache.kafka",
|
||||||
"kafka-perf_2.10": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-11": "org.apache.kafka",
|
||||||
"kafka-perf_2.8.0": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-20": "org.apache.kafka",
|
||||||
"kafka-perf_2.8.2": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-21": "org.apache.kafka",
|
||||||
"kafka-perf_2.9.1": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-22": "org.apache.kafka",
|
||||||
"kafka-perf_2.9.2": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-23": "org.apache.kafka",
|
||||||
"kafka-raft": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-24": "org.apache.kafka",
|
||||||
"kafka-server-common": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-25": "org.apache.kafka",
|
||||||
"kafka-shell": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-26": "org.apache.kafka",
|
||||||
"kafka-storage": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-27": "org.apache.kafka",
|
||||||
"kafka-storage-api": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-28": "org.apache.kafka",
|
||||||
"kafka-streams": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-30": "org.apache.kafka",
|
||||||
"kafka-streams-examples": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-31": "org.apache.kafka",
|
||||||
"kafka-streams-scala_2.11": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-32": "org.apache.kafka",
|
||||||
"kafka-streams-scala_2.12": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-33": "org.apache.kafka",
|
||||||
"kafka-streams-scala_2.13": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-34": "org.apache.kafka",
|
||||||
"kafka-streams-test-utils": "org.apache.kafka",
|
"kafka-streams-upgrade-system-tests-35": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-0100": "org.apache.kafka",
|
"kafka-tools": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-0101": "org.apache.kafka",
|
"kafka-tools-api": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-0102": "org.apache.kafka",
|
"kafka_2.10": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-0110": "org.apache.kafka",
|
"kafka_2.11": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-10": "org.apache.kafka",
|
"kafka_2.12": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-11": "org.apache.kafka",
|
"kafka_2.13": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-20": "org.apache.kafka",
|
"kafka_2.8.0": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-21": "org.apache.kafka",
|
"kafka_2.8.2": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-22": "org.apache.kafka",
|
"kafka_2.9.1": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-23": "org.apache.kafka",
|
"kafka_2.9.2": "org.apache.kafka",
|
||||||
"kafka-streams-upgrade-system-tests-24": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-25": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-26": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-27": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-28": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-30": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-31": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-32": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-33": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-34": "org.apache.kafka",
|
|
||||||
"kafka-streams-upgrade-system-tests-35": "org.apache.kafka",
|
|
||||||
"kafka-tools": "org.apache.kafka",
|
|
||||||
"kafka-tools-api": "org.apache.kafka",
|
|
||||||
"kafka_2.10": "org.apache.kafka",
|
|
||||||
"kafka_2.11": "org.apache.kafka",
|
|
||||||
"kafka_2.12": "org.apache.kafka",
|
|
||||||
"kafka_2.13": "org.apache.kafka",
|
|
||||||
"kafka_2.8.0": "org.apache.kafka",
|
|
||||||
"kafka_2.8.2": "org.apache.kafka",
|
|
||||||
"kafka_2.9.1": "org.apache.kafka",
|
|
||||||
"kafka_2.9.2": "org.apache.kafka",
|
|
||||||
|
|
||||||
// legacy "Jackson 1.x" (aka "jackson-asl") artifacts predate the convention of embedding
|
// legacy "Jackson 1.x" (aka "jackson-asl") artifacts predate the convention of embedding
|
||||||
// META-INF/maven/.../pom.properties in the jar, so groupIDFromKnownPackageList is the only
|
// META-INF/maven/.../pom.properties in the jar, so ArtifactIDToGroupID is the only
|
||||||
// way to recover the correct group ID for jars built without that metadata (e.g. Ant-built
|
// way to recover the correct group ID for jars built without that metadata (e.g. Ant-built
|
||||||
// jars from before ~2014). Without this, the group ID falls back to the artifact name itself,
|
// jars from before ~2014). Without this, the group ID falls back to the artifact name itself,
|
||||||
// producing purls that do not match the vulnerability database's namespace.
|
// producing purls that do not match the vulnerability database's namespace.
|
||||||
@@ -1940,3 +1926,83 @@ var DefaultArtifactIDToGroupID = map[string]string{
|
|||||||
"jackson-xc": "org.codehaus.jackson",
|
"jackson-xc": "org.codehaus.jackson",
|
||||||
"jackson-smile": "org.codehaus.jackson",
|
"jackson-smile": "org.codehaus.jackson",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// groupIDMigration describes an artifact that moved from one group ID to another at a major version.
|
||||||
|
type groupIDMigration struct {
|
||||||
|
before, after string
|
||||||
|
atMajor int
|
||||||
|
}
|
||||||
|
|
||||||
|
// groovy moved from org.codehaus.groovy to org.apache.groovy at 4.0
|
||||||
|
var groovyMigration = groupIDMigration{before: "org.codehaus.groovy", after: "org.apache.groovy", atMajor: 4}
|
||||||
|
|
||||||
|
// migratedArtifactIDToGroupID holds artifacts whose group ID depends on the version. It takes precedence over
|
||||||
|
// defaultArtifactIDToGroupID; use ArtifactIDToGroupID rather than reading it directly.
|
||||||
|
var migratedArtifactIDToGroupID = map[string]groupIDMigration{
|
||||||
|
// groovy artifacts published under both coordinates. Without these, jars that ship no pom metadata get a
|
||||||
|
// stale codehaus group or one guessed from the per-module Automatic-Module-Name (e.g. org.apache.groovy.json).
|
||||||
|
// See https://github.com/anchore/syft/issues/5311
|
||||||
|
"groovy": groovyMigration,
|
||||||
|
"groovy-all": groovyMigration,
|
||||||
|
"groovy-all-jdk14": groovyMigration,
|
||||||
|
"groovy-all-minimal": groovyMigration,
|
||||||
|
"groovy-all-tests": groovyMigration,
|
||||||
|
"groovy-ant": groovyMigration,
|
||||||
|
"groovy-astbuilder": groovyMigration,
|
||||||
|
"groovy-backports-compat23": groovyMigration,
|
||||||
|
"groovy-binary": groovyMigration,
|
||||||
|
"groovy-bom": groovyMigration,
|
||||||
|
"groovy-bsf": groovyMigration,
|
||||||
|
"groovy-cli-commons": groovyMigration,
|
||||||
|
"groovy-cli-picocli": groovyMigration,
|
||||||
|
"groovy-console": groovyMigration,
|
||||||
|
"groovy-datetime": groovyMigration,
|
||||||
|
"groovy-dateutil": groovyMigration,
|
||||||
|
"groovy-docgenerator": groovyMigration,
|
||||||
|
"groovy-groovydoc": groovyMigration,
|
||||||
|
"groovy-groovysh": groovyMigration,
|
||||||
|
"groovy-jaxb": groovyMigration,
|
||||||
|
"groovy-jdk14": groovyMigration,
|
||||||
|
"groovy-jmx": groovyMigration,
|
||||||
|
"groovy-json": groovyMigration,
|
||||||
|
"groovy-json-direct": groovyMigration,
|
||||||
|
"groovy-jsr223": groovyMigration,
|
||||||
|
"groovy-macro": groovyMigration,
|
||||||
|
"groovy-nio": groovyMigration,
|
||||||
|
"groovy-servlet": groovyMigration,
|
||||||
|
"groovy-sql": groovyMigration,
|
||||||
|
"groovy-swing": groovyMigration,
|
||||||
|
"groovy-templates": groovyMigration,
|
||||||
|
"groovy-test": groovyMigration,
|
||||||
|
"groovy-test-junit5": groovyMigration,
|
||||||
|
"groovy-testng": groovyMigration,
|
||||||
|
"groovy-tests-vm8": groovyMigration,
|
||||||
|
"groovy-xml": groovyMigration,
|
||||||
|
"groovy-xmlrpc": groovyMigration,
|
||||||
|
"groovy-yaml": groovyMigration,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArtifactIDToGroupID returns the known group ID for the given artifact ID at the given version. For migrated
|
||||||
|
// artifacts an empty or unparseable version resolves to the pre-migration group ID.
|
||||||
|
func ArtifactIDToGroupID(artifactID, version string) (string, bool) {
|
||||||
|
if m, ok := migratedArtifactIDToGroupID[artifactID]; ok {
|
||||||
|
if majorVersion(version) >= m.atMajor {
|
||||||
|
return m.after, true
|
||||||
|
}
|
||||||
|
return m.before, true
|
||||||
|
}
|
||||||
|
groupID, ok := defaultArtifactIDToGroupID[artifactID]
|
||||||
|
return groupID, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// majorVersion returns the leading numeric component of a version (e.g. "4.0.33" and "4-SNAPSHOT"
|
||||||
|
// resolve to 4), or 0 when the version does not start with a number.
|
||||||
|
func majorVersion(version string) int {
|
||||||
|
leading, _, _ := strings.Cut(version, ".")
|
||||||
|
leading, _, _ = strings.Cut(leading, "-")
|
||||||
|
n, err := strconv.Atoi(leading)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package cpegenerate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestArtifactIDToGroupID(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
artifactID string
|
||||||
|
version string
|
||||||
|
want string
|
||||||
|
wantOK bool
|
||||||
|
}{
|
||||||
|
{name: "unconditional entry", artifactID: "ant-antlr", version: "1.10.0", want: "org.apache.ant", wantOK: true},
|
||||||
|
{name: "unknown artifact", artifactID: "not-a-real-artifact", version: "1.0.0"},
|
||||||
|
{name: "groovy 4 migrates to apache", artifactID: "groovy", version: "4.0.33", want: "org.apache.groovy", wantOK: true},
|
||||||
|
{name: "groovy 3 stays on codehaus", artifactID: "groovy", version: "3.0.22", want: "org.codehaus.groovy", wantOK: true},
|
||||||
|
{name: "groovy without a version falls back to codehaus", artifactID: "groovy-json", want: "org.codehaus.groovy", wantOK: true},
|
||||||
|
{name: "groovy-eclipse stays on codehaus at 4.x", artifactID: "groovy-eclipse-batch", version: "4.0.33-02", want: "org.codehaus.groovy", wantOK: true},
|
||||||
|
{name: "apache-only groovy module", artifactID: "groovy-ginq", version: "4.0.33", want: "org.apache.groovy", wantOK: true},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got, ok := ArtifactIDToGroupID(tt.artifactID, tt.version)
|
||||||
|
assert.Equal(t, tt.wantOK, ok)
|
||||||
|
assert.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_majorVersion(t *testing.T) {
|
||||||
|
tests := map[string]int{
|
||||||
|
"4.0.33": 4,
|
||||||
|
"4.0.0-alpha-1": 4,
|
||||||
|
"4-SNAPSHOT": 4,
|
||||||
|
"4": 4,
|
||||||
|
"3.0.22": 3,
|
||||||
|
"v4.0.33": 0,
|
||||||
|
"": 0,
|
||||||
|
}
|
||||||
|
for version, expect := range tests {
|
||||||
|
t.Run(version, func(t *testing.T) {
|
||||||
|
assert.Equal(t, expect, majorVersion(version))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -400,6 +400,16 @@ func Test_groupIDsFromJavaPackage(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expects: nil,
|
expects: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// pom-less groovy 4 jars (all real ones) must resolve to the apache group, same as the purl
|
||||||
|
name: "groovy 4 without pom metadata",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "groovy",
|
||||||
|
Version: "4.0.33",
|
||||||
|
Metadata: pkg.JavaArchive{Manifest: &pkg.JavaManifest{}},
|
||||||
|
},
|
||||||
|
expects: []string{"org.apache.groovy"},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "no manifest or pom info",
|
name: "no manifest or pom info",
|
||||||
pkg: pkg.Package{
|
pkg: pkg.Package{
|
||||||
@@ -562,7 +572,7 @@ func Test_groupIDsFromJavaManifest(t *testing.T) {
|
|||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
got := groupIDsFromJavaManifest(test.name, &test.manifest)
|
got := groupIDsFromJavaManifest(test.name, "", &test.manifest)
|
||||||
require.Equal(t, test.expected, got)
|
require.Equal(t, test.expected, got)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest
|
|||||||
// findLicenseFromJavaMetadata attempts to find license information from all available maven metadata properties and pom info
|
// findLicenseFromJavaMetadata attempts to find license information from all available maven metadata properties and pom info
|
||||||
func (j *archiveParser) findLicenseFromJavaMetadata(ctx context.Context, groupID, artifactID, version string, parsedPom *parsedPomProject, manifest *pkg.JavaManifest) []pkg.License {
|
func (j *archiveParser) findLicenseFromJavaMetadata(ctx context.Context, groupID, artifactID, version string, parsedPom *parsedPomProject, manifest *pkg.JavaManifest) []pkg.License {
|
||||||
if groupID == "" {
|
if groupID == "" {
|
||||||
if gID := groupIDFromJavaMetadata(artifactID, pkg.JavaArchive{Manifest: manifest}); gID != "" {
|
if gID := groupIDFromJavaMetadata(artifactID, version, pkg.JavaArchive{Manifest: manifest}); gID != "" {
|
||||||
groupID = gID
|
groupID = gID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -786,7 +786,7 @@ func newPackageFromMavenData(ctx context.Context, r *maven.Resolver, pomProperti
|
|||||||
vPathSuffix := ""
|
vPathSuffix := ""
|
||||||
groupID := ""
|
groupID := ""
|
||||||
if parentMetadata, ok := parentPkg.Metadata.(pkg.JavaArchive); ok {
|
if parentMetadata, ok := parentPkg.Metadata.(pkg.JavaArchive); ok {
|
||||||
groupID = groupIDFromJavaMetadata(parentPkg.Name, parentMetadata)
|
groupID = groupIDFromJavaMetadata(parentPkg.Name, parentPkg.Version, parentMetadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
parentKey := fmt.Sprintf("%s:%s:%s", groupID, parentPkg.Name, parentPkg.Version)
|
parentKey := fmt.Sprintf("%s:%s:%s", groupID, parentPkg.Name, parentPkg.Version)
|
||||||
@@ -863,8 +863,8 @@ func packageIdentitiesMatch(p pkg.Package, parentPkg *pkg.Package) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to determine identity with the metadata
|
// try to determine identity with the metadata
|
||||||
groupID := groupIDFromJavaMetadata(p.Name, metadata)
|
groupID := groupIDFromJavaMetadata(p.Name, p.Version, metadata)
|
||||||
parentGroupID := groupIDFromJavaMetadata(parentPkg.Name, parentMetadata)
|
parentGroupID := groupIDFromJavaMetadata(parentPkg.Name, parentPkg.Version, parentMetadata)
|
||||||
if uniquePkgKey(groupID, &p) == uniquePkgKey(parentGroupID, parentPkg) {
|
if uniquePkgKey(groupID, &p) == uniquePkgKey(parentGroupID, parentPkg) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
func packageURL(name, version string, metadata pkg.JavaArchive) string {
|
func packageURL(name, version string, metadata pkg.JavaArchive) string {
|
||||||
var groupID = name
|
var groupID = name
|
||||||
|
|
||||||
if gID := groupIDFromJavaMetadata(name, metadata); gID != "" {
|
if gID := groupIDFromJavaMetadata(name, version, metadata); gID != "" {
|
||||||
groupID = gID
|
groupID = gID
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ func packageURL(name, version string, metadata pkg.JavaArchive) string {
|
|||||||
// The order of precedence is:
|
// The order of precedence is:
|
||||||
// 1. The group ID from the POM properties
|
// 1. The group ID from the POM properties
|
||||||
// 2. The group ID from the POM project
|
// 2. The group ID from the POM project
|
||||||
// 3. The group ID from a select map of known group IDs
|
// 3. The group ID from a select map of known group IDs (see cpegenerate.ArtifactIDToGroupID)
|
||||||
// 4. The group ID from the Java manifest
|
// 4. The group ID from the Java manifest
|
||||||
func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaArchive) (groupID string) {
|
func groupIDFromJavaMetadata(pkgName, version string, metadata pkg.JavaArchive) (groupID string) {
|
||||||
if groupID = groupIDFromPomProperties(metadata.PomProperties); groupID != "" {
|
if groupID = groupIDFromPomProperties(metadata.PomProperties); groupID != "" {
|
||||||
return groupID
|
return groupID
|
||||||
}
|
}
|
||||||
@@ -41,8 +41,8 @@ func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaArchive) (groupID
|
|||||||
return groupID
|
return groupID
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupID = groupIDFromKnownPackageList(pkgName); groupID != "" {
|
if gID, ok := cpegenerate.ArtifactIDToGroupID(pkgName, version); ok {
|
||||||
return groupID
|
return gID
|
||||||
}
|
}
|
||||||
|
|
||||||
if groupID = groupIDFromJavaManifest(metadata.Manifest); groupID != "" {
|
if groupID = groupIDFromJavaManifest(metadata.Manifest); groupID != "" {
|
||||||
@@ -52,13 +52,6 @@ func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaArchive) (groupID
|
|||||||
return groupID
|
return groupID
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupIDFromKnownPackageList(pkgName string) (groupID string) {
|
|
||||||
if groupID, ok := cpegenerate.DefaultArtifactIDToGroupID[pkgName]; ok {
|
|
||||||
return groupID
|
|
||||||
}
|
|
||||||
return groupID
|
|
||||||
}
|
|
||||||
|
|
||||||
func groupIDFromJavaManifest(manifest *pkg.JavaManifest) (groupID string) {
|
func groupIDFromJavaManifest(manifest *pkg.JavaManifest) (groupID string) {
|
||||||
if manifest == nil {
|
if manifest == nil {
|
||||||
return groupID
|
return groupID
|
||||||
|
|||||||
@@ -132,6 +132,26 @@ func Test_packageURL(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expect: "pkg:maven/parent/example-java-app-maven@0.1.0",
|
expect: "pkg:maven/parent/example-java-app-maven@0.1.0",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// regression for github.com/anchore/syft/issues/5311
|
||||||
|
name: "groovy 4 without pom metadata",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "groovy",
|
||||||
|
Version: "4.0.33",
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg,
|
||||||
|
Metadata: pkg.JavaArchive{
|
||||||
|
VirtualPath: "groovy-4.0.33.jar",
|
||||||
|
Manifest: &pkg.JavaManifest{
|
||||||
|
Main: []pkg.KeyValue{
|
||||||
|
{Key: "Bundle-SymbolicName", Value: "groovy"},
|
||||||
|
{Key: "Automatic-Module-Name", Value: "org.apache.groovy.core"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: "pkg:maven/org.apache.groovy/groovy@4.0.33",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.expect, func(t *testing.T) {
|
t.Run(tt.expect, func(t *testing.T) {
|
||||||
@@ -144,6 +164,7 @@ func Test_groupIDFromJavaMetadata(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
pkgName string
|
pkgName string
|
||||||
|
version string
|
||||||
metadata pkg.JavaArchive
|
metadata pkg.JavaArchive
|
||||||
expect string
|
expect string
|
||||||
}{
|
}{
|
||||||
@@ -218,6 +239,38 @@ func Test_groupIDFromJavaMetadata(t *testing.T) {
|
|||||||
metadata: pkg.JavaArchive{},
|
metadata: pkg.JavaArchive{},
|
||||||
expect: "org.apache.derby",
|
expect: "org.apache.derby",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// regression for github.com/anchore/syft/issues/5311: groovy 4+ moved to org.apache.groovy, but
|
||||||
|
// jars without pom metadata fall through to the known package list, which keeps the codehaus-era
|
||||||
|
// coordinates. The map must also beat the per-module Automatic-Module-Name in the manifest.
|
||||||
|
name: "known package list groovy 4 beats the manifest",
|
||||||
|
pkgName: "groovy",
|
||||||
|
version: "4.0.33",
|
||||||
|
metadata: pkg.JavaArchive{
|
||||||
|
Manifest: &pkg.JavaManifest{
|
||||||
|
Main: []pkg.KeyValue{
|
||||||
|
{Key: "Bundle-SymbolicName", Value: "groovy"},
|
||||||
|
{Key: "Automatic-Module-Name", Value: "org.apache.groovy.core"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: "org.apache.groovy",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// modules only published under the apache coordinates would otherwise take the group ID
|
||||||
|
// from the per-module Automatic-Module-Name (org.apache.groovy.ginq)
|
||||||
|
name: "known package list apache-only groovy module",
|
||||||
|
pkgName: "groovy-ginq",
|
||||||
|
version: "4.0.33",
|
||||||
|
metadata: pkg.JavaArchive{
|
||||||
|
Manifest: &pkg.JavaManifest{
|
||||||
|
Main: []pkg.KeyValue{
|
||||||
|
{Key: "Automatic-Module-Name", Value: "org.apache.groovy.ginq"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expect: "org.apache.groovy",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "java manifest",
|
name: "java manifest",
|
||||||
metadata: pkg.JavaArchive{
|
metadata: pkg.JavaArchive{
|
||||||
@@ -241,7 +294,7 @@ func Test_groupIDFromJavaMetadata(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
assert.Equal(t, tt.expect, groupIDFromJavaMetadata(tt.pkgName, tt.metadata))
|
assert.Equal(t, tt.expect, groupIDFromJavaMetadata(tt.pkgName, tt.version, tt.metadata))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user