feat: report the perl interpreter as a cpan package (#5132)

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:
Alex Goodman
2026-09-30 15:45:46 -04:00
committed by GitHub
parent 509f1a1a5b
commit f76ae8e57c
7 changed files with 119 additions and 11 deletions
@@ -460,8 +460,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"),
},
@@ -473,8 +473,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"),
},
@@ -486,8 +486,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"),
},
@@ -3187,6 +3187,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())