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 adddda977..4014f7502 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/data/cpe-index.json @@ -1514,6 +1514,18 @@ "zjjserver": "cpe:2.3:a:zjjserver_project:zjjserver:*:*:*:*:*:node.js:*:*", "zwserver": "cpe:2.3:a:zwserver_project:zwserver:*:*:*:*:*:node.js:*:*" }, + "php_composer": { + "alfnru/password_recovery": "cpe:2.3:a:password_recovery_project:password_recovery:*:*:*:*:*:roundcube:*:*", + "fineuploader/php-traditional-server": "cpe:2.3:a:php-traditional-server_project:php-traditional-server:*:*:*:*:*:*:*:*", + "frappant/frp-form-answers": "cpe:2.3:a:frappant:forms_export:*:*:*:*:*:typo3:*:*", + "joomla/session": "cpe:2.3:a:joomla:session:*:*:*:*:*:*:*:*", + "mustache/mustache": "cpe:2.3:a:mustache_project:mustache:*:*:*:*:*:php:*:*", + "phpfastcache/phpfastcache": "cpe:2.3:a:phpfastcache:phpfastcache:*:*:*:*:*:*:*:*", + "shopware/shopware": "cpe:2.3:a:shopware:shopware:*:*:*:*:*:*:*:*", + "typo3/html-sanitizer": "cpe:2.3:a:typo3:html_sanitizer:*:*:*:*:*:*:*:*", + "typo3/phar-stream-wrapper": "cpe:2.3:a:typo3:pharstreamwrapper:*:*:*:*:*:*:*:*", + "yab/quarx": "cpe:2.3:a:quarx_cms_project:quarx_cms:*:*:*:*:*:*:*:*" + }, "php_pear": { "Archive_Tar": "cpe:2.3:a:php:pear_archive_tar:*:*:*:*:*:*:*:*", "HTML_AJAX": "cpe:2.3:a:pear:html_ajax:*:*:*:*:*:*:*:*", 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 1597f7f9e..ccd5f0ed6 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,7 @@ const ( prefixForPHPPearHTTP = "http://pear.php.net/" prefixForPHPPecl = "https://pecl.php.net/" prefixForPHPPeclHTTP = "http://pecl.php.net/" + prefixForPHPComposer = "https://packagist.org/packages/" ) // indexCPEList creates an index of CPEs by ecosystem. @@ -147,6 +148,9 @@ func indexCPEList(list CpeList) *dictionary.Indexed { case strings.HasPrefix(ref, prefixForPHPPecl), strings.HasPrefix(ref, prefixForPHPPeclHTTP): addEntryForPHPPeclPackage(indexed, ref, cpeItemName) + + case strings.HasPrefix(ref, prefixForPHPComposer): + addEntryForPHPComposerPackage(indexed, ref, cpeItemName) } } } @@ -294,3 +298,21 @@ func addEntryForPHPPeclPackage(indexed *dictionary.Indexed, ref string, cpeItemN indexed.EcosystemPackages[dictionary.EcosystemPHPPecl][ref] = cpeItemName } + +func addEntryForPHPComposerPackage(indexed *dictionary.Indexed, ref string, cpeItemName string) { + // Prune off the non-package-name parts of the URL + ref = strings.TrimPrefix(ref, prefixForPHPComposer) + components := strings.Split(ref, "/") + + if len(components) < 2 { + return + } + + ref = components[0] + "/" + components[1] + + if _, ok := indexed.EcosystemPackages[dictionary.EcosystemPHPComposer]; !ok { + indexed.EcosystemPackages[dictionary.EcosystemPHPComposer] = make(dictionary.Packages) + } + + indexed.EcosystemPackages[dictionary.EcosystemPHPComposer][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 267ffe011..537553791 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 @@ -203,6 +203,19 @@ func Test_addEntryFuncs(t *testing.T) { }, }, }, + { + name: "addEntryForPHPComposerPackage", + addEntryFunc: addEntryForPHPComposerPackage, + inputRef: "https://packagist.org/packages/frappant/frp-form-answers", + inputCpeItemName: "cpe:2.3:a:frappant:forms_export:*:*:*:*:*:*:*:*", + expectedIndexed: dictionary.Indexed{ + EcosystemPackages: map[string]dictionary.Packages{ + dictionary.EcosystemPHPComposer: { + "frappant/frp-form-answers": "cpe:2.3:a:frappant:forms_export:*:*:*:*:*:*:*:*", + }, + }, + }, + }, } 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 95e1c430e..f74e99066 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 @@ -16,6 +16,9 @@ "unicode-json": "cpe:2.3:a:unicode:unicode-json:*:*:*:*:*:node.js:*:*", "unicorn-list": "cpe:2.3:a:unicorn-list_project:unicorn-list:*:*:*:*:*:node.js:*:*" }, + "php_composer": { + "frappant/frp-form-answers": "cpe:2.3:a:frappant:forms_export:*:*:*:*:*:typo3:*:*" + }, "php_pear": { "HTML_QuickForm": "cpe:2.3:a:html_quickform_project:html_quickform:*:*:*:*:*:*:*:*", "PEAR": "cpe:2.3:a:php:pear:*:*:*:*:*:*:*:*", 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 1bcd5d466..702bb8c66 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 @@ -24985,4 +24985,13 @@ + + !Frappant Forms Export for TYPO3 + + Version + Vendor + Product + + + diff --git a/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go b/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go index 7977d9e6a..f6291ddfb 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go +++ b/syft/pkg/cataloger/internal/cpegenerate/dictionary/types.go @@ -6,6 +6,7 @@ const ( EcosystemPyPI = "pypi" EcosystemPHPPear = "php_pear" EcosystemPHPPecl = "php_pecl" + EcosystemPHPComposer = "php_composer" EcosystemJenkinsPlugins = "jenkins_plugins" EcosystemRustCrates = "rust_crates" ) diff --git a/syft/pkg/cataloger/internal/cpegenerate/generate.go b/syft/pkg/cataloger/internal/cpegenerate/generate.go index 45161e9d4..24dcd3d39 100644 --- a/syft/pkg/cataloger/internal/cpegenerate/generate.go +++ b/syft/pkg/cataloger/internal/cpegenerate/generate.go @@ -86,6 +86,9 @@ func FromDictionaryFind(p pkg.Package) (cpe.CPE, bool) { case pkg.RustPkg: cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemRustCrates][p.Name] + case pkg.PhpComposerPkg: + cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemPHPComposer][p.Name] + case pkg.PhpPeclPkg: cpeString, ok = dict.EcosystemPackages[dictionary.EcosystemPHPPecl][p.Name]