mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 17:03:17 +01:00
Improve CPE generation for Jenkins/Jira plugins
Signed-off-by: Dan Luhring <dan.luhring@anchore.com>
This commit is contained in:
parent
b301b56db1
commit
091fd1f0b0
@ -54,6 +54,9 @@ 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"
|
||||||
@ -100,7 +103,7 @@ func candidateTargetSoftwareAttrs(p pkg.Package) []string {
|
|||||||
var targetSw []string
|
var targetSw []string
|
||||||
switch p.Language {
|
switch p.Language {
|
||||||
case pkg.Java:
|
case pkg.Java:
|
||||||
targetSw = append(targetSw, "java", "maven")
|
targetSw = append(targetSw, candidateTargetSoftwareAttrsForJava(p)...)
|
||||||
case pkg.JavaScript:
|
case pkg.JavaScript:
|
||||||
targetSw = append(targetSw, "node.js", "nodejs")
|
targetSw = append(targetSw, "node.js", "nodejs")
|
||||||
case pkg.Ruby:
|
case pkg.Ruby:
|
||||||
@ -109,51 +112,130 @@ func candidateTargetSoftwareAttrs(p pkg.Package) []string {
|
|||||||
targetSw = append(targetSw, "python")
|
targetSw = append(targetSw, "python")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Type == pkg.JenkinsPluginPkg {
|
|
||||||
targetSw = append(targetSw, "jenkins", "cloudbees_jenkins")
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
||||||
|
// Use the more specific indicator if available
|
||||||
|
|
||||||
|
if isJenkinsPlugin(p) {
|
||||||
|
return []string{"jenkins", "cloudbees_jenkins"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []string{"java", "maven"}
|
||||||
|
}
|
||||||
|
|
||||||
func candidateVendors(p pkg.Package) []string {
|
func candidateVendors(p pkg.Package) []string {
|
||||||
|
// TODO: Confirm whether using products as vendors is helpful to the matching process
|
||||||
vendors := candidateProducts(p)
|
vendors := candidateProducts(p)
|
||||||
|
|
||||||
switch p.Language {
|
switch p.Language {
|
||||||
case pkg.Python:
|
case pkg.Python:
|
||||||
vendors = append(vendors, fmt.Sprintf("python-%s", p.Name))
|
vendors = append(vendors, fmt.Sprintf("python-%s", p.Name))
|
||||||
case pkg.Java:
|
case pkg.Java:
|
||||||
if p.MetadataType == pkg.JavaMetadataType {
|
if p.MetadataType == pkg.JavaMetadataType {
|
||||||
if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok && metadata.PomProperties != nil {
|
vendors = append(vendors, candidateVendorsForJava(p)...)
|
||||||
// derive the vendor from the groupID (e.g. org.sonatype.nexus --> sonatype)
|
|
||||||
if strings.HasPrefix(metadata.PomProperties.GroupID, "org.") || strings.HasPrefix(metadata.PomProperties.GroupID, "com.") {
|
|
||||||
fields := strings.Split(metadata.PomProperties.GroupID, ".")
|
|
||||||
if len(fields) >= 3 {
|
|
||||||
vendors = append(vendors, fields[1])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vendors
|
return vendors
|
||||||
}
|
}
|
||||||
|
|
||||||
func candidateProducts(p pkg.Package) []string {
|
func candidateProducts(p pkg.Package) []string {
|
||||||
var products = []string{p.Name}
|
products := []string{p.Name}
|
||||||
|
|
||||||
if p.Language == pkg.Java {
|
if p.Language == pkg.Java {
|
||||||
if p.MetadataType == pkg.JavaMetadataType {
|
products = append(products, candidateProductsForJava(p)...)
|
||||||
if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok && metadata.PomProperties != nil {
|
|
||||||
// derive the product from the groupID (e.g. org.sonatype.nexus --> nexus)
|
|
||||||
if strings.HasPrefix(metadata.PomProperties.GroupID, "org.") || strings.HasPrefix(metadata.PomProperties.GroupID, "com.") {
|
|
||||||
fields := strings.Split(metadata.PomProperties.GroupID, ".")
|
|
||||||
if len(fields) >= 3 {
|
|
||||||
products = append(products, fields[2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return any known product name swaps prepended to the results
|
// return any known product name swaps prepended to the results
|
||||||
return append(productCandidatesByPkgType.getCandidates(p.Type, p.Name), products...)
|
return append(productCandidatesByPkgType.getCandidates(p.Type, p.Name), products...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func candidateProductsForJava(p pkg.Package) []string {
|
||||||
|
if product, _ := productAndVendorFromPomPropertiesGroupID(p); product != "" {
|
||||||
|
return []string{product}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func candidateVendorsForJava(p pkg.Package) []string {
|
||||||
|
if _, vendor := productAndVendorFromPomPropertiesGroupID(p); vendor != "" {
|
||||||
|
return []string{vendor}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func productAndVendorFromPomPropertiesGroupID(p pkg.Package) (string, string) {
|
||||||
|
groupID := groupIDFromPomProperties(p)
|
||||||
|
if !shouldConsiderGroupID(groupID) {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasAnyOfPrefixes(groupID, "com", "org") {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Split(groupID, ".")
|
||||||
|
if len(fields) < 3 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
product := fields[2]
|
||||||
|
vendor := fields[1]
|
||||||
|
return product, vendor
|
||||||
|
}
|
||||||
|
|
||||||
|
func groupIDFromPomProperties(p pkg.Package) string {
|
||||||
|
metadata, ok := p.Metadata.(pkg.JavaMetadata)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if metadata.PomProperties == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return metadata.PomProperties.GroupID
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldConsiderGroupID(groupID string) bool {
|
||||||
|
if groupID == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
excludedGroupIDs := []string{
|
||||||
|
pomPropertiesGroupIDJiraPlugins,
|
||||||
|
pomPropertiesGroupIDJenkinsPlugins,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, excludedGroupID := range excludedGroupIDs {
|
||||||
|
if groupID == excludedGroupID {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasAnyOfPrefixes(input string, prefixes ...string) bool {
|
||||||
|
for _, prefix := range prefixes {
|
||||||
|
if strings.HasPrefix(input, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerate(t *testing.T) {
|
func TestGeneratePackageCPEs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
p pkg.Package
|
p pkg.Package
|
||||||
@ -132,7 +132,7 @@ func TestGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jenkins package",
|
name: "jenkins package identified via pkg type",
|
||||||
p: pkg.Package{
|
p: pkg.Package{
|
||||||
Name: "name",
|
Name: "name",
|
||||||
Version: "3.2",
|
Version: "3.2",
|
||||||
@ -142,13 +142,32 @@ func TestGenerate(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
|
"cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
|
||||||
"cpe:2.3:a:*:name:3.2:*:*:*:*:java:*:*",
|
|
||||||
"cpe:2.3:a:*:name:3.2:*:*:*:*:maven:*:*",
|
|
||||||
"cpe:2.3:a:*:name:3.2:*:*:*:*:jenkins:*:*",
|
"cpe:2.3:a:*:name:3.2:*:*:*:*:jenkins:*:*",
|
||||||
"cpe:2.3:a:*:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
|
"cpe:2.3:a:*:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
|
||||||
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
|
||||||
"cpe:2.3:a:name:name:3.2:*:*:*:*:java:*:*",
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:jenkins:*:*",
|
||||||
"cpe:2.3:a:name:name:3.2:*:*:*:*:maven:*:*",
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jenkins package identified via groupId",
|
||||||
|
p: pkg.Package{
|
||||||
|
Name: "name",
|
||||||
|
Version: "3.2",
|
||||||
|
FoundBy: "some-analyzer",
|
||||||
|
Language: pkg.Java,
|
||||||
|
Type: pkg.JavaPkg,
|
||||||
|
Metadata: pkg.JavaMetadata{
|
||||||
|
PomProperties: &pkg.PomProperties{
|
||||||
|
GroupID: "com.cloudbees.jenkins.plugins",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"cpe:2.3:a:*:name:3.2:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:*:name:3.2:*:*:*:*:jenkins:*:*",
|
||||||
|
"cpe:2.3:a:*:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
|
||||||
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
|
||||||
"cpe:2.3:a:name:name:3.2:*:*:*:*:jenkins:*:*",
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:jenkins:*:*",
|
||||||
"cpe:2.3:a:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
|
"cpe:2.3:a:name:name:3.2:*:*:*:*:cloudbees_jenkins:*:*",
|
||||||
},
|
},
|
||||||
@ -165,13 +184,13 @@ func TestGenerate(t *testing.T) {
|
|||||||
actualCpeSet.Add(a.BindToFmtString())
|
actualCpeSet.Add(a.BindToFmtString())
|
||||||
}
|
}
|
||||||
|
|
||||||
extra := strset.Difference(expectedCpeSet, actualCpeSet).List()
|
extra := strset.Difference(actualCpeSet, expectedCpeSet).List()
|
||||||
sort.Strings(extra)
|
sort.Strings(extra)
|
||||||
for _, d := range extra {
|
for _, d := range extra {
|
||||||
t.Errorf("extra CPE: %+v", d)
|
t.Errorf("extra CPE: %+v", d)
|
||||||
}
|
}
|
||||||
|
|
||||||
missing := strset.Difference(actualCpeSet, expectedCpeSet).List()
|
missing := strset.Difference(expectedCpeSet, actualCpeSet).List()
|
||||||
sort.Strings(missing)
|
sort.Strings(missing)
|
||||||
for _, d := range missing {
|
for _, d := range missing {
|
||||||
t.Errorf("missing CPE: %+v", d)
|
t.Errorf("missing CPE: %+v", d)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user