Consider Author field for wordpress plugins when generating CPEs (#2946)

* enhance wordpress vendor candidates for CPEs

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* encode wordpress plugin target software

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman 2024-06-10 14:12:20 -04:00 committed by GitHub
parent f966bcfd03
commit 0956753409
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 16 deletions

View File

@ -129,23 +129,26 @@ func FromDictionaryFind(p pkg.Package) ([]cpe.CPE, bool) {
func FromPackageAttributes(p pkg.Package) []cpe.CPE { func FromPackageAttributes(p pkg.Package) []cpe.CPE {
vendors := candidateVendors(p) vendors := candidateVendors(p)
products := candidateProducts(p) products := candidateProducts(p)
targetSWs := candidateTargetSw(p)
if len(products) == 0 { if len(products) == 0 {
return nil return nil
} }
keys := strset.New() keys := strset.New()
cpes := make([]cpe.Attributes, 0) cpes := make([]cpe.Attributes, 0)
for _, product := range products { for _, ts := range targetSWs {
for _, vendor := range vendors { for _, product := range products {
// prevent duplicate entries... for _, vendor := range vendors {
key := fmt.Sprintf("%s|%s|%s", product, vendor, p.Version) // prevent duplicate entries...
if keys.Has(key) { key := fmt.Sprintf("%s|%s|%s|%s", product, vendor, p.Version, ts)
continue if keys.Has(key) {
} continue
keys.Add(key) }
// add a new entry... keys.Add(key)
if c := newCPE(product, vendor, p.Version, cpe.Any); c != nil { // add a new entry...
cpes = append(cpes, *c) if c := newCPE(product, vendor, p.Version, ts); c != nil {
cpes = append(cpes, *c)
}
} }
} }
} }
@ -162,6 +165,13 @@ func FromPackageAttributes(p pkg.Package) []cpe.CPE {
return result return result
} }
func candidateTargetSw(p pkg.Package) []string {
if p.Type == pkg.WordpressPluginPkg {
return []string{"wordpress"}
}
return []string{cpe.Any}
}
//nolint:funlen //nolint:funlen
func candidateVendors(p pkg.Package) []string { func candidateVendors(p pkg.Package) []string {
// in ecosystems where the packaging metadata does not have a clear field to indicate a vendor (or a field that // in ecosystems where the packaging metadata does not have a clear field to indicate a vendor (or a field that

View File

@ -719,6 +719,31 @@ func TestGeneratePackageCPEs(t *testing.T) {
"cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", "cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
}, },
}, },
{
name: "wordpress plugin",
p: pkg.Package{
Name: "WP Coder",
Version: "2.5.1",
Type: pkg.WordpressPluginPkg,
Metadata: pkg.WordpressPluginEntry{
PluginInstallDirectory: "wp-coder",
Author: "Wow-Company",
AuthorURI: "https://wow-estore.com",
},
},
expected: []string{
"cpe:2.3:a:wow-company:wp-coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow-company:wp_coder:2.5.1:*:*:*:*:wordpress:*:*", // this is the correct CPE relative to CVE-2021-25053
"cpe:2.3:a:wow-estore:wp-coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow-estore:wp_coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow:wp-coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow:wp_coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow_company:wp-coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow_company:wp_coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow_estore:wp-coder:2.5.1:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:wow_estore:wp_coder:2.5.1:*:*:*:*:wordpress:*:*",
},
},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -21,12 +21,18 @@ func candidateVendorsForWordpressPlugin(p pkg.Package) fieldCandidateSet {
vendors := newFieldCandidateSet() vendors := newFieldCandidateSet()
if metadata.Author != "" {
vendors.addValue(strings.ToLower(metadata.Author))
}
if metadata.AuthorURI != "" { if metadata.AuthorURI != "" {
matchMap := internal.MatchNamedCaptureGroups(vendorFromURLRegexp, metadata.AuthorURI) matchMap := internal.MatchNamedCaptureGroups(vendorFromURLRegexp, metadata.AuthorURI)
if vendor, ok := matchMap["vendor"]; ok && vendor != "" { if vendor, ok := matchMap["vendor"]; ok && vendor != "" {
vendors.addValue(vendor) vendors.addValue(strings.ToLower(vendor))
} }
} else { }
if len(vendors) == 0 {
// add plugin_name + _project as a vendor if no Author URI found // add plugin_name + _project as a vendor if no Author URI found
vendors.addValue(fmt.Sprintf("%s_project", normalizeWordpressPluginName(p.Name))) vendors.addValue(fmt.Sprintf("%s_project", normalizeWordpressPluginName(p.Name)))
} }

View File

@ -24,7 +24,7 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
AuthorURI: "https://automattic.com/wordpress-plugins/", AuthorURI: "https://automattic.com/wordpress-plugins/",
}, },
}, },
expected: []string{"automattic"}, expected: []string{"automattic - anti-spam team", "automattic"},
}, },
{ {
name: "All-in-One WP Migration", name: "All-in-One WP Migration",
@ -47,7 +47,7 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
AuthorURI: "https://bookingultrapro.com/", AuthorURI: "https://bookingultrapro.com/",
}, },
}, },
expected: []string{"bookingultrapro"}, expected: []string{"booking ultra pro", "bookingultrapro"},
}, },
{ {
name: "Coming Soon Chop Chop", name: "Coming Soon Chop Chop",
@ -58,7 +58,7 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
AuthorURI: "https://www.chop-chop.org", AuthorURI: "https://www.chop-chop.org",
}, },
}, },
expected: []string{"chop-chop"}, expected: []string{"chop-chop.org", "chop-chop"},
}, },
{ {
name: "Access Code Feeder", name: "Access Code Feeder",
@ -71,6 +71,22 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
// When a plugin as no `Author URI` use plugin_name + _project as a vendor // When a plugin as no `Author URI` use plugin_name + _project as a vendor
expected: []string{"access_code_feeder_project"}, expected: []string{"access_code_feeder_project"},
}, },
{
name: "WP Coder",
pkg: pkg.Package{
Name: "WP Coder",
Metadata: pkg.WordpressPluginEntry{
PluginInstallDirectory: "wp-coder",
Author: "Wow-Company",
AuthorURI: "https://wow-estore.com/",
},
},
// found in the wild https://plugins.trac.wordpress.org/browser/wp-coder/tags/2.5.1/wp-coder.php
expected: []string{
"wow-company", // this is the correct answer relative to CPEs registered on CVE-2021-25053
"wow-estore",
},
},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {