mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Add support for PHP Pear (#2775)
* Add support for PHP Pear and unify PECL with it Signed-off-by: Laurent Goderre <laurent.goderre@docker.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix tests Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * remove log statements Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix struct comment Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Laurent Goderre <laurent.goderre@docker.com> Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
78ef2cf53b
commit
529840bfc0
@ -140,11 +140,11 @@ Note that flags using the @<version> can be used for earlier versions of each sp
|
|||||||
- Linux kernel archives (vmlinz)
|
- Linux kernel archives (vmlinz)
|
||||||
- Linux kernel modules (ko)
|
- Linux kernel modules (ko)
|
||||||
- Nix (outputs in /nix/store)
|
- Nix (outputs in /nix/store)
|
||||||
- PHP (composer)
|
- PHP (composer, PECL, Pear)
|
||||||
- Python (wheel, egg, poetry, requirements.txt)
|
- Python (wheel, egg, poetry, requirements.txt)
|
||||||
- Red Hat (rpm)
|
- Red Hat (rpm)
|
||||||
- Ruby (gem)
|
- Ruby (gem)
|
||||||
- Rust (cargo.lock)
|
- Rust (cargo.lock, auditable binary)
|
||||||
- Swift (cocoapods, swift-package-manager)
|
- Swift (cocoapods, swift-package-manager)
|
||||||
- Wordpress plugins
|
- Wordpress plugins
|
||||||
- Terraform providers (.terraform.lock.hcl)
|
- Terraform providers (.terraform.lock.hcl)
|
||||||
|
|||||||
@ -502,8 +502,8 @@ var commonTestCases = []testCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find php pecl package",
|
name: "find php pear/pecl package",
|
||||||
pkgType: pkg.PhpPeclPkg,
|
pkgType: pkg.PhpPearPkg,
|
||||||
pkgLanguage: pkg.PHP,
|
pkgLanguage: pkg.PHP,
|
||||||
pkgInfo: map[string]string{
|
pkgInfo: map[string]string{
|
||||||
"memcached": "3.2.0",
|
"memcached": "3.2.0",
|
||||||
|
|||||||
@ -85,6 +85,7 @@ func TestPkgCoverageImage(t *testing.T) {
|
|||||||
definedPkgs.Remove(string(pkg.GithubActionPkg))
|
definedPkgs.Remove(string(pkg.GithubActionPkg))
|
||||||
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
|
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
|
||||||
definedPkgs.Remove(string(pkg.TerraformPkg))
|
definedPkgs.Remove(string(pkg.TerraformPkg))
|
||||||
|
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead
|
||||||
|
|
||||||
var cases []testCase
|
var cases []testCase
|
||||||
cases = append(cases, commonTestCases...)
|
cases = append(cases, commonTestCases...)
|
||||||
@ -227,6 +228,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
|
|||||||
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
|
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
|
||||||
definedPkgs.Remove(string(pkg.Rpkg))
|
definedPkgs.Remove(string(pkg.Rpkg))
|
||||||
definedPkgs.Remove(string(pkg.UnknownPkg))
|
definedPkgs.Remove(string(pkg.UnknownPkg))
|
||||||
|
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages
|
||||||
|
|
||||||
// for directory scans we should not expect to see any of the following package types
|
// for directory scans we should not expect to see any of the following package types
|
||||||
definedPkgs.Remove(string(pkg.KbPkg))
|
definedPkgs.Remove(string(pkg.KbPkg))
|
||||||
|
|||||||
@ -56,6 +56,7 @@ func TestAllPackageCatalogersReachableInTasks(t *testing.T) {
|
|||||||
// not reachable since they are deprecated
|
// not reachable since they are deprecated
|
||||||
"dotnet-portable-executable-cataloger",
|
"dotnet-portable-executable-cataloger",
|
||||||
"dotnet-deps-cataloger",
|
"dotnet-deps-cataloger",
|
||||||
|
"php-pecl-serialized-cataloger",
|
||||||
// not reachable by design
|
// not reachable by design
|
||||||
"sbom-cataloger",
|
"sbom-cataloger",
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
a:5:{s:4:"name";s:9:"memcached";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}}
|
a:6:{s:4:"name";s:9:"memcached";s:7:"channel";s:12:"pecl.php.net";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}}
|
||||||
@ -3,5 +3,5 @@ package internal
|
|||||||
const (
|
const (
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||||
JSONSchemaVersion = "16.0.25"
|
JSONSchemaVersion = "16.0.26"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -100,7 +100,7 @@ func DefaultPackageTaskFactories() Factories {
|
|||||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM,
|
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM,
|
||||||
),
|
),
|
||||||
newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"),
|
newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"),
|
||||||
newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pecl"),
|
newSimplePackageTaskFactory(php.NewPearCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pear"),
|
||||||
newPackageTaskFactory(
|
newPackageTaskFactory(
|
||||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||||
return python.NewPackageCataloger(cfg.PackagesConfig.Python)
|
return python.NewPackageCataloger(cfg.PackagesConfig.Python)
|
||||||
@ -166,5 +166,6 @@ func DefaultPackageTaskFactories() Factories {
|
|||||||
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
|
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
|
||||||
newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
|
newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
|
||||||
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
|
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
|
||||||
|
newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2944
schema/json/schema-16.0.26.json
Normal file
2944
schema/json/schema-16.0.26.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "anchore.io/schema/syft/json/16.0.25/document",
|
"$id": "anchore.io/schema/syft/json/16.0.26/document",
|
||||||
"$ref": "#/$defs/Document",
|
"$ref": "#/$defs/Document",
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"AlpmDbEntry": {
|
"AlpmDbEntry": {
|
||||||
@ -1872,6 +1872,9 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/$defs/PhpComposerLockEntry"
|
"$ref": "#/$defs/PhpComposerLockEntry"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/PhpPearEntry"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/$defs/PhpPeclEntry"
|
"$ref": "#/$defs/PhpPeclEntry"
|
||||||
},
|
},
|
||||||
@ -2164,11 +2167,38 @@
|
|||||||
"dist"
|
"dist"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"PhpPearEntry": {
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"channel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"version"
|
||||||
|
]
|
||||||
|
},
|
||||||
"PhpPeclEntry": {
|
"PhpPeclEntry": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"channel": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"version": {
|
"version": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -34,6 +34,7 @@ func Test_OriginatorSupplier(t *testing.T) {
|
|||||||
pkg.NixStoreEntry{},
|
pkg.NixStoreEntry{},
|
||||||
pkg.NpmPackageLockEntry{},
|
pkg.NpmPackageLockEntry{},
|
||||||
pkg.PhpComposerInstalledEntry{},
|
pkg.PhpComposerInstalledEntry{},
|
||||||
|
pkg.PhpPearEntry{},
|
||||||
pkg.PhpPeclEntry{},
|
pkg.PhpPeclEntry{},
|
||||||
pkg.PortageEntry{},
|
pkg.PortageEntry{},
|
||||||
pkg.PythonPipfileLockEntry{},
|
pkg.PythonPipfileLockEntry{},
|
||||||
|
|||||||
@ -40,6 +40,8 @@ func SourceInfo(p pkg.Package) string {
|
|||||||
answer = "acquired package info from rust cargo manifest"
|
answer = "acquired package info from rust cargo manifest"
|
||||||
case pkg.PhpComposerPkg:
|
case pkg.PhpComposerPkg:
|
||||||
answer = "acquired package info from PHP composer manifest"
|
answer = "acquired package info from PHP composer manifest"
|
||||||
|
case pkg.PhpPearPkg:
|
||||||
|
answer = "acquired package info from PHP Pear manifest"
|
||||||
case pkg.PhpPeclPkg:
|
case pkg.PhpPeclPkg:
|
||||||
answer = "acquired package info from PHP Pecl manifest"
|
answer = "acquired package info from PHP Pecl manifest"
|
||||||
case pkg.CocoapodsPkg:
|
case pkg.CocoapodsPkg:
|
||||||
|
|||||||
@ -143,6 +143,14 @@ func Test_SourceInfo(t *testing.T) {
|
|||||||
"from PHP Pecl manifest",
|
"from PHP Pecl manifest",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: pkg.Package{
|
||||||
|
Type: pkg.PhpPearPkg,
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"from PHP Pear manifest",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: pkg.Package{
|
input: pkg.Package{
|
||||||
Type: pkg.DartPubPkg,
|
Type: pkg.DartPubPkg,
|
||||||
|
|||||||
@ -42,6 +42,7 @@ func AllTypes() []any {
|
|||||||
pkg.OpamPackage{},
|
pkg.OpamPackage{},
|
||||||
pkg.PhpComposerInstalledEntry{},
|
pkg.PhpComposerInstalledEntry{},
|
||||||
pkg.PhpComposerLockEntry{},
|
pkg.PhpComposerLockEntry{},
|
||||||
|
pkg.PhpPearEntry{},
|
||||||
pkg.PhpPeclEntry{},
|
pkg.PhpPeclEntry{},
|
||||||
pkg.PortageEntry{},
|
pkg.PortageEntry{},
|
||||||
pkg.PythonPackage{},
|
pkg.PythonPackage{},
|
||||||
|
|||||||
@ -96,6 +96,7 @@ var jsonTypes = makeJSONTypes(
|
|||||||
jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
|
jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
|
||||||
jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
|
jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
|
||||||
jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"),
|
jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"),
|
||||||
|
jsonNames(pkg.PhpPearEntry{}, "php-pear-entry"),
|
||||||
jsonNames(pkg.PortageEntry{}, "portage-db-entry", "PortageMetadata"),
|
jsonNames(pkg.PortageEntry{}, "portage-db-entry", "PortageMetadata"),
|
||||||
jsonNames(pkg.PythonPackage{}, "python-package", "PythonPackageMetadata"),
|
jsonNames(pkg.PythonPackage{}, "python-package", "PythonPackageMetadata"),
|
||||||
jsonNames(pkg.PythonPipfileLockEntry{}, "python-pipfile-lock-entry", "PythonPipfileLockMetadata"),
|
jsonNames(pkg.PythonPipfileLockEntry{}, "python-pipfile-lock-entry", "PythonPipfileLockMetadata"),
|
||||||
|
|||||||
@ -23,8 +23,16 @@ func NewComposerLockCataloger() pkg.Cataloger {
|
|||||||
WithParserByGlobs(parseComposerLock, "**/composer.lock")
|
WithParserByGlobs(parseComposerLock, "**/composer.lock")
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPeclCataloger returns a new cataloger for PHP PECL metadata“.
|
// NewPearCataloger returns a new cataloger for PHP Pear metadata (including Pecl metadata).
|
||||||
|
func NewPearCataloger() pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("php-pear-serialized-cataloger").
|
||||||
|
WithParserByGlobs(parsePear, "**/php/.registry/**/*.reg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPeclCataloger returns a new cataloger for PHP Pecl metadata. Note: this will also catalog Pear metadata so should
|
||||||
|
// not be used in conjunction with the Pear Cataloger.
|
||||||
|
// Deprecated: please use NewPearCataloger instead.
|
||||||
func NewPeclCataloger() pkg.Cataloger {
|
func NewPeclCataloger() pkg.Cataloger {
|
||||||
return generic.NewCataloger("php-pecl-serialized-cataloger").
|
return generic.NewCataloger("php-pecl-serialized-cataloger").
|
||||||
WithParserByGlobs(parsePeclSerialized, "**/php/.registry/.channel.*/*.reg")
|
WithParserByGlobs(parsePecl, "**/php/.registry/.channel.*/*.reg")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,6 +56,31 @@ func Test_ComposerLockCataloger_Globs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_PearCataloger_Globs(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fixture string
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "obtain pear files",
|
||||||
|
fixture: "test-fixtures/glob-paths",
|
||||||
|
expected: []string{
|
||||||
|
"php/.registry/.channel.pecl.php.net/memcached.reg",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
pkgtest.NewCatalogTester().
|
||||||
|
FromDirectory(t, test.fixture).
|
||||||
|
ExpectsResolverContentQueries(test.expected).
|
||||||
|
TestCataloger(t, NewPearCataloger())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Test_PeclCataloger_Globs(t *testing.T) {
|
func Test_PeclCataloger_Globs(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@ -63,7 +88,7 @@ func Test_PeclCataloger_Globs(t *testing.T) {
|
|||||||
expected []string
|
expected []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "obtain pecl files",
|
name: "obtain pear files",
|
||||||
fixture: "test-fixtures/glob-paths",
|
fixture: "test-fixtures/glob-paths",
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"php/.registry/.channel.pecl.php.net/memcached.reg",
|
"php/.registry/.channel.pecl.php.net/memcached.reg",
|
||||||
|
|||||||
@ -14,7 +14,7 @@ func newComposerLockPackage(pd parsedLockData, indexLocation file.Location) pkg.
|
|||||||
Version: pd.Version,
|
Version: pd.Version,
|
||||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||||
PURL: packageURL(pd.Name, pd.Version),
|
PURL: packageURLFromComposer(pd.Name, pd.Version),
|
||||||
Language: pkg.PHP,
|
Language: pkg.PHP,
|
||||||
Type: pkg.PhpComposerPkg,
|
Type: pkg.PhpComposerPkg,
|
||||||
Metadata: pd.PhpComposerLockEntry,
|
Metadata: pd.PhpComposerLockEntry,
|
||||||
@ -30,7 +30,7 @@ func newComposerInstalledPackage(pd parsedInstalledData, indexLocation file.Loca
|
|||||||
Version: pd.Version,
|
Version: pd.Version,
|
||||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||||
PURL: packageURL(pd.Name, pd.Version),
|
PURL: packageURLFromComposer(pd.Name, pd.Version),
|
||||||
Language: pkg.PHP,
|
Language: pkg.PHP,
|
||||||
Type: pkg.PhpComposerPkg,
|
Type: pkg.PhpComposerPkg,
|
||||||
Metadata: pd.PhpComposerInstalledEntry,
|
Metadata: pd.PhpComposerInstalledEntry,
|
||||||
@ -40,23 +40,39 @@ func newComposerInstalledPackage(pd parsedInstalledData, indexLocation file.Loca
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func newPeclPackage(pd pkg.PhpPeclEntry, indexLocation file.Location) pkg.Package {
|
func newPearPackage(pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||||
p := pkg.Package{
|
p := pkg.Package{
|
||||||
Name: pd.Name,
|
Name: pd.Name,
|
||||||
Version: pd.Version,
|
Version: pd.Version,
|
||||||
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||||
PURL: packageURLFromPecl(pd.Name, pd.Version),
|
PURL: packageURLFromPear(pd.Name, pd.Channel, pd.Version),
|
||||||
Language: pkg.PHP,
|
Language: pkg.PHP,
|
||||||
Type: pkg.PhpPeclPkg,
|
Type: pkg.PhpPearPkg,
|
||||||
Metadata: pd,
|
Metadata: pd.ToPear(),
|
||||||
}
|
}
|
||||||
|
|
||||||
p.SetID()
|
p.SetID()
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func packageURL(name, version string) string {
|
func newPeclPackage(pd peclPearData, indexLocation file.Location) pkg.Package {
|
||||||
|
p := pkg.Package{
|
||||||
|
Name: pd.Name,
|
||||||
|
Version: pd.Version,
|
||||||
|
Locations: file.NewLocationSet(indexLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||||
|
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(indexLocation, pd.License...)...),
|
||||||
|
PURL: packageURLFromPear(pd.Name, pd.Channel, pd.Version),
|
||||||
|
Language: pkg.PHP,
|
||||||
|
Type: pkg.PhpPeclPkg,
|
||||||
|
Metadata: pd.ToPecl(),
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetID()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func packageURLFromComposer(name, version string) string {
|
||||||
var pkgName, vendor string
|
var pkgName, vendor string
|
||||||
fields := strings.Split(name, "/")
|
fields := strings.Split(name, "/")
|
||||||
switch len(fields) {
|
switch len(fields) {
|
||||||
@ -82,10 +98,15 @@ func packageURL(name, version string) string {
|
|||||||
return pURL.ToString()
|
return pURL.ToString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func packageURLFromPecl(pkgName, version string) string {
|
func packageURLFromPear(pkgName, channel, version string) string {
|
||||||
|
namespace := channel
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "pecl.php.net"
|
||||||
|
}
|
||||||
|
|
||||||
pURL := packageurl.NewPackageURL(
|
pURL := packageurl.NewPackageURL(
|
||||||
"pecl",
|
"pear",
|
||||||
"",
|
namespace,
|
||||||
pkgName,
|
pkgName,
|
||||||
version,
|
version,
|
||||||
nil,
|
nil,
|
||||||
|
|||||||
@ -35,7 +35,7 @@ func Test_packageURL(t *testing.T) {
|
|||||||
|
|
||||||
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 := packageURL(test.packageName, test.packageVersion)
|
actual := packageURLFromComposer(test.packageName, test.packageVersion)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
@ -45,22 +45,30 @@ func Test_packageURL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_packageURLFromPecl(t *testing.T) {
|
func Test_packageURLFromPear(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
channel string
|
||||||
version string
|
version string
|
||||||
expected string
|
expected string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "memcached",
|
name: "memcached",
|
||||||
|
channel: "pear.php.net",
|
||||||
version: "3.2.0",
|
version: "3.2.0",
|
||||||
expected: "pkg:pecl/memcached@3.2.0",
|
expected: "pkg:pear/pear.php.net/memcached@3.2.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "memcached",
|
||||||
|
channel: "", // important!
|
||||||
|
version: "3.2.0",
|
||||||
|
expected: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
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 := packageURLFromPecl(test.name, test.version)
|
actual := packageURLFromPear(test.name, test.channel, test.version)
|
||||||
if actual != test.expected {
|
if actual != test.expected {
|
||||||
dmp := diffmatchpatch.New()
|
dmp := diffmatchpatch.New()
|
||||||
diffs := dmp.DiffMain(test.expected, actual, true)
|
diffs := dmp.DiffMain(test.expected, actual, true)
|
||||||
|
|||||||
112
syft/pkg/cataloger/php/parse_pecl_pear.go
Normal file
112
syft/pkg/cataloger/php/parse_pecl_pear.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package php
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/elliotchance/phpserialize"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/unknown"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peclPearData struct {
|
||||||
|
Name string
|
||||||
|
Channel string
|
||||||
|
Version string
|
||||||
|
License []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *peclPearData) ToPear() pkg.PhpPearEntry {
|
||||||
|
return pkg.PhpPearEntry{
|
||||||
|
Name: p.Name,
|
||||||
|
Channel: p.Channel,
|
||||||
|
Version: p.Version,
|
||||||
|
License: p.License,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *peclPearData) ToPecl() pkg.PhpPeclEntry {
|
||||||
|
return pkg.PhpPeclEntry(p.ToPear())
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePecl(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
m, err := parsePeclPearSerialized(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pecl package found"))
|
||||||
|
}
|
||||||
|
return []pkg.Package{newPeclPackage(*m, reader.Location)}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePear(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
m, err := parsePeclPearSerialized(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil, unknown.New(reader.Location, fmt.Errorf("no pear package found"))
|
||||||
|
}
|
||||||
|
return []pkg.Package{newPearPackage(*m, reader.Location)}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parsePeclPearSerialized is a parser function for Pear metadata contents, returning "Default" php packages discovered.
|
||||||
|
func parsePeclPearSerialized(reader file.LocationReadCloser) (*peclPearData, error) {
|
||||||
|
data, err := io.ReadAll(reader)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata, err := phpserialize.UnmarshalAssociativeArray(
|
||||||
|
data,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse pear metadata file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
name, ok := metadata["name"].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("failed to parse pear package name: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, ok := metadata["channel"].(string)
|
||||||
|
if !ok {
|
||||||
|
// this could be the v5 format
|
||||||
|
channel = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
version := readStruct(metadata, "version", "release")
|
||||||
|
license := readStruct(metadata, "license", "_content")
|
||||||
|
|
||||||
|
return &peclPearData{
|
||||||
|
Name: name,
|
||||||
|
Channel: channel,
|
||||||
|
Version: version,
|
||||||
|
License: []string{
|
||||||
|
license,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStruct(metadata any, fields ...string) string {
|
||||||
|
if len(fields) > 0 {
|
||||||
|
value, ok := metadata.(map[any]any)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return readStruct(value[fields[0]], fields[1:]...)
|
||||||
|
}
|
||||||
|
value, ok := metadata.(string)
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
139
syft/pkg/cataloger/php/parse_pecl_pear_test.go
Normal file
139
syft/pkg/cataloger/php/parse_pecl_pear_test.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package php
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParsePear(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fixture string
|
||||||
|
expectedPkgs []pkg.Package
|
||||||
|
expectedRelationships []artifact.Relationship
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v6 format",
|
||||||
|
fixture: "test-fixtures/memcached-v6-format.reg",
|
||||||
|
expectedPkgs: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "memcached",
|
||||||
|
Version: "3.2.0",
|
||||||
|
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||||
|
Licenses: pkg.NewLicenseSet(
|
||||||
|
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||||
|
),
|
||||||
|
Language: pkg.PHP,
|
||||||
|
Type: pkg.PhpPearPkg,
|
||||||
|
Metadata: pkg.PhpPearEntry{
|
||||||
|
Name: "memcached",
|
||||||
|
Channel: "pecl.php.net",
|
||||||
|
Version: "3.2.0",
|
||||||
|
License: []string{"PHP License"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v5 format",
|
||||||
|
fixture: "test-fixtures/memcached-v5-format.reg",
|
||||||
|
expectedPkgs: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "memcached",
|
||||||
|
Version: "3.2.0",
|
||||||
|
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||||
|
Licenses: pkg.NewLicenseSet(
|
||||||
|
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||||
|
),
|
||||||
|
Language: pkg.PHP,
|
||||||
|
Type: pkg.PhpPearPkg,
|
||||||
|
Metadata: pkg.PhpPearEntry{ // important: missing channel
|
||||||
|
Name: "memcached",
|
||||||
|
Version: "3.2.0",
|
||||||
|
License: []string{"PHP License"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
pkgtest.TestFileParser(t, tt.fixture, parsePear, tt.expectedPkgs, tt.expectedRelationships)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParsePecl(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fixture string
|
||||||
|
expectedPkgs []pkg.Package
|
||||||
|
expectedRelationships []artifact.Relationship
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "v6 format",
|
||||||
|
fixture: "test-fixtures/memcached-v6-format.reg",
|
||||||
|
expectedPkgs: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "memcached",
|
||||||
|
Version: "3.2.0",
|
||||||
|
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||||
|
Licenses: pkg.NewLicenseSet(
|
||||||
|
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v6-format.reg")),
|
||||||
|
),
|
||||||
|
Language: pkg.PHP,
|
||||||
|
Type: pkg.PhpPeclPkg, // important!
|
||||||
|
Metadata: pkg.PhpPeclEntry{ // important!
|
||||||
|
Name: "memcached",
|
||||||
|
Channel: "pecl.php.net",
|
||||||
|
Version: "3.2.0",
|
||||||
|
License: []string{"PHP License"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "v5 format",
|
||||||
|
fixture: "test-fixtures/memcached-v5-format.reg",
|
||||||
|
expectedPkgs: []pkg.Package{
|
||||||
|
{
|
||||||
|
Name: "memcached",
|
||||||
|
Version: "3.2.0",
|
||||||
|
PURL: "pkg:pear/pecl.php.net/memcached@3.2.0",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||||
|
Licenses: pkg.NewLicenseSet(
|
||||||
|
pkg.NewLicenseFromLocations("PHP License", file.NewLocation("test-fixtures/memcached-v5-format.reg")),
|
||||||
|
),
|
||||||
|
Language: pkg.PHP,
|
||||||
|
Type: pkg.PhpPeclPkg, // important!
|
||||||
|
Metadata: pkg.PhpPeclEntry{ // important!
|
||||||
|
Name: "memcached",
|
||||||
|
Version: "3.2.0",
|
||||||
|
License: []string{"PHP License"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
pkgtest.TestFileParser(t, tt.fixture, parsePecl, tt.expectedPkgs, tt.expectedRelationships)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_corruptPecl(t *testing.T) {
|
||||||
|
pkgtest.NewCatalogTester().
|
||||||
|
FromFile(t, "test-fixtures/glob-paths/php/.registry/.channel.pecl.php.net/memcached.reg").
|
||||||
|
WithError().
|
||||||
|
TestParser(t, parseComposerLock)
|
||||||
|
}
|
||||||
@ -1,73 +0,0 @@
|
|||||||
package php
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/elliotchance/phpserialize"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
|
||||||
)
|
|
||||||
|
|
||||||
// parsePeclSerialized is a parser function for PECL metadata contents, returning "Default" php packages discovered.
|
|
||||||
func parsePeclSerialized(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
|
||||||
var pkgs []pkg.Package
|
|
||||||
data, err := io.ReadAll(reader)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to read file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
metadata, err := phpserialize.UnmarshalAssociativeArray(
|
|
||||||
data,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("failed to parse pecl metadata file: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
name, ok := metadata["name"].(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("failed to parse pecl package name: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
version := readStruct(metadata, "version", "release")
|
|
||||||
license := readStruct(metadata, "license", "_content")
|
|
||||||
|
|
||||||
pkgs = append(
|
|
||||||
pkgs,
|
|
||||||
newPeclPackage(
|
|
||||||
pkg.PhpPeclEntry{
|
|
||||||
Name: name,
|
|
||||||
Version: version,
|
|
||||||
License: []string{
|
|
||||||
license,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
reader.Location,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
return pkgs, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStruct(metadata any, fields ...string) string {
|
|
||||||
if len(fields) > 0 {
|
|
||||||
value, ok := metadata.(map[any]any)
|
|
||||||
if !ok {
|
|
||||||
log.Tracef("unable to read '%s' from: %v", fields[0], metadata)
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return readStruct(value[fields[0]], fields[1:]...)
|
|
||||||
}
|
|
||||||
value, ok := metadata.(string)
|
|
||||||
if !ok {
|
|
||||||
log.Tracef("unable to read value from: %v", metadata)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
package php
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/file"
|
|
||||||
"github.com/anchore/syft/syft/pkg"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParsePeclSerialized(t *testing.T) {
|
|
||||||
var expectedRelationships []artifact.Relationship
|
|
||||||
fixture := "test-fixtures/memcached.reg"
|
|
||||||
locations := file.NewLocationSet(file.NewLocation(fixture))
|
|
||||||
expectedPkgs := []pkg.Package{
|
|
||||||
{
|
|
||||||
Name: "memcached",
|
|
||||||
Version: "3.2.0",
|
|
||||||
PURL: "pkg:pecl/memcached@3.2.0",
|
|
||||||
Locations: locations,
|
|
||||||
Licenses: pkg.NewLicenseSet(
|
|
||||||
pkg.NewLicenseFromLocations("PHP License", file.NewLocation(fixture)),
|
|
||||||
),
|
|
||||||
Language: pkg.PHP,
|
|
||||||
Type: pkg.PhpPeclPkg,
|
|
||||||
Metadata: pkg.PhpPeclEntry{
|
|
||||||
Name: "memcached",
|
|
||||||
Version: "3.2.0",
|
|
||||||
License: []string{"PHP License"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
pkgtest.TestFileParser(t, fixture, parsePeclSerialized, expectedPkgs, expectedRelationships)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_corruptPecl(t *testing.T) {
|
|
||||||
pkgtest.NewCatalogTester().
|
|
||||||
FromFile(t, "test-fixtures/glob-paths/php/.registry/.channel.pecl.php.net/memcached.reg").
|
|
||||||
WithError().
|
|
||||||
TestParser(t, parseComposerLock)
|
|
||||||
}
|
|
||||||
@ -0,0 +1 @@
|
|||||||
|
a:6:{s:4:"name";s:9:"memcached";s:7:"channel";s:12:"pecl.php.net";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}}
|
||||||
@ -37,8 +37,14 @@ type PhpComposerAuthors struct {
|
|||||||
Homepage string `json:"homepage,omitempty"`
|
Homepage string `json:"homepage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PhpPeclEntry struct {
|
// PhpPeclEntry represents a single package entry found within php pecl metadata files.
|
||||||
|
// Deprecated: please use PhpPearEntry instead with the pear cataloger.
|
||||||
|
type PhpPeclEntry PhpPearEntry
|
||||||
|
|
||||||
|
// PhpPearEntry represents a single package entry found within php pear metadata files.
|
||||||
|
type PhpPearEntry struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Channel string `json:"channel,omitempty"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
License []string `json:"license,omitempty"`
|
License []string `json:"license,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,8 @@ const (
|
|||||||
NpmPkg Type = "npm"
|
NpmPkg Type = "npm"
|
||||||
OpamPkg Type = "opam"
|
OpamPkg Type = "opam"
|
||||||
PhpComposerPkg Type = "php-composer"
|
PhpComposerPkg Type = "php-composer"
|
||||||
PhpPeclPkg Type = "php-pecl"
|
PhpPeclPkg Type = "php-pecl" // Deprecated: will be removed in syft v2.0
|
||||||
|
PhpPearPkg Type = "php-pear"
|
||||||
PortagePkg Type = "portage"
|
PortagePkg Type = "portage"
|
||||||
PythonPkg Type = "python"
|
PythonPkg Type = "python"
|
||||||
Rpkg Type = "R-package"
|
Rpkg Type = "R-package"
|
||||||
@ -78,6 +79,7 @@ var AllPkgs = []Type{
|
|||||||
OpamPkg,
|
OpamPkg,
|
||||||
PhpComposerPkg,
|
PhpComposerPkg,
|
||||||
PhpPeclPkg,
|
PhpPeclPkg,
|
||||||
|
PhpPearPkg,
|
||||||
PortagePkg,
|
PortagePkg,
|
||||||
PythonPkg,
|
PythonPkg,
|
||||||
Rpkg,
|
Rpkg,
|
||||||
@ -132,8 +134,8 @@ func (t Type) PackageURLType() string {
|
|||||||
return packageurl.TypeGeneric
|
return packageurl.TypeGeneric
|
||||||
case PhpComposerPkg:
|
case PhpComposerPkg:
|
||||||
return packageurl.TypeComposer
|
return packageurl.TypeComposer
|
||||||
case PhpPeclPkg:
|
case PhpPearPkg, PhpPeclPkg:
|
||||||
return "pecl"
|
return "pear"
|
||||||
case PythonPkg:
|
case PythonPkg:
|
||||||
return packageurl.TypePyPi
|
return packageurl.TypePyPi
|
||||||
case PortagePkg:
|
case PortagePkg:
|
||||||
@ -192,8 +194,8 @@ func TypeByName(name string) Type {
|
|||||||
return DebPkg
|
return DebPkg
|
||||||
case packageurl.TypeComposer:
|
case packageurl.TypeComposer:
|
||||||
return PhpComposerPkg
|
return PhpComposerPkg
|
||||||
case "pecl":
|
case "pear", "pecl":
|
||||||
return PhpPeclPkg
|
return PhpPearPkg
|
||||||
case packageurl.TypeGolang:
|
case packageurl.TypeGolang:
|
||||||
return GoModulePkg
|
return GoModulePkg
|
||||||
case packageurl.TypeGem:
|
case packageurl.TypeGem:
|
||||||
|
|||||||
@ -63,8 +63,16 @@ func TestTypeFromPURL(t *testing.T) {
|
|||||||
expected: PhpComposerPkg,
|
expected: PhpComposerPkg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
purl: "pkg:pecl/memcached@3.2.0",
|
purl: "pkg:pear/pecl.php.net/memcached@3.2.0", // pecl namespace
|
||||||
expected: PhpPeclPkg,
|
expected: PhpPearPkg,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
purl: "pkg:pear/pear.php.net/memcached@3.2.0", // pear namespace
|
||||||
|
expected: PhpPearPkg,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
purl: "pkg:pecl/pecl.php.net/memcached@3.2.0", // note: this is an invalid purl, but we will handle it anyway in case folks created the type pre-emptively
|
||||||
|
expected: PhpPearPkg, // we should still consider this a pear package
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
purl: "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=zip&classifier=dist",
|
purl: "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=zip&classifier=dist",
|
||||||
@ -124,7 +132,7 @@ func TestTypeFromPURL(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var pkgTypes []string
|
var pkgTypes = strset.New()
|
||||||
var expectedTypes = strset.New()
|
var expectedTypes = strset.New()
|
||||||
for _, ty := range AllPkgs {
|
for _, ty := range AllPkgs {
|
||||||
expectedTypes.Add(string(ty))
|
expectedTypes.Add(string(ty))
|
||||||
@ -141,19 +149,19 @@ func TestTypeFromPURL(t *testing.T) {
|
|||||||
expectedTypes.Remove(string(WordpressPluginPkg))
|
expectedTypes.Remove(string(WordpressPluginPkg))
|
||||||
expectedTypes.Remove(string(TerraformPkg))
|
expectedTypes.Remove(string(TerraformPkg))
|
||||||
expectedTypes.Remove(string(GraalVMNativeImagePkg))
|
expectedTypes.Remove(string(GraalVMNativeImagePkg))
|
||||||
|
expectedTypes.Remove(string(PhpPeclPkg)) // we should always consider this a pear package
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(string(test.expected), func(t *testing.T) {
|
t.Run(string(test.expected), func(t *testing.T) {
|
||||||
actual := TypeFromPURL(test.purl)
|
actual := TypeFromPURL(test.purl)
|
||||||
|
|
||||||
if actual != "" {
|
if actual != "" {
|
||||||
pkgTypes = append(pkgTypes, string(actual))
|
pkgTypes.Add(string(actual))
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, test.expected, actual)
|
assert.Equal(t, test.expected, actual)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.ElementsMatch(t, expectedTypes.List(), pkgTypes, "missing one or more package types to test against (maybe a package type was added?)")
|
assert.ElementsMatch(t, expectedTypes.List(), pkgTypes.List(), "missing one or more package types to test against (maybe a package type was added?)")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user