diff --git a/Taskfile.yaml b/Taskfile.yaml index 7261a36f225..61312f40e21 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -390,7 +390,7 @@ tasks: generate-cpe-dictionary-index: desc: Generate the CPE index based off of the latest available CPE dictionary - dir: "syft/pkg/cataloger/common/cpe/dictionary" + dir: "syft/pkg/cataloger/internal/cpegenerate/dictionary" cmds: - "go generate" diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json b/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json index 26060565f78..6f4a6ca1ff4 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json @@ -1031,6 +1031,19 @@ "zjjserver": "cpe:2.3:a:zjjserver_project:zjjserver:*:*:*:*:*:node.js:*:*", "zwserver": "cpe:2.3:a:zwserver_project:zwserver:*:*:*:*:*:node.js:*:*" }, + "php_pear": { + "Archive_Tar": "cpe:2.3:a:php:pear_archive_tar:*:*:*:*:*:*:*:*", + "HTML_AJAX": "cpe:2.3:a:pear:html_ajax:*:*:*:*:*:*:*:*", + "HTML_QuickForm": "cpe:2.3:a:html_quickform_project:html_quickform:*:*:*:*:*:*:*:*", + "PEAR": "cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*", + "XML_RPC": "cpe:2.3:a:php:xml_rpc:*:*:*:*:*:pear:*:*" + }, + "php_pecl": { + "imagick": "cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*", + "memcached": "cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*", + "pecl_http": "cpe:2.3:a:php:pecl_http:*:*:*:*:*:*:*:*", + "xhprof": "cpe:2.3:a:php:xhprof:*:*:*:*:*:*:*:*" + }, "pypi": { "0.0.1": "cpe:2.3:a:pypi:pypi:*:*:*:*:*:*:*:*", "AAmiles": "cpe:2.3:a:pypi:aamiles:*:*:*:*:*:pypi:*:*", diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate.go b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate.go index 23868e30f35..24746b3f021 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate.go +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate.go @@ -108,6 +108,10 @@ const ( prefixForPyPIPackages = "https://pypi.org/project/" prefixForJenkinsPlugins = "https://github.com/jenkinsci/" prefixForRustCrates = "https://crates.io/crates/" + prefixForPHPPear = "https://pear.php.net/" + prefixForPHPPearHTTP = "http://pear.php.net/" + prefixForPHPPecl = "https://pecl.php.net/" + prefixForPHPPeclHTTP = "http://pecl.php.net/" ) // indexCPEList creates an index of CPEs by ecosystem. @@ -141,6 +145,12 @@ func indexCPEList(list CpeList) *dictionary.Indexed { case strings.HasPrefix(ref, prefixForRustCrates): addEntryForRustCrate(indexed, ref, cpeItemName) + + case strings.HasPrefix(ref, prefixForPHPPear), strings.HasPrefix(ref, prefixForPHPPearHTTP): + addEntryForPHPPearPackage(indexed, ref, cpeItemName) + + case strings.HasPrefix(ref, prefixForPHPPecl), strings.HasPrefix(ref, prefixForPHPPeclHTTP): + addEntryForPHPPeclPackage(indexed, ref, cpeItemName) } } } @@ -228,3 +238,63 @@ func addEntryForNPMPackage(indexed *dictionary.Indexed, ref string, cpeItemName indexed.EcosystemPackages[dictionary.EcosystemNPM][ref] = cpeItemName } + +func phpExtensionPackageFromURLFragment(ref string) string { + if strings.HasPrefix(ref, "package/") { // package/HTML_QuickForm/download + ref = strings.TrimPrefix(ref, "package/") + components := strings.Split(ref, "/") + + if len(components) < 1 { + return "" + } + + ref = components[0] + } else if strings.Contains(ref, "?package=") { // package-changelog.php?package=xhprof&release=0.9.4 + components := strings.Split(ref, "?package=") + + if len(components) < 2 { + return "" + } + + components = strings.Split(components[1], "&") + if len(components) < 2 { + return "" + } + + ref = components[0] + } + + return ref +} + +func addEntryForPHPPearPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) { + ref = strings.TrimPrefix(ref, prefixForPHPPear) + ref = strings.TrimPrefix(ref, prefixForPHPPearHTTP) + ref = phpExtensionPackageFromURLFragment(ref) + + if ref == "" { + return + } + + if _, ok := indexed.EcosystemPackages[dictionary.EcosystemPHPPear]; !ok { + indexed.EcosystemPackages[dictionary.EcosystemPHPPear] = make(dictionary.Packages) + } + + indexed.EcosystemPackages[dictionary.EcosystemPHPPear][ref] = cpeItemName +} + +func addEntryForPHPPeclPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) { + ref = strings.TrimPrefix(ref, prefixForPHPPecl) + ref = strings.TrimPrefix(ref, prefixForPHPPeclHTTP) + ref = phpExtensionPackageFromURLFragment(ref) + + if ref == "" { + return + } + + if _, ok := indexed.EcosystemPackages[dictionary.EcosystemPHPPecl]; !ok { + indexed.EcosystemPackages[dictionary.EcosystemPHPPecl] = make(dictionary.Packages) + } + + indexed.EcosystemPackages[dictionary.EcosystemPHPPecl][ref] = cpeItemName +} diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate_test.go b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate_test.go index db13735d742..267ffe011da 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate_test.go +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/generate_test.go @@ -151,6 +151,58 @@ func Test_addEntryFuncs(t *testing.T) { }, }, }, + { + name: "addEntryForPHPPeclPackage", + addEntryFunc: addEntryForPHPPeclPackage, + inputRef: "https://pecl.php.net/package/imagick/something/something/v4007.0", + inputCpeItemName: "cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*", + expectedIndexed: dictionary.Indexed{ + EcosystemPackages: map[string]dictionary.Packages{ + dictionary.EcosystemPHPPecl: { + "imagick": "cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*", + }, + }, + }, + }, + { + name: "addEntryForPHPPeclPackage http changelog", + addEntryFunc: addEntryForPHPPeclPackage, + inputRef: "http://pecl.php.net/package-changelog.php?package=memcached&release", + inputCpeItemName: "cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*", + expectedIndexed: dictionary.Indexed{ + EcosystemPackages: map[string]dictionary.Packages{ + dictionary.EcosystemPHPPecl: { + "memcached": "cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*", + }, + }, + }, + }, + { + name: "addEntryForPHPPearPackage", + addEntryFunc: addEntryForPHPPearPackage, + inputRef: "https://pear.php.net/package/PEAR/download", + inputCpeItemName: "cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*", + expectedIndexed: dictionary.Indexed{ + EcosystemPackages: map[string]dictionary.Packages{ + dictionary.EcosystemPHPPear: { + "PEAR": "cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*", + }, + }, + }, + }, + { + name: "addEntryForPHPPearPackage http changelog", + addEntryFunc: addEntryForPHPPearPackage, + inputRef: "http://pear.php.net/package-changelog.php?package=abcdefg&release", + inputCpeItemName: "cpe:2.3:a:php:abcdefg:*:*:*:*:*:*:*:*", + expectedIndexed: dictionary.Indexed{ + EcosystemPackages: map[string]dictionary.Packages{ + dictionary.EcosystemPHPPear: { + "abcdefg": "cpe:2.3:a:php:abcdefg:*:*:*:*:*:*:*:*", + }, + }, + }, + }, } for _, tt := range tests { diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/expected-cpe-index.json b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/expected-cpe-index.json index c6f95615961..bf34cee57b3 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/expected-cpe-index.json +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/expected-cpe-index.json @@ -13,6 +13,16 @@ "unicode": "cpe:2.3:a:unicode_project:unicode:*:*:*:*:*:node.js:*:*", "unicorn-list": "cpe:2.3:a:unicorn-list_project:unicorn-list:*:*:*:*:*:node.js:*:*" }, + "php_pear": { + "HTML_QuickForm": "cpe:2.3:a:html_quickform_project:html_quickform:*:*:*:*:*:*:*:*", + "PEAR": "cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*", + "XML_RPC": "cpe:2.3:a:php:xml_rpc:*:*:*:*:*:pear:*:*" + }, + "php_pecl": { + "imagick": "cpe:2.3:a:php:imagick:*:*:*:*:*:*:*:*", + "memcached": "cpe:2.3:a:php:memcached:*:*:*:*:*:*:*:*", + "xhprof": "cpe:2.3:a:php:xhprof:*:*:*:*:*:*:*:*" + }, "rubygems": { "openssl": "cpe:2.3:a:ruby-lang:openssl:*:*:*:*:*:*:*:*" }, diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/official-cpe-dictionary_v2.3.xml b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/official-cpe-dictionary_v2.3.xml index 5cd48842ee7..ac5cb0c8f30 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/official-cpe-dictionary_v2.3.xml +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/index-generator/testdata/official-cpe-dictionary_v2.3.xml @@ -24873,4 +24873,114 @@ + + PHP XHProf 0.9.1 + + product changelog + + + + + PHP XHProf 0.9.2 + + product changelog + + + + + PHP XHProf 0.9.3 + + product changelog + + + + + PHP memecached 0.1.0 + + Change Log + + + + + PHP memecached 0.1.2 + + Change Log + + + + + PHP memecached 0.1.3 + + Change Log + + + + + PHP Imagick 3.3.0 + + Version + + + + + PHP Imagick 3.4.0 + + Version + + + + + PHP XML_RPC 1.4.4 for Pear + + Version + Product + + + + + PHP XML_RPC 1.4.5 for Pear + + Version + Product + + + + + PHP XML_RPC 1.4.6 for Pear + + Version + Product + + + + + PHP PEAR 1.0 + + Change Log + + + + + PHP PEAR 1.0 Beta 1 + + Change Log + + + + + HTML_QuickForm project HTML_QuickForm 2.0 + + Project + Version + + + + + HTML_QuickForm project HTML_QuickForm 2.1 + + Project + Version + + + diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go b/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go index a8a5f8f7fdb..7977d9e6ae7 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go @@ -4,6 +4,8 @@ const ( EcosystemNPM = "npm" EcosystemRubyGems = "rubygems" EcosystemPyPI = "pypi" + EcosystemPHPPear = "php_pear" + EcosystemPHPPecl = "php_pecl" EcosystemJenkinsPlugins = "jenkins_plugins" EcosystemRustCrates = "rust_crates" )