mirror of
https://github.com/anchore/syft.git
synced 2025-11-18 08:53:15 +01:00
fix: apk product/vendor generation for old metadata (#1635)
This fixes some instances where the improved APK CPE generation logic caused regressions for older alpine package APK metadata. It now generates multiple "upstream" candidates with both name and package type which reduces the amount of duplicated code in the apk cpe gen logic. This also improves the handling of stream version packages, so now we can correctly identify packages such as ruby3.2-rexml as the rexml ruby gem. Signed-off-by: Weston Steimel <weston.steimel@anchore.com>
This commit is contained in:
parent
e92b0fa629
commit
8e1205f7ab
@ -19,8 +19,11 @@ const ApkDBGlob = "**/lib/apk/db/installed"
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
_ FileOwner = (*ApkMetadata)(nil)
|
_ FileOwner = (*ApkMetadata)(nil)
|
||||||
prefixes = []string{"py-", "py2-", "py3-", "ruby-"}
|
prefixesToPackageType = map[string]Type{
|
||||||
upstreamPattern = regexp.MustCompile(`^(?P<upstream>[a-zA-Z][\w-]*?)\-?\d[\d\.]*$`)
|
"py-": PythonPkg,
|
||||||
|
"ruby-": GemPkg,
|
||||||
|
}
|
||||||
|
streamVersionPkgNamePattern = regexp.MustCompile(`^(?P<stream>[a-zA-Z][\w-]*?)(?P<streamVersion>\-?\d[\d\.]*?)($|-(?P<subPackage>[a-zA-Z][\w-]*?)?)$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ApkMetadata represents all captured data for a Alpine DB package entry.
|
// ApkMetadata represents all captured data for a Alpine DB package entry.
|
||||||
@ -121,23 +124,44 @@ func (m ApkMetadata) OwnedFiles() (result []string) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m ApkMetadata) Upstream() string {
|
type UpstreamCandidate struct {
|
||||||
if m.OriginPackage != "" && m.OriginPackage != m.Package {
|
Name string
|
||||||
return m.OriginPackage
|
Type Type
|
||||||
}
|
}
|
||||||
|
|
||||||
groups := internal.MatchNamedCaptureGroups(upstreamPattern, m.Package)
|
func (m ApkMetadata) UpstreamCandidates() (candidates []UpstreamCandidate) {
|
||||||
|
name := m.Package
|
||||||
upstream, ok := groups["upstream"]
|
if m.OriginPackage != "" && m.OriginPackage != m.Package {
|
||||||
if !ok {
|
candidates = append(candidates, UpstreamCandidate{Name: m.OriginPackage, Type: ApkPkg})
|
||||||
upstream = m.Package
|
}
|
||||||
}
|
|
||||||
|
groups := internal.MatchNamedCaptureGroups(streamVersionPkgNamePattern, m.Package)
|
||||||
for _, p := range prefixes {
|
stream, ok := groups["stream"]
|
||||||
if strings.HasPrefix(upstream, p) {
|
|
||||||
return strings.TrimPrefix(upstream, p)
|
if ok && stream != "" {
|
||||||
}
|
sub, ok := groups["subPackage"]
|
||||||
}
|
|
||||||
|
if ok && sub != "" {
|
||||||
return upstream
|
name = fmt.Sprintf("%s-%s", stream, sub)
|
||||||
|
} else {
|
||||||
|
name = stream
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for prefix, typ := range prefixesToPackageType {
|
||||||
|
if strings.HasPrefix(name, prefix) {
|
||||||
|
t := strings.TrimPrefix(name, prefix)
|
||||||
|
if t != "" {
|
||||||
|
candidates = append(candidates, UpstreamCandidate{Name: t, Type: typ})
|
||||||
|
return candidates
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
candidates = append(candidates, UpstreamCandidate{Name: name, Type: UnknownPkg})
|
||||||
|
return candidates
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sergi/go-diff/diffmatchpatch"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
@ -165,26 +164,30 @@ func TestSpaceDelimitedStringSlice_UnmarshalJSON(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApkMetadata_Upstream(t *testing.T) {
|
func TestApkMetadata_UpstreamCandidates(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
metadata ApkMetadata
|
metadata ApkMetadata
|
||||||
expected string
|
expected []UpstreamCandidate
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "gocase",
|
name: "gocase",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "p",
|
Package: "p",
|
||||||
},
|
},
|
||||||
expected: "p",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "p", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same package and origin",
|
name: "same package and origin simple case",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "p",
|
Package: "p",
|
||||||
OriginPackage: "p",
|
OriginPackage: "p",
|
||||||
},
|
},
|
||||||
expected: "p",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "p", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "different package and origin",
|
name: "different package and origin",
|
||||||
@ -192,15 +195,30 @@ func TestApkMetadata_Upstream(t *testing.T) {
|
|||||||
Package: "p",
|
Package: "p",
|
||||||
OriginPackage: "origin",
|
OriginPackage: "origin",
|
||||||
},
|
},
|
||||||
expected: "origin",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "origin", Type: ApkPkg},
|
||||||
|
{Name: "p", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "upstream python package information as qualifier",
|
name: "upstream python package information as qualifier py- prefix",
|
||||||
|
metadata: ApkMetadata{
|
||||||
|
Package: "py-potatoes",
|
||||||
|
OriginPackage: "py-potatoes",
|
||||||
|
},
|
||||||
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "potatoes", Type: PythonPkg},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "upstream python package information as qualifier py3- prefix",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "py3-potatoes",
|
Package: "py3-potatoes",
|
||||||
OriginPackage: "py3-potatoes",
|
OriginPackage: "py3-potatoes",
|
||||||
},
|
},
|
||||||
expected: "potatoes",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "potatoes", Type: PythonPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "python package with distinct origin package",
|
name: "python package with distinct origin package",
|
||||||
@ -208,7 +226,10 @@ func TestApkMetadata_Upstream(t *testing.T) {
|
|||||||
Package: "py3-non-existant",
|
Package: "py3-non-existant",
|
||||||
OriginPackage: "abcdefg",
|
OriginPackage: "abcdefg",
|
||||||
},
|
},
|
||||||
expected: "abcdefg",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "abcdefg", Type: ApkPkg},
|
||||||
|
{Name: "non-existant", Type: PythonPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "upstream ruby package information as qualifier",
|
name: "upstream ruby package information as qualifier",
|
||||||
@ -216,117 +237,171 @@ func TestApkMetadata_Upstream(t *testing.T) {
|
|||||||
Package: "ruby-something",
|
Package: "ruby-something",
|
||||||
OriginPackage: "ruby-something",
|
OriginPackage: "ruby-something",
|
||||||
},
|
},
|
||||||
expected: "something",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "something", Type: GemPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "python package with distinct origin package",
|
name: "ruby package with distinct origin package",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "ruby-something",
|
Package: "ruby-something",
|
||||||
OriginPackage: "1234567",
|
OriginPackage: "1234567",
|
||||||
},
|
},
|
||||||
expected: "1234567",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "1234567", Type: ApkPkg},
|
||||||
|
{Name: "something", Type: GemPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "postgesql-15 upstream postgresql",
|
name: "postgesql-15 upstream postgresql",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "postgresql-15",
|
Package: "postgresql-15",
|
||||||
},
|
},
|
||||||
expected: "postgresql",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "postgresql", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "postgesql15 upstream postgresql",
|
name: "postgesql15 upstream postgresql",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "postgresql15",
|
Package: "postgresql15",
|
||||||
},
|
},
|
||||||
expected: "postgresql",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "postgresql", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "go-1.19 upstream go",
|
name: "go-1.19 upstream go",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "go-1.19",
|
Package: "go-1.19",
|
||||||
},
|
},
|
||||||
expected: "go",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "go", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "go1.143 upstream go",
|
name: "go1.143 upstream go",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "go1.143",
|
Package: "go1.143",
|
||||||
},
|
},
|
||||||
expected: "go",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "go", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "abc-101.191.23456 upstream abc",
|
name: "abc-101.191.23456 upstream abc",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "abc-101.191.23456",
|
Package: "abc-101.191.23456",
|
||||||
},
|
},
|
||||||
expected: "abc",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "abc", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "abc101.191.23456 upstream abc",
|
name: "abc101.191.23456 upstream abc",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "abc101.191.23456",
|
Package: "abc101.191.23456",
|
||||||
},
|
},
|
||||||
expected: "abc",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "abc", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "abc101-12345-1045 upstream abc101-12345",
|
name: "abc101-12345-1045 upstream abc101-12345",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "abc101-12345-1045",
|
Package: "abc101-12345-1045",
|
||||||
},
|
},
|
||||||
expected: "abc101-12345",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "abc101-12345", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "abc101-a12345-1045 upstream abc101-a12345",
|
name: "abc101-a12345-1045 upstream abc101-a12345",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "abc101-a12345-1045",
|
Package: "abc101-a12345-1045",
|
||||||
},
|
},
|
||||||
expected: "abc101-a12345",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "abc-a12345-1045", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "package starting with single digit",
|
name: "package starting with single digit",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "3proxy",
|
Package: "3proxy",
|
||||||
},
|
},
|
||||||
expected: "3proxy",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "3proxy", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "package starting with multiple digits",
|
name: "package starting with multiple digits",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "356proxy",
|
Package: "356proxy",
|
||||||
},
|
},
|
||||||
expected: "356proxy",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "356proxy", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "package composed of only digits",
|
name: "package composed of only digits",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "123456",
|
Package: "123456",
|
||||||
},
|
},
|
||||||
expected: "123456",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "123456", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ruby-3.6 upstream ruby",
|
name: "ruby-3.6 upstream ruby",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "ruby-3.6",
|
Package: "ruby-3.6",
|
||||||
},
|
},
|
||||||
expected: "ruby",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "ruby", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ruby3.6 upstream ruby",
|
name: "ruby3.6 upstream ruby",
|
||||||
metadata: ApkMetadata{
|
metadata: ApkMetadata{
|
||||||
Package: "ruby3.6",
|
Package: "ruby3.6",
|
||||||
},
|
},
|
||||||
expected: "ruby",
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "ruby", Type: UnknownPkg},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ruby3.6-tacos upstream tacos",
|
||||||
|
metadata: ApkMetadata{
|
||||||
|
Package: "ruby3.6-tacos",
|
||||||
|
},
|
||||||
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "tacos", Type: GemPkg},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ruby-3.6-tacos upstream tacos",
|
||||||
|
metadata: ApkMetadata{
|
||||||
|
Package: "ruby-3.6-tacos",
|
||||||
|
},
|
||||||
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "tacos", Type: GemPkg},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "abc1234jksajflksa",
|
||||||
|
metadata: ApkMetadata{
|
||||||
|
Package: "abc1234jksajflksa",
|
||||||
|
},
|
||||||
|
expected: []UpstreamCandidate{
|
||||||
|
{Name: "abc1234jksajflksa", Type: UnknownPkg},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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 := test.metadata.Upstream()
|
actual := test.metadata.UpstreamCandidates()
|
||||||
if actual != test.expected {
|
assert.Equal(t, test.expected, actual)
|
||||||
dmp := diffmatchpatch.New()
|
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
|
||||||
t.Errorf("diff: %s", dmp.DiffPrettyText(diffs))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,9 +36,14 @@ func packageURL(m pkg.ApkMetadata, distro *linux.Release) string {
|
|||||||
pkg.PURLQualifierArch: m.Architecture,
|
pkg.PURLQualifierArch: m.Architecture,
|
||||||
}
|
}
|
||||||
|
|
||||||
upstream := m.Upstream()
|
upstreams := m.UpstreamCandidates()
|
||||||
if upstream != "" && upstream != m.Package {
|
if len(upstreams) > 0 {
|
||||||
qualifiers[pkg.PURLQualifierUpstream] = upstream
|
// only room for one value so for now just take the first one
|
||||||
|
upstream := upstreams[0]
|
||||||
|
|
||||||
|
if upstream.Name != "" && upstream.Name != m.Package {
|
||||||
|
qualifiers[pkg.PURLQualifierUpstream] = upstream.Name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return packageurl.NewPackageURL(
|
return packageurl.NewPackageURL(
|
||||||
|
|||||||
@ -234,7 +234,7 @@ func Test_PackageURL(t *testing.T) {
|
|||||||
ID: "alpine",
|
ID: "alpine",
|
||||||
VersionID: "3.4.6",
|
VersionID: "3.4.6",
|
||||||
},
|
},
|
||||||
expected: "pkg:apk/alpine/abc101-a12345-1045@101.191.23456?arch=a&upstream=abc101-a12345&distro=alpine-3.4.6",
|
expected: "pkg:apk/alpine/abc101-a12345-1045@101.191.23456?arch=a&upstream=abc-a12345-1045&distro=alpine-3.4.6",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wolfi distro",
|
name: "wolfi distro",
|
||||||
|
|||||||
@ -1,159 +1,9 @@
|
|||||||
package cpe
|
package cpe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
pythonPrefixes = []string{"py-", "py2-", "py3-"}
|
|
||||||
rubyPrefixes = []string{"ruby-"}
|
|
||||||
)
|
|
||||||
|
|
||||||
func pythonCandidateVendorsFromName(v string) fieldCandidateSet {
|
|
||||||
vendors := newFieldCandidateSet()
|
|
||||||
vendors.addValue(v)
|
|
||||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, v, v)...)
|
|
||||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, v)...)
|
|
||||||
|
|
||||||
for _, av := range additionalVendorsForPython(v) {
|
|
||||||
vendors.addValue(av)
|
|
||||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, av, av)...)
|
|
||||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, av)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vendors
|
|
||||||
}
|
|
||||||
|
|
||||||
func pythonCandidateVendorsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
|
||||||
vendors := newFieldCandidateSet()
|
|
||||||
upstream := m.Upstream()
|
|
||||||
|
|
||||||
for _, p := range pythonPrefixes {
|
|
||||||
if strings.HasPrefix(m.Package, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
|
||||||
vendors.union(pythonCandidateVendorsFromName(t))
|
|
||||||
}
|
|
||||||
|
|
||||||
if upstream != m.Package && strings.HasPrefix(upstream, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(upstream, p))
|
|
||||||
vendors.union(pythonCandidateVendorsFromName(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vendors
|
|
||||||
}
|
|
||||||
|
|
||||||
func pythonCandidateProductsFromName(p string) fieldCandidateSet {
|
|
||||||
products := newFieldCandidateSet()
|
|
||||||
products.addValue(p)
|
|
||||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.PythonPkg, p)...)
|
|
||||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.PythonPkg, p)...)
|
|
||||||
return products
|
|
||||||
}
|
|
||||||
|
|
||||||
func pythonCandidateProductsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
|
||||||
products := newFieldCandidateSet()
|
|
||||||
upstream := m.Upstream()
|
|
||||||
|
|
||||||
for _, p := range pythonPrefixes {
|
|
||||||
if strings.HasPrefix(m.Package, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
|
||||||
products.union(pythonCandidateProductsFromName(t))
|
|
||||||
}
|
|
||||||
|
|
||||||
if upstream != m.Package && strings.HasPrefix(upstream, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(upstream, p))
|
|
||||||
products.union(pythonCandidateProductsFromName(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return products
|
|
||||||
}
|
|
||||||
|
|
||||||
func rubyCandidateVendorsFromName(v string) fieldCandidateSet {
|
|
||||||
vendors := newFieldCandidateSet()
|
|
||||||
vendors.addValue(v)
|
|
||||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.GemPkg, v, v)...)
|
|
||||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.GemPkg, v)...)
|
|
||||||
return vendors
|
|
||||||
}
|
|
||||||
|
|
||||||
func rubyCandidateVendorsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
|
||||||
vendors := newFieldCandidateSet()
|
|
||||||
upstream := m.Upstream()
|
|
||||||
|
|
||||||
if upstream != "ruby" {
|
|
||||||
for _, p := range rubyPrefixes {
|
|
||||||
if strings.HasPrefix(m.Package, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
|
||||||
vendors.union(rubyCandidateVendorsFromName(t))
|
|
||||||
}
|
|
||||||
|
|
||||||
if upstream != "" && upstream != m.Package && strings.HasPrefix(upstream, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(upstream, p))
|
|
||||||
vendors.union(rubyCandidateVendorsFromName(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vendors
|
|
||||||
}
|
|
||||||
|
|
||||||
func rubyCandidateProductsFromName(p string) fieldCandidateSet {
|
|
||||||
products := newFieldCandidateSet()
|
|
||||||
products.addValue(p)
|
|
||||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.GemPkg, p)...)
|
|
||||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.GemPkg, p)...)
|
|
||||||
return products
|
|
||||||
}
|
|
||||||
|
|
||||||
func rubyCandidateProductsFromAPK(m pkg.ApkMetadata) fieldCandidateSet {
|
|
||||||
products := newFieldCandidateSet()
|
|
||||||
upstream := m.Upstream()
|
|
||||||
|
|
||||||
if upstream != "ruby" {
|
|
||||||
for _, p := range rubyPrefixes {
|
|
||||||
if strings.HasPrefix(m.Package, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(m.Package, p))
|
|
||||||
products.union(rubyCandidateProductsFromName(t))
|
|
||||||
}
|
|
||||||
|
|
||||||
if upstream != "" && upstream != m.Package && strings.HasPrefix(upstream, p) {
|
|
||||||
t := strings.ToLower(strings.TrimPrefix(upstream, p))
|
|
||||||
products.union(rubyCandidateProductsFromName(t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return products
|
|
||||||
}
|
|
||||||
|
|
||||||
func candidateVendorsFromAPKUpstream(m pkg.ApkMetadata) fieldCandidateSet {
|
|
||||||
vendors := newFieldCandidateSet()
|
|
||||||
upstream := m.Upstream()
|
|
||||||
if upstream != "" && upstream != m.Package {
|
|
||||||
vendors.addValue(upstream)
|
|
||||||
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.ApkPkg, upstream, upstream)...)
|
|
||||||
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.ApkPkg, upstream)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vendors
|
|
||||||
}
|
|
||||||
|
|
||||||
func candidateProductsFromAPKUpstream(m pkg.ApkMetadata) fieldCandidateSet {
|
|
||||||
products := newFieldCandidateSet()
|
|
||||||
upstream := m.Upstream()
|
|
||||||
if upstream != "" {
|
|
||||||
products.addValue(upstream)
|
|
||||||
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.ApkPkg, upstream)...)
|
|
||||||
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.ApkPkg, upstream)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return products
|
|
||||||
}
|
|
||||||
|
|
||||||
func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet {
|
func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet {
|
||||||
metadata, ok := p.Metadata.(pkg.ApkMetadata)
|
metadata, ok := p.Metadata.(pkg.ApkMetadata)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -161,9 +11,30 @@ func candidateVendorsForAPK(p pkg.Package) fieldCandidateSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vendors := newFieldCandidateSet()
|
vendors := newFieldCandidateSet()
|
||||||
vendors.union(pythonCandidateVendorsFromAPK(metadata))
|
candidates := metadata.UpstreamCandidates()
|
||||||
vendors.union(rubyCandidateVendorsFromAPK(metadata))
|
|
||||||
vendors.union(candidateVendorsFromAPKUpstream(metadata))
|
for _, c := range candidates {
|
||||||
|
switch c.Type {
|
||||||
|
case pkg.UnknownPkg:
|
||||||
|
vendors.addValue(c.Name)
|
||||||
|
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.ApkPkg, c.Name, c.Name)...)
|
||||||
|
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.ApkPkg, c.Name)...)
|
||||||
|
case pkg.PythonPkg:
|
||||||
|
vendors.addValue(c.Name)
|
||||||
|
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, c.Type, c.Name, c.Name)...)
|
||||||
|
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, c.Type, c.Name)...)
|
||||||
|
for _, av := range additionalVendorsForPython(c.Name) {
|
||||||
|
vendors.addValue(av)
|
||||||
|
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, pkg.PythonPkg, av, av)...)
|
||||||
|
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, pkg.PythonPkg, av)...)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
vendors.addValue(c.Name)
|
||||||
|
vendors.addValue(findAdditionalVendors(defaultCandidateAdditions, c.Type, c.Name, c.Name)...)
|
||||||
|
vendors.removeByValue(findVendorsToRemove(defaultCandidateRemovals, c.Type, c.Name)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vendors.union(candidateVendorsFromURL(metadata.URL))
|
vendors.union(candidateVendorsFromURL(metadata.URL))
|
||||||
|
|
||||||
for v := range vendors {
|
for v := range vendors {
|
||||||
@ -181,9 +52,20 @@ func candidateProductsForAPK(p pkg.Package) fieldCandidateSet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
products := newFieldCandidateSet()
|
products := newFieldCandidateSet()
|
||||||
products.union(pythonCandidateProductsFromAPK(metadata))
|
candidates := metadata.UpstreamCandidates()
|
||||||
products.union(rubyCandidateProductsFromAPK(metadata))
|
|
||||||
products.union(candidateProductsFromAPKUpstream(metadata))
|
for _, c := range candidates {
|
||||||
|
switch c.Type {
|
||||||
|
case pkg.UnknownPkg:
|
||||||
|
products.addValue(c.Name)
|
||||||
|
products.addValue(findAdditionalProducts(defaultCandidateAdditions, pkg.ApkPkg, c.Name)...)
|
||||||
|
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, pkg.ApkPkg, c.Name)...)
|
||||||
|
default:
|
||||||
|
products.addValue(c.Name)
|
||||||
|
products.addValue(findAdditionalProducts(defaultCandidateAdditions, c.Type, c.Name)...)
|
||||||
|
products.removeByValue(findProductsToRemove(defaultCandidateRemovals, c.Type, c.Name)...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for p := range products {
|
for p := range products {
|
||||||
p.disallowDelimiterVariations = true
|
p.disallowDelimiterVariations = true
|
||||||
|
|||||||
@ -69,7 +69,35 @@ func Test_candidateVendorsForAPK(t *testing.T) {
|
|||||||
URL: "https://www.gnu.org/software/make",
|
URL: "https://www.gnu.org/software/make",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
expected: []string{"gnu"},
|
expected: []string{"gnu", "make"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ruby-rake with matching origin",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "ruby-rake",
|
||||||
|
Type: pkg.ApkPkg,
|
||||||
|
MetadataType: pkg.ApkMetadataType,
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Package: "ruby-rake",
|
||||||
|
URL: "https://github.com/ruby/rake",
|
||||||
|
OriginPackage: "ruby-rake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"rake", "ruby-lang", "ruby"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ruby-rake with non-matching origin",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "ruby-rake",
|
||||||
|
Type: pkg.ApkPkg,
|
||||||
|
MetadataType: pkg.ApkMetadataType,
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Package: "ruby-rake",
|
||||||
|
URL: "https://www.ruby-lang.org/",
|
||||||
|
OriginPackage: "ruby",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"rake", "ruby-lang", "ruby"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -142,6 +170,31 @@ func Test_candidateProductsForAPK(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expected: []string{"make"},
|
expected: []string{"make"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "ruby-rake with matching origin",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Package: "ruby-rake",
|
||||||
|
URL: "https://github.com/ruby/rake",
|
||||||
|
OriginPackage: "ruby-rake",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"rake"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ruby-rake with non-matching origin",
|
||||||
|
pkg: pkg.Package{
|
||||||
|
Name: "ruby-rake",
|
||||||
|
Type: pkg.ApkPkg,
|
||||||
|
MetadataType: pkg.ApkMetadataType,
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Package: "ruby-rake",
|
||||||
|
URL: "https://www.ruby-lang.org/",
|
||||||
|
OriginPackage: "ruby",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"rake", "ruby"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
|||||||
@ -673,6 +673,48 @@ func TestGeneratePackageCPEs(t *testing.T) {
|
|||||||
"cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*",
|
"cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "regression: ruby-rake apk missing expected ruby-lang:rake CPE",
|
||||||
|
p: pkg.Package{
|
||||||
|
Name: "ruby-rake",
|
||||||
|
Version: "2.7.6-r0",
|
||||||
|
Type: pkg.ApkPkg,
|
||||||
|
FoundBy: "apk-db-analyzer",
|
||||||
|
Language: pkg.UnknownLanguage,
|
||||||
|
MetadataType: pkg.ApkMetadataType,
|
||||||
|
Metadata: pkg.ApkMetadata{
|
||||||
|
Package: "ruby-rake",
|
||||||
|
URL: "https://www.ruby-lang.org/",
|
||||||
|
OriginPackage: "ruby",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"cpe:2.3:a:ruby-lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:rake:ruby:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-lang:ruby:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby-rake:ruby:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby:ruby:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_lang:ruby:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
"cpe:2.3:a:ruby_rake:ruby:2.7.6-r0:*:*:*:*:*:*:*",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user