Identify Jenkins plugin upstream of CPE generation

Signed-off-by: Dan Luhring <dan.luhring@anchore.com>
This commit is contained in:
Dan Luhring 2021-04-20 19:25:19 -04:00
parent fa7fd718cb
commit 33e6be0b74
No known key found for this signature in database
GPG Key ID: 9CEE23D079426CEF
6 changed files with 176 additions and 32 deletions

View File

@ -54,9 +54,6 @@ func (s candidateStore) getCandidates(t pkg.Type, key string) []string {
return value return value
} }
const pomPropertiesGroupIDJenkinsPlugins = "com.cloudbees.jenkins.plugins"
const pomPropertiesGroupIDJiraPlugins = "com.atlassian.jira.plugins"
func newCPE(product, vendor, version, targetSW string) wfn.Attributes { func newCPE(product, vendor, version, targetSW string) wfn.Attributes {
cpe := *(wfn.NewAttributesWithAny()) cpe := *(wfn.NewAttributesWithAny())
cpe.Part = "a" cpe.Part = "a"
@ -115,22 +112,9 @@ func candidateTargetSoftwareAttrs(p pkg.Package) []string {
return targetSw return targetSw
} }
func isJenkinsPlugin(p pkg.Package) bool {
if p.Type == pkg.JenkinsPluginPkg {
return true
}
if groupID := groupIDFromPomProperties(p); groupID == pomPropertiesGroupIDJenkinsPlugins {
return true
}
return false
}
func candidateTargetSoftwareAttrsForJava(p pkg.Package) []string { func candidateTargetSoftwareAttrsForJava(p pkg.Package) []string {
// Use the more specific indicator if available // Use the more specific indicator if available
if p.Type == pkg.JenkinsPluginPkg {
if isJenkinsPlugin(p) {
return []string{"jenkins", "cloudbees_jenkins"} return []string{"jenkins", "cloudbees_jenkins"}
} }
@ -217,8 +201,8 @@ func shouldConsiderGroupID(groupID string) bool {
} }
excludedGroupIDs := []string{ excludedGroupIDs := []string{
pomPropertiesGroupIDJiraPlugins, pkg.PomPropertiesGroupIDJiraPlugins,
pomPropertiesGroupIDJenkinsPlugins, pkg.PomPropertiesGroupIDJenkinsPlugins,
} }
for _, excludedGroupID := range excludedGroupIDs { for _, excludedGroupID := range excludedGroupIDs {

View File

@ -156,7 +156,7 @@ func TestGeneratePackageCPEs(t *testing.T) {
Version: "3.2", Version: "3.2",
FoundBy: "some-analyzer", FoundBy: "some-analyzer",
Language: pkg.Java, Language: pkg.Java,
Type: pkg.JavaPkg, Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
PomProperties: &pkg.PomProperties{ PomProperties: &pkg.PomProperties{
GroupID: "com.cloudbees.jenkins.plugins", GroupID: "com.cloudbees.jenkins.plugins",

View File

@ -186,14 +186,14 @@ func (j *archiveParser) discoverPkgsFromAllPomProperties(parentPkg *pkg.Package)
continue continue
} }
pkgs = append(pkgs, j.packagesFromPomProperties(pomProperties, parentPkg)...) pkgs = append(pkgs, j.packagesFromPomProperties(*pomProperties, parentPkg)...)
} }
return pkgs, nil return pkgs, nil
} }
// packagesFromPomProperties processes a single Maven POM properties for a given parent package, returning all listed Java packages found and // packagesFromPomProperties processes a single Maven POM properties for a given parent package, returning all listed Java packages found and
// associating each discovered package to the given parent package. // associating each discovered package to the given parent package.
func (j *archiveParser) packagesFromPomProperties(pomProperties *pkg.PomProperties, parentPkg *pkg.Package) []pkg.Package { func (j *archiveParser) packagesFromPomProperties(pomProperties pkg.PomProperties, parentPkg *pkg.Package) []pkg.Package {
// keep the artifact name within the virtual path if this package does not match the parent package // keep the artifact name within the virtual path if this package does not match the parent package
vPathSuffix := "" vPathSuffix := ""
if !strings.HasPrefix(pomProperties.ArtifactID, parentPkg.Name) { if !strings.HasPrefix(pomProperties.ArtifactID, parentPkg.Name) {
@ -206,11 +206,11 @@ func (j *archiveParser) packagesFromPomProperties(pomProperties *pkg.PomProperti
Name: pomProperties.ArtifactID, Name: pomProperties.ArtifactID,
Version: pomProperties.Version, Version: pomProperties.Version,
Language: pkg.Java, Language: pkg.Java,
Type: pkg.JavaPkg, Type: pomProperties.PkgTypeIndicated(),
MetadataType: pkg.JavaMetadataType, MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath, VirtualPath: virtualPath,
PomProperties: pomProperties, PomProperties: &pomProperties,
Parent: parentPkg, Parent: parentPkg,
}, },
} }
@ -243,10 +243,13 @@ func (j *archiveParser) packagesFromPomProperties(pomProperties *pkg.PomProperti
parentPkg.Version = p.Version parentPkg.Version = p.Version
} }
// We may have learned more about the type via data in the pom properties
parentPkg.Type = p.Type
// keep the pom properties, but don't overwrite existing pom properties // keep the pom properties, but don't overwrite existing pom properties
parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata) parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata)
if ok && parentMetadata.PomProperties == nil { if ok && parentMetadata.PomProperties == nil {
parentMetadata.PomProperties = pomProperties parentMetadata.PomProperties = &pomProperties
parentPkg.Metadata = parentMetadata parentPkg.Metadata = parentMetadata
} }

View File

@ -629,12 +629,12 @@ func TestPackagesFromPomProperties(t *testing.T) {
}, },
}, },
{ {
name: "child matches parent by key", name: "single package from pom properties that's a Jenkins plugin",
props: &pkg.PomProperties{ props: &pkg.PomProperties{
Name: "some-name", Name: "some-name",
GroupID: "some-group-id", GroupID: "com.cloudbees.jenkins.plugins",
ArtifactID: "some-parent-name", // note: matches parent package ArtifactID: "some-artifact-id",
Version: "2.0", // note: matches parent package Version: "1.0",
}, },
parent: &pkg.Package{ parent: &pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
@ -650,6 +650,66 @@ func TestPackagesFromPomProperties(t *testing.T) {
expectedParent: pkg.Package{ expectedParent: pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Metadata: pkg.JavaMetadata{
VirtualPath: "some-parent-virtual-path",
Manifest: nil,
PomProperties: nil,
Parent: nil,
},
},
expectedPackages: []pkg.Package{
{
Name: "some-artifact-id",
Version: "1.0",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
MetadataType: pkg.JavaMetadataType,
Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":" + "some-artifact-id",
PomProperties: &pkg.PomProperties{
Name: "some-name",
GroupID: "com.cloudbees.jenkins.plugins",
ArtifactID: "some-artifact-id",
Version: "1.0",
},
Parent: &pkg.Package{
Name: "some-parent-name",
Version: "2.0",
Metadata: pkg.JavaMetadata{
VirtualPath: "some-parent-virtual-path",
Manifest: nil,
PomProperties: nil,
Parent: nil,
},
},
},
},
},
},
{
name: "child matches parent by key",
props: &pkg.PomProperties{
Name: "some-name",
GroupID: "some-group-id",
ArtifactID: "some-parent-name", // note: matches parent package
Version: "2.0", // note: matches parent package
},
parent: &pkg.Package{
Name: "some-parent-name",
Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{
VirtualPath: "some-parent-virtual-path",
Manifest: nil,
PomProperties: nil,
Parent: nil,
},
},
// note: the SAME as the original parent values
expectedParent: pkg.Package{
Name: "some-parent-name",
Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: "some-parent-virtual-path", VirtualPath: "some-parent-virtual-path",
Manifest: nil, Manifest: nil,
@ -665,6 +725,44 @@ func TestPackagesFromPomProperties(t *testing.T) {
}, },
expectedPackages: nil, expectedPackages: nil,
}, },
{
name: "child matches parent by key and is Jenkins plugin",
props: &pkg.PomProperties{
Name: "some-name",
GroupID: "com.cloudbees.jenkins.plugins",
ArtifactID: "some-parent-name", // note: matches parent package
Version: "2.0", // note: matches parent package
},
parent: &pkg.Package{
Name: "some-parent-name",
Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{
VirtualPath: "some-parent-virtual-path",
Manifest: nil,
PomProperties: nil,
Parent: nil,
},
},
expectedParent: pkg.Package{
Name: "some-parent-name",
Version: "2.0",
Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaMetadata{
VirtualPath: "some-parent-virtual-path",
Manifest: nil,
// note: we attach the discovered pom properties data
PomProperties: &pkg.PomProperties{
Name: "some-name",
GroupID: "com.cloudbees.jenkins.plugins",
ArtifactID: "some-parent-name", // note: matches parent package
Version: "2.0", // note: matches parent package
},
Parent: nil,
},
},
expectedPackages: nil,
},
{ {
name: "child matches parent by virtual path", name: "child matches parent by virtual path",
props: &pkg.PomProperties{ props: &pkg.PomProperties{
@ -676,6 +774,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
parent: &pkg.Package{ parent: &pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path
Manifest: nil, Manifest: nil,
@ -686,6 +785,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
expectedParent: pkg.Package{ expectedParent: pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path
Manifest: nil, Manifest: nil,
@ -712,6 +812,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
parent: &pkg.Package{ parent: &pkg.Package{
Name: "", // note: empty Name: "", // note: empty
Version: "", // note: empty Version: "", // note: empty
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path
Manifest: nil, Manifest: nil,
@ -722,6 +823,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
expectedParent: pkg.Package{ expectedParent: pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "3.0", Version: "3.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path
Manifest: nil, Manifest: nil,
@ -748,11 +850,12 @@ func TestPackagesFromPomProperties(t *testing.T) {
parent: &pkg.Package{ parent: &pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path
Manifest: nil, Manifest: nil,
PomProperties: &pkg.PomProperties{ PomProperties: &pkg.PomProperties{
Name: "EXISTS", //note: this already exists and should not be overridden Name: "EXISTS", // note: this already exists and should not be overridden
}, },
Parent: nil, Parent: nil,
}, },
@ -760,12 +863,13 @@ func TestPackagesFromPomProperties(t *testing.T) {
expectedParent: pkg.Package{ expectedParent: pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path VirtualPath: virtualPath + ":some-parent-name", // note: matching virtual path
Manifest: nil, Manifest: nil,
// note: we attach the discovered pom properties data // note: we attach the discovered pom properties data
PomProperties: &pkg.PomProperties{ PomProperties: &pkg.PomProperties{
Name: "EXISTS", //note: this already exists and should not be overridden Name: "EXISTS", // note: this already exists and should not be overridden
}, },
Parent: nil, Parent: nil,
}, },
@ -783,6 +887,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
parent: &pkg.Package{ parent: &pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":NEW_VIRTUAL_PATH", // note: DOES NOT match the existing virtual path VirtualPath: virtualPath + ":NEW_VIRTUAL_PATH", // note: DOES NOT match the existing virtual path
Manifest: nil, Manifest: nil,
@ -794,6 +899,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
expectedParent: pkg.Package{ expectedParent: pkg.Package{
Name: "some-parent-name", Name: "some-parent-name",
Version: "2.0", Version: "2.0",
Type: pkg.JavaPkg,
Metadata: pkg.JavaMetadata{ Metadata: pkg.JavaMetadata{
VirtualPath: virtualPath + ":NEW_VIRTUAL_PATH", VirtualPath: virtualPath + ":NEW_VIRTUAL_PATH",
Manifest: nil, Manifest: nil,
@ -823,7 +929,7 @@ func TestPackagesFromPomProperties(t *testing.T) {
t.Cleanup(cleanup) t.Cleanup(cleanup)
// get the test data // get the test data
actualPackages := parser.packagesFromPomProperties(test.props, test.parent) actualPackages := parser.packagesFromPomProperties(*test.props, test.parent)
assert.Equal(t, test.expectedPackages, actualPackages) assert.Equal(t, test.expectedPackages, actualPackages)
assert.Equal(t, test.expectedParent, *test.parent) assert.Equal(t, test.expectedParent, *test.parent)
}) })

View File

@ -20,6 +20,15 @@ type PomProperties struct {
Extra map[string]string `mapstructure:",remain" json:"extraFields"` Extra map[string]string `mapstructure:",remain" json:"extraFields"`
} }
// PkgTypeIndicated returns the package Type indicated by the data contained in the PomProperties.
func (p PomProperties) PkgTypeIndicated() Type {
if p.GroupID == PomPropertiesGroupIDJenkinsPlugins {
return JenkinsPluginPkg
}
return JavaPkg
}
// JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file. // JavaManifest represents the fields of interest extracted from a Java archive's META-INF/MANIFEST.MF file.
type JavaManifest struct { type JavaManifest struct {
Main map[string]string `json:"main,omitempty"` Main map[string]string `json:"main,omitempty"`
@ -43,3 +52,6 @@ func (m JavaMetadata) PackageURL() string {
return "" return ""
} }
const PomPropertiesGroupIDJenkinsPlugins = "com.cloudbees.jenkins.plugins"
const PomPropertiesGroupIDJiraPlugins = "com.atlassian.jira.plugins"

View File

@ -2,9 +2,48 @@ package pkg
import ( import (
"github.com/sergi/go-diff/diffmatchpatch" "github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
"testing" "testing"
) )
func TestPomProperties_PkgTypeIndicated(t *testing.T) {
cases := []struct {
name string
pomProperties PomProperties
expectedType Type
}{
{
name: "regular Java package",
pomProperties: PomProperties{
Path: "some path",
Name: "some name",
GroupID: "some group ID",
ArtifactID: "some artifact ID",
Version: "1",
},
expectedType: JavaPkg,
},
{
name: "jenkins plugin",
pomProperties: PomProperties{
Path: "some path",
Name: "some name",
GroupID: "com.cloudbees.jenkins.plugins",
ArtifactID: "some artifact ID",
Version: "1",
},
expectedType: JenkinsPluginPkg,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
actual := tc.pomProperties.PkgTypeIndicated()
assert.Equal(t, tc.expectedType, actual)
})
}
}
func TestJavaMetadata_pURL(t *testing.T) { func TestJavaMetadata_pURL(t *testing.T) {
tests := []struct { tests := []struct {
metadata JavaMetadata metadata JavaMetadata