mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
fix: suppress misleading upstream CPE for PHP extension binaries (#5102)
Motivation: syft's PHP interpreter cataloger reports each bundled PHP C extension (openssl, ldap, sqlite3, etc.) as its own package, using the PHP interpreter's own version number as the package version (since these extensions are compiled into a specific PHP release and have no independent version of their own). The classifier already attaches an explicit CPE like cpe:2.3:a:php-openssl:php-openssl:8.5.7:... to make clear this refers to the bundled PHP extension, not upstream OpenSSL. However, that CPE was marked with Source: cpe.GeneratedSource. Syft's package-finalization pipeline (internal/task/package_task_factory.go) only skips its generic, name-based CPE generation when a package already has an "authoritative" CPE (any Source other than GeneratedSource) - so it also generated and appended a second CPE, cpe:2.3:a:openssl:openssl:8.5.7:..., derived from the bare package name. That second CPE is indistinguishable from a real standalone OpenSSL/LDAP/SQLite installation and can cause vulnerability scanners that consume this SBOM to falsely flag PHP hosts as running vulnerable upstream libraries at version "8.5.7" (a PHP version, not a real OpenSSL/LDAP/SQLite release). See anchore/syft#5014 for a full repro. Approach: Mark the classifier's explicit CPE with Source: cpe.DeclaredSource instead of cpe.GeneratedSource, so it is treated as authoritative and the extra name-derived CPE is no longer generated. This mirrors the existing pattern in syft/pkg/cataloger/java/parse_jvm_release.go's newJvmCpe, which uses the same DeclaredSource + comment convention to mark a syft-synthesized (not literally file-declared) CPE as trustworthy and suppress further generation. It also brings this classifier in line with its sibling php-cli/php-fpm/php-apache classifiers in the same file, which already mark their CPEs as authoritative (via NVDDictionaryLookupSource, not applicable here since php-<ext> is not a real NVD dictionary entry). This change only suppresses the extra generated CPE; it does not change the package name, type, version, or PURL, and the existing php-<ext>:php-<ext> CPE is unaffected. Validation: go build ./... go test ./syft/pkg/cataloger/php/... ./internal/task/... ./syft/pkg/cataloger/internal/cpegenerate/... All pass except the two pre-existing docker-image-fixture subtests of Test_InterpreterCataloger, which fail in this sandbox only because the `docker` binary isn't available (exec: "docker": executable file not found in $PATH); confirmed via `git stash` that these two subtests fail identically on main without this change, i.e. this is a sandbox limitation and not a regression. Added a new unit test, Test_getClassifier_declaredCPESource, that directly asserts the classifier's CPE now carries Source: cpe.DeclaredSource. Fixes #5014 Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
This commit is contained in:
co-authored by
Pujitha Paladugu
parent
1dc16fbac4
commit
26927eab02
@@ -236,7 +236,10 @@ func (p interpreterCataloger) getClassifier(realPath string) (string, *binutils.
|
||||
Vendor: fmt.Sprintf("php-%s", name),
|
||||
Product: fmt.Sprintf("php-%s", name),
|
||||
},
|
||||
Source: cpe.GeneratedSource,
|
||||
// note: we must use a declared source here so that this CPE is treated as authoritative and no
|
||||
// further CPEs are generated from the (misleading) package name alone, e.g. cpe:...:openssl:openssl
|
||||
// for the openssl PHP extension, which would imply this is the upstream OpenSSL project.
|
||||
Source: cpe.DeclaredSource,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@ package php
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
@@ -147,3 +151,16 @@ func Test_InterpreterCataloger(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getClassifier_declaredCPESource(t *testing.T) {
|
||||
// regression test for https://github.com/anchore/syft/issues/5014
|
||||
// the CPE assigned to a PHP extension must be declared (not generated) so that downstream CPE
|
||||
// generation does not also add a misleading CPE derived from the bare extension name alone
|
||||
// (e.g. cpe:2.3:a:openssl:openssl:... for the openssl extension, implying the upstream OpenSSL project).
|
||||
c := interpreterCataloger{name: "php-interpreter-cataloger"}
|
||||
name, cls := c.getClassifier("/usr/lib/php85/modules/openssl.so")
|
||||
require.Equal(t, "openssl", name)
|
||||
require.NotNil(t, cls)
|
||||
require.Len(t, cls.CPEs, 1)
|
||||
assert.Equal(t, cpe.DeclaredSource, cls.CPEs[0].Source)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user