Internalize CPE generation logic (#2541)

* migrate CPE generation logic to internal

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

* remove create function

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-01-26 12:16:05 -05:00 committed by GitHub
parent 7f90b8f1eb
commit b6cbf82389
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 63 additions and 49 deletions

View file

@ -8,6 +8,8 @@ import (
"github.com/facebookincubator/nvdtools/wfn" "github.com/facebookincubator/nvdtools/wfn"
) )
const Any = ""
type CPE struct { type CPE struct {
Part string Part string
Vendor string Vendor string
@ -123,7 +125,7 @@ func normalizeField(field string) string {
// keep dashes and forward slashes unescaped // keep dashes and forward slashes unescaped
if field == "*" { if field == "*" {
return wfn.Any return Any
} }
return stripSlashes(field) return stripSlashes(field)
} }

View file

@ -0,0 +1,15 @@
package cpe
import (
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/cpegenerate"
)
func Generate(p pkg.Package) []cpe.CPE {
return cpegenerate.FromPackageAttributes(p)
}
func DictionaryFind(p pkg.Package) (cpe.CPE, bool) {
return cpegenerate.FromDictionaryFind(p)
}

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"fmt" "fmt"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"testing" "testing"

View file

@ -12,7 +12,7 @@ import (
"github.com/facebookincubator/nvdtools/wfn" "github.com/facebookincubator/nvdtools/wfn"
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe/dictionary" "github.com/anchore/syft/syft/pkg/cataloger/internal/cpegenerate/dictionary"
) )
func generateIndexedDictionaryJSON(rawGzipData io.Reader) ([]byte, error) { func generateIndexedDictionaryJSON(rawGzipData io.Reader) ([]byte, error) {

View file

@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe/dictionary" "github.com/anchore/syft/syft/pkg/cataloger/internal/cpegenerate/dictionary"
) )
func Test_generateIndexedDictionaryJSON(t *testing.T) { func Test_generateIndexedDictionaryJSON(t *testing.T) {

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"strconv" "strconv"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
// A fieldCandidateCondition returns true if the condition is true for a given fieldCandidate. // A fieldCandidateCondition returns true if the condition is true for a given fieldCandidate.
type fieldCandidateCondition func(fieldCandidate) bool type fieldCandidateCondition func(fieldCandidate) bool

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"strings" "strings"

View file

@ -1,10 +1,8 @@
package cpe package cpegenerate
import ( import (
"strings" "strings"
"github.com/facebookincubator/nvdtools/wfn"
"github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
) )
@ -45,18 +43,18 @@ func disallowNonParseableCPEs(c cpe.CPE, _ pkg.Package) bool {
} }
// jenkins plugins should not match against jenkins // jenkins plugins should not match against jenkins
func disallowJenkinsServerCPEForPluginPackage(cpe cpe.CPE, p pkg.Package) bool { func disallowJenkinsServerCPEForPluginPackage(c cpe.CPE, p pkg.Package) bool {
if p.Type == pkg.JenkinsPluginPkg && cpe.Product == jenkinsName { if p.Type == pkg.JenkinsPluginPkg && c.Product == jenkinsName {
return true return true
} }
return false return false
} }
// filter to account that packages that are not for jenkins but have a CPE generated that will match against jenkins // filter to account that packages that are not for jenkins but have a CPE generated that will match against jenkins
func disallowJenkinsCPEsNotAssociatedWithJenkins(cpe cpe.CPE, p pkg.Package) bool { func disallowJenkinsCPEsNotAssociatedWithJenkins(c cpe.CPE, p pkg.Package) bool {
// jenkins server should only match against a product with the name jenkins // jenkins server should only match against a product with the name jenkins
if cpe.Product == jenkinsName && !strings.Contains(strings.ToLower(p.Name), jenkinsName) { if c.Product == jenkinsName && !strings.Contains(strings.ToLower(p.Name), jenkinsName) {
if cpe.Vendor == wfn.Any || cpe.Vendor == jenkinsName || cpe.Vendor == "cloudbees" { if c.Vendor == cpe.Any || c.Vendor == jenkinsName || c.Vendor == "cloudbees" {
return true return true
} }
} }
@ -64,10 +62,10 @@ func disallowJenkinsCPEsNotAssociatedWithJenkins(cpe cpe.CPE, p pkg.Package) boo
} }
// filter to account for packages which are jira client packages but have a CPE that will match against jira // filter to account for packages which are jira client packages but have a CPE that will match against jira
func disallowJiraClientServerMismatch(cpe cpe.CPE, p pkg.Package) bool { func disallowJiraClientServerMismatch(c cpe.CPE, p pkg.Package) bool {
// jira / atlassian should not apply to clients // jira / atlassian should not apply to clients
if cpe.Product == "jira" && strings.Contains(strings.ToLower(p.Name), "client") { if c.Product == "jira" && strings.Contains(strings.ToLower(p.Name), "client") {
if cpe.Vendor == wfn.Any || cpe.Vendor == "jira" || cpe.Vendor == "atlassian" { if c.Vendor == cpe.Any || c.Vendor == "jira" || c.Vendor == "atlassian" {
return true return true
} }
} }

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"bufio" "bufio"
@ -10,13 +10,12 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/facebookincubator/nvdtools/wfn"
"github.com/scylladb/go-set/strset" "github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log" "github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe/dictionary" "github.com/anchore/syft/syft/pkg/cataloger/internal/cpegenerate/dictionary"
) )
// knownVendors contains vendor strings that are known to exist in // knownVendors contains vendor strings that are known to exist in
@ -59,7 +58,7 @@ func GetIndexedDictionary() (_ *dictionary.Indexed, err error) {
return indexedCPEDictionary, err return indexedCPEDictionary, err
} }
func DictionaryFind(p pkg.Package) (cpe.CPE, bool) { func FromDictionaryFind(p pkg.Package) (cpe.CPE, bool) {
dict, err := GetIndexedDictionary() dict, err := GetIndexedDictionary()
if err != nil { if err != nil {
log.Debugf("dictionary CPE lookup not available: %+v", err) log.Debugf("dictionary CPE lookup not available: %+v", err)
@ -107,10 +106,10 @@ func DictionaryFind(p pkg.Package) (cpe.CPE, bool) {
return parsedCPE, true return parsedCPE, true
} }
// Generate Create a list of CPEs for a given package, trying to guess the vendor, product tuple. We should be trying to // FromPackageAttributes Create a list of CPEs for a given package, trying to guess the vendor, product tuple. We should be trying to
// generate the minimal set of representative CPEs, which implies that optional fields should not be included // generate the minimal set of representative CPEs, which implies that optional fields should not be included
// (such as target SW). // (such as target SW).
func Generate(p pkg.Package) []cpe.CPE { func FromPackageAttributes(p pkg.Package) []cpe.CPE {
vendors := candidateVendors(p) vendors := candidateVendors(p)
products := candidateProducts(p) products := candidateProducts(p)
if len(products) == 0 { if len(products) == 0 {
@ -128,7 +127,7 @@ func Generate(p pkg.Package) []cpe.CPE {
} }
keys.Add(key) keys.Add(key)
// add a new entry... // add a new entry...
if c := newCPE(product, vendor, p.Version, wfn.Any); c != nil { if c := newCPE(product, vendor, p.Version, cpe.Any); c != nil {
cpes = append(cpes, *c) cpes = append(cpes, *c)
} }
} }

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"fmt" "fmt"
@ -711,7 +711,7 @@ func TestGeneratePackageCPEs(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) {
actual := Generate(test.p) actual := FromPackageAttributes(test.p)
expectedCpeSet := set.NewStringSet(test.expected...) expectedCpeSet := set.NewStringSet(test.expected...)
actualCpeSet := set.NewStringSet() actualCpeSet := set.NewStringSet()
@ -994,7 +994,7 @@ func TestDictionaryFindIsWired(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) {
got, gotExists := DictionaryFind(tt.pkg) got, gotExists := FromDictionaryFind(tt.pkg)
assert.Equal(t, tt.want, got.BindToFmtString()) assert.Equal(t, tt.want, got.BindToFmtString())
assert.Equal(t, tt.wantExists, gotExists) assert.Equal(t, tt.wantExists, gotExists)

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"net/url" "net/url"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"sort" "sort"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
var DefaultArtifactIDToGroupID = map[string]string{ var DefaultArtifactIDToGroupID = map[string]string{
"ant": "org.apache.ant", "ant": "org.apache.ant",

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"strings" "strings"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import "github.com/anchore/syft/syft/pkg" import "github.com/anchore/syft/syft/pkg"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"fmt" "fmt"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import "github.com/anchore/syft/syft/pkg" import "github.com/anchore/syft/syft/pkg"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import "github.com/anchore/syft/syft/pkg" import "github.com/anchore/syft/syft/pkg"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import "strings" import "strings"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"testing" "testing"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"regexp" "regexp"

View file

@ -1,4 +1,4 @@
package cpe package cpegenerate
import ( import (
"testing" "testing"

View file

@ -5,7 +5,7 @@ import (
"github.com/anchore/packageurl-go" "github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common/cpe" "github.com/anchore/syft/syft/pkg/cataloger/internal/cpegenerate"
) )
// PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec) // PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec)
@ -53,7 +53,7 @@ func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaArchive) (groupID
} }
func groupIDFromKnownPackageList(pkgName string) (groupID string) { func groupIDFromKnownPackageList(pkgName string) (groupID string) {
if groupID, ok := cpe.DefaultArtifactIDToGroupID[pkgName]; ok { if groupID, ok := cpegenerate.DefaultArtifactIDToGroupID[pkgName]; ok {
return groupID return groupID
} }
return groupID return groupID
@ -64,13 +64,13 @@ func groupIDFromJavaManifest(manifest *pkg.JavaManifest) (groupID string) {
return groupID return groupID
} }
groupIDS := cpe.GetManifestFieldGroupIDs(manifest, cpe.PrimaryJavaManifestGroupIDFields) groupIDS := cpegenerate.GetManifestFieldGroupIDs(manifest, cpegenerate.PrimaryJavaManifestGroupIDFields)
// assumes that primaryJavaManifestNameFields are ordered by priority // assumes that primaryJavaManifestNameFields are ordered by priority
if len(groupIDS) != 0 { if len(groupIDS) != 0 {
return groupIDS[0] return groupIDS[0]
} }
groupIDS = cpe.GetManifestFieldGroupIDs(manifest, cpe.SecondaryJavaManifestGroupIDFields) groupIDS = cpegenerate.GetManifestFieldGroupIDs(manifest, cpegenerate.SecondaryJavaManifestGroupIDFields)
if len(groupIDS) != 0 { if len(groupIDS) != 0 {
return groupIDS[0] return groupIDS[0]