mirror of
https://github.com/anchore/syft.git
synced 2026-08-19 16:48:27 +02:00
feat: report the perl interpreter as a cpan package
The `perl-binary` classifier now emits `pkg:cpan/perl@<version>` instead of `pkg:generic/perl@<version>`. `perl` is itself a CPAN distribution and carries advisories under that name, so typing it as `generic` left every one of them unreachable. This changes an existing purl. Anything keyed on `pkg:generic/perl` (allowlists, policy, SBOM diffs) needs updating. Builds on the cpan cataloging branch, which adds the package type this uses. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
1827ce2f4f
commit
29d4816575
@ -502,6 +502,19 @@ func convertToJSONSchemaTypesFromMetadata(metadataTypes []string) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packageTypeConstName renders a package type using its go constant name, which is what the detector
|
||||||
|
// "type" field reports (e.g. "BinaryPkg").
|
||||||
|
// a switch rather than a derivation, since pkg.Type values don't reliably map back to their constant
|
||||||
|
// names (e.g. "java-archive" is JavaPkg); add an arm when a classifier declares a new type.
|
||||||
|
func packageTypeConstName(t pkg.Type) string {
|
||||||
|
switch t {
|
||||||
|
case pkg.CpanPkg:
|
||||||
|
return "CpanPkg"
|
||||||
|
default:
|
||||||
|
return "BinaryPkg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// EnrichWithBinaryClassifier enriches an entry with binary classifier detectors if it's the binary-classifier-cataloger
|
// EnrichWithBinaryClassifier enriches an entry with binary classifier detectors if it's the binary-classifier-cataloger
|
||||||
func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry *capabilities.CatalogerEntry) {
|
func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry *capabilities.CatalogerEntry) {
|
||||||
// special handling for binary-classifier-cataloger: auto-generate one detector per classifier
|
// special handling for binary-classifier-cataloger: auto-generate one detector per classifier
|
||||||
@ -523,7 +536,7 @@ func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry
|
|||||||
Name: classifier.Package,
|
Name: classifier.Package,
|
||||||
PURL: purlStr,
|
PURL: purlStr,
|
||||||
CPEs: cpeStrings,
|
CPEs: cpeStrings,
|
||||||
Type: "BinaryPkg",
|
Type: packageTypeConstName(classifier.PackageType()),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -155,10 +155,10 @@ catalogers:
|
|||||||
packages:
|
packages:
|
||||||
- class: perl-binary
|
- class: perl-binary
|
||||||
name: perl
|
name: perl
|
||||||
purl: pkg:generic/perl
|
purl: pkg:cpan/perl
|
||||||
cpes:
|
cpes:
|
||||||
- cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*
|
- cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*
|
||||||
type: BinaryPkg
|
type: CpanPkg
|
||||||
- method: glob
|
- method: glob
|
||||||
criteria:
|
criteria:
|
||||||
- '**/composer*'
|
- '**/composer*'
|
||||||
|
|||||||
@ -436,8 +436,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
|||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "perl",
|
Name: "perl",
|
||||||
Version: "5.12.5",
|
Version: "5.12.5",
|
||||||
Type: "binary",
|
Type: "cpan",
|
||||||
PURL: "pkg:generic/perl@5.12.5",
|
PURL: "pkg:cpan/perl@5.12.5",
|
||||||
Locations: locations("perl"),
|
Locations: locations("perl"),
|
||||||
Metadata: metadata("perl-binary"),
|
Metadata: metadata("perl-binary"),
|
||||||
},
|
},
|
||||||
@ -449,8 +449,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
|||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "perl",
|
Name: "perl",
|
||||||
Version: "5.20.0",
|
Version: "5.20.0",
|
||||||
Type: "binary",
|
Type: "cpan",
|
||||||
PURL: "pkg:generic/perl@5.20.0",
|
PURL: "pkg:cpan/perl@5.20.0",
|
||||||
Locations: locations("perl"),
|
Locations: locations("perl"),
|
||||||
Metadata: metadata("perl-binary"),
|
Metadata: metadata("perl-binary"),
|
||||||
},
|
},
|
||||||
@ -462,8 +462,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
|||||||
expected: pkg.Package{
|
expected: pkg.Package{
|
||||||
Name: "perl",
|
Name: "perl",
|
||||||
Version: "5.37.8",
|
Version: "5.37.8",
|
||||||
Type: "binary",
|
Type: "cpan",
|
||||||
PURL: "pkg:generic/perl@5.37.8",
|
PURL: "pkg:cpan/perl@5.37.8",
|
||||||
Locations: locations("perl"),
|
Locations: locations("perl"),
|
||||||
Metadata: metadata("perl-binary"),
|
Metadata: metadata("perl-binary"),
|
||||||
},
|
},
|
||||||
@ -3097,6 +3097,58 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test_Cataloger_PackageTypes covers what assertPackagesAreEqual deliberately ignores: the package type and CPEs.
|
||||||
|
// The perl interpreter is reported as cpan so it lines up with the rest of the CPAN ecosystem, everything else
|
||||||
|
// keeps the default binary type.
|
||||||
|
func Test_Cataloger_PackageTypes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
logicalFixture string
|
||||||
|
wantType pkg.Type
|
||||||
|
wantPURL string
|
||||||
|
wantCPEs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
logicalFixture: "perl/5.20.0/linux-amd64",
|
||||||
|
wantType: pkg.CpanPkg,
|
||||||
|
wantPURL: "pkg:cpan/perl@5.20.0",
|
||||||
|
wantCPEs: []string{"cpe:2.3:a:perl:perl:5.20.0:*:*:*:*:*:*:*"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
logicalFixture: "haproxy/1.5.14/linux-amd64",
|
||||||
|
wantType: pkg.BinaryPkg,
|
||||||
|
wantPURL: "pkg:generic/haproxy@1.5.14",
|
||||||
|
wantCPEs: []string{"cpe:2.3:a:haproxy:haproxy:1.5.14:*:*:*:*:*:*:*"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.logicalFixture, func(t *testing.T) {
|
||||||
|
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||||
|
|
||||||
|
path := testutil.SnippetOrBinary(t, test.logicalFixture, *mustUseOriginalBinaries)
|
||||||
|
|
||||||
|
src, err := directorysource.NewFromPath(path)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
resolver, err := src.FileResolver(source.SquashedScope)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
packages, _, err := c.Catalog(context.Background(), resolver)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, packages, 1)
|
||||||
|
|
||||||
|
var cpes []string
|
||||||
|
for _, c := range packages[0].CPEs {
|
||||||
|
cpes = append(cpes, c.Attributes.BindToFmtString())
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, test.wantType, packages[0].Type)
|
||||||
|
assert.Equal(t, test.wantPURL, packages[0].PURL)
|
||||||
|
assert.Equal(t, test.wantCPEs, cpes)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
||||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/anchore/packageurl-go"
|
"github.com/anchore/packageurl-go"
|
||||||
"github.com/anchore/syft/syft/cpe"
|
"github.com/anchore/syft/syft/cpe"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/binutils"
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/binutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -248,8 +249,9 @@ func DefaultClassifiers() []binutils.Classifier {
|
|||||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||||
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||||
Package: "perl",
|
Package: "perl",
|
||||||
PURL: mustPURL("pkg:generic/perl@version"),
|
PURL: mustPURL("pkg:cpan/perl@version"),
|
||||||
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||||
|
Type: pkg.CpanPkg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Class: "php-composer-binary",
|
Class: "php-composer-binary",
|
||||||
|
|||||||
@ -48,6 +48,17 @@ type Classifier struct {
|
|||||||
|
|
||||||
// CPEs are the specific CPEs we want to include for this binary with updated version information
|
// CPEs are the specific CPEs we want to include for this binary with updated version information
|
||||||
CPEs []cpe.CPE `json:"cpes"`
|
CPEs []cpe.CPE `json:"cpes"`
|
||||||
|
|
||||||
|
// Type optionally overrides the package type reported for matches; when unset pkg.BinaryPkg is used
|
||||||
|
Type pkg.Type `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PackageType is the package type reported for matches of this classifier, defaulting to pkg.BinaryPkg.
|
||||||
|
func (cfg Classifier) PackageType() pkg.Type {
|
||||||
|
if cfg.Type == "" {
|
||||||
|
return pkg.BinaryPkg
|
||||||
|
}
|
||||||
|
return cfg.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg Classifier) MarshalJSON() ([]byte, error) {
|
func (cfg Classifier) MarshalJSON() ([]byte, error) {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ func NewClassifierPackage(classifier Classifier, location file.Location, matchMe
|
|||||||
Locations: file.NewLocationSet(
|
Locations: file.NewLocationSet(
|
||||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||||
),
|
),
|
||||||
Type: pkg.BinaryPkg,
|
Type: classifier.PackageType(),
|
||||||
CPEs: cpes,
|
CPEs: cpes,
|
||||||
FoundBy: catalogerName,
|
FoundBy: catalogerName,
|
||||||
Metadata: pkg.BinarySignature{
|
Metadata: pkg.BinarySignature{
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/cpe"
|
"github.com/anchore/syft/syft/cpe"
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
"github.com/anchore/syft/syft/internal/unionreader"
|
"github.com/anchore/syft/syft/internal/unionreader"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
"github.com/anchore/syft/syft/source/directorysource"
|
"github.com/anchore/syft/syft/source/directorysource"
|
||||||
)
|
)
|
||||||
@ -102,6 +103,35 @@ func Test_ClassifierCPEs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_ClassifierPackageType(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
classifier Classifier
|
||||||
|
want pkg.Type
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "defaults to binary when unset",
|
||||||
|
classifier: Classifier{Class: "some-binary", Package: "some-app"},
|
||||||
|
want: pkg.BinaryPkg,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "honors an explicit type",
|
||||||
|
classifier: Classifier{Class: "perl-binary", Package: "perl", Type: pkg.CpanPkg},
|
||||||
|
want: pkg.CpanPkg,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.want, test.classifier.PackageType())
|
||||||
|
|
||||||
|
p := NewClassifierPackage(test.classifier, file.NewLocation("/usr/bin/app"), map[string]string{"version": "1.2.3"}, "cataloger-name")
|
||||||
|
require.NotNil(t, p)
|
||||||
|
assert.Equal(t, test.want, p.Type)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestClassifier_MarshalJSON(t *testing.T) {
|
func TestClassifier_MarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user