mirror of
https://github.com/anchore/syft.git
synced 2026-08-19 08:38:25 +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
|
||||
}
|
||||
|
||||
// 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
|
||||
func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry *capabilities.CatalogerEntry) {
|
||||
// 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,
|
||||
PURL: purlStr,
|
||||
CPEs: cpeStrings,
|
||||
Type: "BinaryPkg",
|
||||
Type: packageTypeConstName(classifier.PackageType()),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -155,10 +155,10 @@ catalogers:
|
||||
packages:
|
||||
- class: perl-binary
|
||||
name: perl
|
||||
purl: pkg:generic/perl
|
||||
purl: pkg:cpan/perl
|
||||
cpes:
|
||||
- cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
type: CpanPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/composer*'
|
||||
|
||||
@ -436,8 +436,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.12.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.12.5",
|
||||
Type: "cpan",
|
||||
PURL: "pkg:cpan/perl@5.12.5",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
@ -449,8 +449,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.20.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.20.0",
|
||||
Type: "cpan",
|
||||
PURL: "pkg:cpan/perl@5.20.0",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
@ -462,8 +462,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.37.8",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.37.8",
|
||||
Type: "cpan",
|
||||
PURL: "pkg:cpan/perl@5.37.8",
|
||||
Locations: locations("perl"),
|
||||
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) {
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/binutils"
|
||||
)
|
||||
|
||||
@ -248,8 +249,9 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
Package: "perl",
|
||||
PURL: mustPURL("pkg:generic/perl@version"),
|
||||
PURL: mustPURL("pkg:cpan/perl@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
Type: pkg.CpanPkg,
|
||||
},
|
||||
{
|
||||
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 []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) {
|
||||
|
||||
@ -35,7 +35,7 @@ func NewClassifierPackage(classifier Classifier, location file.Location, matchMe
|
||||
Locations: file.NewLocationSet(
|
||||
location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Type: pkg.BinaryPkg,
|
||||
Type: classifier.PackageType(),
|
||||
CPEs: cpes,
|
||||
FoundBy: catalogerName,
|
||||
Metadata: pkg.BinarySignature{
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"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/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) {
|
||||
|
||||
tests := []struct {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user