Skip to content

Commit

Permalink
Consider Author field for wordpress plugins when generating CPEs (#…
Browse files Browse the repository at this point in the history
…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>
  • Loading branch information
wagoodman committed Jun 10, 2024
1 parent f966bcf commit 0956753
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 16 deletions.
32 changes: 21 additions & 11 deletions syft/pkg/cataloger/internal/cpegenerate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,23 +129,26 @@ func FromDictionaryFind(p pkg.Package) ([]cpe.CPE, bool) {
func FromPackageAttributes(p pkg.Package) []cpe.CPE {
vendors := candidateVendors(p)
products := candidateProducts(p)
targetSWs := candidateTargetSw(p)
if len(products) == 0 {
return nil
}

keys := strset.New()
cpes := make([]cpe.Attributes, 0)
for _, product := range products {
for _, vendor := range vendors {
// prevent duplicate entries...
key := fmt.Sprintf("%s|%s|%s", product, vendor, p.Version)
if keys.Has(key) {
continue
}
keys.Add(key)
// add a new entry...
if c := newCPE(product, vendor, p.Version, cpe.Any); c != nil {
cpes = append(cpes, *c)
for _, ts := range targetSWs {
for _, product := range products {
for _, vendor := range vendors {
// prevent duplicate entries...
key := fmt.Sprintf("%s|%s|%s|%s", product, vendor, p.Version, ts)
if keys.Has(key) {
continue
}
keys.Add(key)
// add a new entry...
if c := newCPE(product, vendor, p.Version, ts); c != nil {
cpes = append(cpes, *c)
}
}
}
}
Expand All @@ -162,6 +165,13 @@ func FromPackageAttributes(p pkg.Package) []cpe.CPE {
return result
}

func candidateTargetSw(p pkg.Package) []string {
if p.Type == pkg.WordpressPluginPkg {
return []string{"wordpress"}
}
return []string{cpe.Any}
}

//nolint:funlen
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
Expand Down
25 changes: 25 additions & 0 deletions syft/pkg/cataloger/internal/cpegenerate/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,31 @@ func TestGeneratePackageCPEs(t *testing.T) {
"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 {
Expand Down
10 changes: 8 additions & 2 deletions syft/pkg/cataloger/internal/cpegenerate/wordpress.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ func candidateVendorsForWordpressPlugin(p pkg.Package) fieldCandidateSet {

vendors := newFieldCandidateSet()

if metadata.Author != "" {
vendors.addValue(strings.ToLower(metadata.Author))
}

if metadata.AuthorURI != "" {
matchMap := internal.MatchNamedCaptureGroups(vendorFromURLRegexp, metadata.AuthorURI)
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
vendors.addValue(fmt.Sprintf("%s_project", normalizeWordpressPluginName(p.Name)))
}
Expand Down
22 changes: 19 additions & 3 deletions syft/pkg/cataloger/internal/cpegenerate/wordpress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
AuthorURI: "https://automattic.com/wordpress-plugins/",
},
},
expected: []string{"automattic"},
expected: []string{"automattic - anti-spam team", "automattic"},
},
{
name: "All-in-One WP Migration",
Expand All @@ -47,7 +47,7 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
AuthorURI: "https://bookingultrapro.com/",
},
},
expected: []string{"bookingultrapro"},
expected: []string{"booking ultra pro", "bookingultrapro"},
},
{
name: "Coming Soon Chop Chop",
Expand All @@ -58,7 +58,7 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
AuthorURI: "https://www.chop-chop.org",
},
},
expected: []string{"chop-chop"},
expected: []string{"chop-chop.org", "chop-chop"},
},
{
name: "Access Code Feeder",
Expand All @@ -71,6 +71,22 @@ func Test_candidateVendorsForWordpressPlugin(t *testing.T) {
// When a plugin as no `Author URI` use plugin_name + _project as a vendor
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 {
t.Run(test.name, func(t *testing.T) {
Expand Down

0 comments on commit 0956753

Please sign in to comment.