syft/syft/pkg/cataloger/common/cpe/generate_test.go
Alex Goodman 1aaa644007
Remove MetadataType from core package object and normalize JSON metadataType values (#1983)
* [wip]

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* distinct the package metadata functions

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* remove metadata type from package core model

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* incorporate review feedback for names

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add RPM archive metadata and split parser helpers

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* clarify the python package metadata type

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* rename the KB metadata type

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* break hackage and composer types by use case

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* linting fix

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix encoding and decoding for syft-json and cyclonedx

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* bump json schema to 11

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update cyclonedx-json snapshots

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update cyclonedx-xml snapshots

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update spdx-json snapshots

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update spdx-tv snapshots

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update syft-json snapshots

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* correct metadata type in stack yaml parser test

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix bom-ref redactor for cyclonedx-xml

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add tests for legacy package metadata names

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* regenerate json schema v11

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix legacy HackageMetadataType reflect type value check

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* packagemetadata discovery should account for type shadowing

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix linting

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix cli tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* bump json schema version to v12

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* update json schema to incorporate changes from main

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add syft-json legacy config option

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add tests around v11-v12 json decoding

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add docs for SYFT_JSON_LEGACY

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* rename structs to be compliant with new naming scheme

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2023-10-30 12:12:04 -04:00

1005 lines
33 KiB
Go

package cpe
import (
"fmt"
"sort"
"strings"
"testing"
"github.com/scylladb/go-set"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
func TestGeneratePackageCPEs(t *testing.T) {
tests := []struct {
name string
p pkg.Package
expected []string
}{
{
name: "hyphen replacement",
p: pkg.Package{
Name: "name-part",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Python,
Type: pkg.DebPkg,
},
expected: []string{
"cpe:2.3:a:name-part:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name-part:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name-part:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name-part:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name_part:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name_part:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name_part:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name_part:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name-part:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name-part:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name-part:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name-part:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:python_name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name_part:name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name_part:name_part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name_part:python-name-part:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name_part:python_name_part:3.2:*:*:*:*:*:*:*",
},
},
{
name: "python language",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Python,
Type: pkg.DebPkg,
Metadata: pkg.PythonPackage{
Author: "alex goodman",
AuthorEmail: "william.goodman@anchore.com",
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python-name:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:python_name:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodman:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodman:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodman:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william-goodman:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william-goodman:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william-goodman:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodman:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodman:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodman:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodman_project:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodman_project:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodman_project:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodmanproject:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodmanproject:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:alex_goodmanproject:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodman_project:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodman_project:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodman_project:python_name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodmanproject:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodmanproject:python-name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:william_goodmanproject:python_name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "javascript language",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.JavaScript,
Metadata: pkg.NpmPackage{
Author: "jon",
URL: "https://github.com/bob/npm-name",
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:bob:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "ruby language",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Ruby,
Type: pkg.DebPkg,
Metadata: pkg.RubyGemspec{
Authors: []string{
"someones name",
"someones.elses.name@gmail.com",
},
Homepage: "https://github.com/tom/ruby-name",
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-lang:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_lang:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:someones-elses-name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:someones-name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:someones_elses_name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:someones_name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:tom:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "java language",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JavaPkg,
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "java language with groupID",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JavaPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.sonatype.nexus",
},
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:nexus:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:nexus:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:nexus:nexus:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:sonatype:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:sonatype:nexus:3.2:*:*:*:*:*:*:*",
},
},
{
name: "java with URL in metadata", // regression: https://github.com/anchore/grype/issues/417
p: pkg.Package{
Name: "wstx-asl",
Version: "3.2.7",
Type: pkg.JavaPkg,
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Ant-Version": "Apache Ant 1.6.5",
"Built-By": "tatu",
"Created-By": "1.4.2_03-b02 (Sun Microsystems Inc.)",
"Implementation-Title": "WoodSToX XML-processor",
"Implementation-Vendor": "woodstox.codehaus.org",
"Implementation-Version": "3.2.7",
"Manifest-Version": "1.0",
"Specification-Title": "StAX 1.0 API",
"Specification-Vendor": "http://jcp.org/en/jsr/detail?id=173",
"Specification-Version": "1.0",
},
},
},
},
expected: []string{
"cpe:2.3:a:woodstox_codehaus_org:wstx-asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:woodstox_codehaus_org:wstx_asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:woodstox-codehaus-org:wstx_asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:woodstox-codehaus-org:wstx-asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:wstx_asl:wstx-asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:wstx-asl:wstx-asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:wstx-asl:wstx_asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:wstx_asl:wstx_asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:wstx:wstx_asl:3.2.7:*:*:*:*:*:*:*",
"cpe:2.3:a:wstx:wstx-asl:3.2.7:*:*:*:*:*:*:*",
},
},
{
name: "jenkins package identified via pkg type",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "java language - multi tier manifest fields",
p: pkg.Package{
Name: "cxf-rt-bindings-xml",
Version: "3.3.10",
FoundBy: "java-cataloger",
Language: pkg.Java,
Type: pkg.JavaPkg,
Metadata: pkg.JavaArchive{
VirtualPath: "/opt/jboss/keycloak/modules/system/layers/base/org/apache/cxf/impl/main/cxf-rt-bindings-xml-3.3.10.jar",
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Automatic-Module-Name": "org.apache.cxf.binding.xml",
"Bnd-LastModified": "1615836524860",
"Build-Jdk": "1.8.0_261",
"Built-By": "dkulp",
"Bundle-ActivationPolicy": "lazy",
"Bundle-Description": "Apache CXF Runtime XML Binding",
"Bundle-DocURL": "http://cxf.apache.org",
"Bundle-License": "https://www.apache.org/licenses/LICENSE-2.0.txt",
"Bundle-ManifestVersion": "2",
"Bundle-Name": "Apache CXF Runtime XML Binding",
"Bundle-SymbolicName": "org.apache.cxf.cxf-rt-bindings-xml",
"Bundle-Vendor": "The Apache Software Foundation",
"Bundle-Version": "3.3.10",
"Created-By": "Apache Maven Bundle Plugin",
"Export-Package": "org.apache.cxf.binding.xml;version=\"3.3.10\",org.apache.cxf.binding.xml.wsdl11;version=\"3.3.10\",org.apache.cxf.binding.xml.interceptor;version=\"3.3.10\",org.apache.cxf.bindings.xformat;version=\"3.3.10\"",
"Implementation-Vendor": "The Apache Software Foundation",
"Implementation-Vendor-Id": "org.apache",
"Implementation-Version": "3.3.10",
"Import-Package": "javax.xml.bind;version=\"[0,3)\",javax.xml.bind.annotation;version=\"[0,3)\",javax.wsdl;resolution:=optional,javax.wsdl.extensions;resolution:=optional,javax.wsdl.extensions.http;resolution:=optional,javax.xml.namespace,javax.xml.stream,org.apache.cxf;version=\"[3.3,4)\",org.apache.cxf.binding;version=\"[3.3,4)\",org.apache.cxf.binding.xml,org.apache.cxf.binding.xml.interceptor,org.apache.cxf.bindings.xformat,org.apache.cxf.common.i18n;version=\"[3.3,4)\",org.apache.cxf.common.injection;version=\"[3.3,4)\",org.apache.cxf.common.logging;version=\"[3.3,4)\",org.apache.cxf.common.util;version=\"[3.3,4)\",org.apache.cxf.endpoint;version=\"[3.3,4)\",org.apache.cxf.helpers;version=\"[3.3,4)\",org.apache.cxf.interceptor;version=\"[3.3,4)\",org.apache.cxf.message;version=\"[3.3,4)\",org.apache.cxf.service.model;version=\"[3.3,4)\",org.apache.cxf.staxutils;version=\"[3.3,4)\",org.apache.cxf.tools.common;version=\"[3.3,4)\";resolution:=optional,org.apache.cxf.tools.validator;version=\"[3.3,4)\";resolution:=optional,org.apache.cxf.transport;version=\"[3.3,4)\",org.apache.cxf.wsdl;version=\"[3.3,4)\";resolution:=optional,org.apache.cxf.wsdl.http;version=\"[3.3,4)\",org.apache.cxf.wsdl.interceptors;version=\"[3.3,4)\";resolution:=optional,org.w3c.dom",
"Manifest-Version": "1.0",
"Require-Capability": "osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"",
"Specification-Vendor": "The Apache Software Foundation",
"Specification-Version": "3.3.10",
"Tool": "Bnd-4.2.0.201903051501",
},
},
PomProperties: &pkg.JavaPomProperties{
Path: "META-INF/maven/org.apache.cxf/cxf-rt-bindings-xml/pom.properties",
GroupID: "org.apache.cxf",
ArtifactID: "cxf-rt-bindings-xml",
Version: "3.3.10",
},
},
},
expected: []string{
"cpe:2.3:a:apache:cxf-rt-bindings-xml:3.3.10:*:*:*:*:*:*:*",
"cpe:2.3:a:apache:cxf:3.3.10:*:*:*:*:*:*:*",
"cpe:2.3:a:apache:cxf_rt_bindings_xml:3.3.10:*:*:*:*:*:*:*",
},
},
{
name: "rpm vendor selection",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Type: pkg.RpmPkg,
Metadata: pkg.RpmDBEntry{
Vendor: "some-vendor",
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:some-vendor:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:some_vendor:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "rpm with epoch",
p: pkg.Package{
Name: "name",
Version: "1:3.2",
FoundBy: "some-analyzer",
Type: pkg.RpmPkg,
Metadata: pkg.RpmDBEntry{
Vendor: "some-vendor",
},
},
expected: []string{
"cpe:2.3:a:name:name:1\\:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:some-vendor:name:1\\:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:some_vendor:name:1\\:3.2:*:*:*:*:*:*:*",
},
},
{
name: "deb with epoch",
p: pkg.Package{
Name: "name",
Version: "1:3.2",
FoundBy: "some-analyzer",
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{},
},
expected: []string{
"cpe:2.3:a:name:name:1\\:3.2:*:*:*:*:*:*:*",
},
},
{
name: "cloudbees jenkins package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "com.cloudbees.jenkins.plugins",
},
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins.io package identified via groupId prefix",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "io.jenkins.plugins.name.something",
},
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:name:something:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:something:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:something:something:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:something:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins.io package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "io.jenkins.plugins",
},
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins-ci.io package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "io.jenkins-ci.plugins",
},
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins-ci.org package identified via groupId",
p: pkg.Package{
Name: "name",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JenkinsPluginPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.jenkins-ci.plugins",
},
},
},
expected: []string{
"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jira-atlassian filtering",
p: pkg.Package{
Name: "jira_client_core",
Version: "3.2",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JavaPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.atlassian.jira",
ArtifactID: "jira_client_core",
},
},
},
expected: []string{
"cpe:2.3:a:atlassian:jira-client-core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:atlassian:jira_client_core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira-client-core:jira-client-core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira-client-core:jira:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira-client-core:jira_client_core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira-client:jira-client-core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira-client:jira:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira-client:jira_client_core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira:jira-client-core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira:jira_client_core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira_client:jira-client-core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira_client:jira:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira_client:jira_client_core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira_client_core:jira-client-core:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira_client_core:jira:3.2:*:*:*:*:*:*:*",
"cpe:2.3:a:jira_client_core:jira_client_core:3.2:*:*:*:*:*:*:*",
},
},
{
name: "jenkins filtering",
p: pkg.Package{
Name: "cloudbees-installation-manager",
Version: "2.89.0.33",
FoundBy: "some-analyzer",
Language: pkg.Java,
Type: pkg.JavaPkg,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "com.cloudbees.jenkins.modules",
ArtifactID: "cloudbees-installation-manager",
},
},
},
expected: []string{
"cpe:2.3:a:cloudbees-installation-manager:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees-installation-manager:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees-installation:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees-installation:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees_installation:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees_installation:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees_installation_manager:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:cloudbees_installation_manager:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:modules:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
"cpe:2.3:a:modules:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
},
},
{
name: "go product and vendor candidates are wired up",
p: pkg.Package{
Name: "github.com/someone/something",
Version: "3.2",
FoundBy: "go-cataloger",
Language: pkg.Go,
Type: pkg.GoModulePkg,
},
expected: []string{
"cpe:2.3:a:someone:something:3.2:*:*:*:*:*:*:*",
},
},
{
name: "go product with vendor candidates and an extra sub-item",
p: pkg.Package{
Name: "github.com/someone/something/more",
Version: "3.2",
FoundBy: "go-cataloger",
Language: pkg.Go,
Type: pkg.GoModulePkg,
},
expected: []string{
"cpe:2.3:a:someone:something\\/more:3.2:*:*:*:*:*:*:*",
},
},
{
name: "generate no CPEs for indeterminate golang package name",
p: pkg.Package{
Name: "github.com/what",
Version: "3.2",
FoundBy: "go-cataloger",
Language: pkg.Go,
Type: pkg.GoModulePkg,
},
expected: []string{},
},
{
name: "regression: handlebars within java archive",
p: pkg.Package{
Name: "handlebars",
Version: "3.0.8",
Type: pkg.JavaPkg,
Language: pkg.Java,
FoundBy: "java-cataloger",
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Extension-Name": "handlebars",
"Group-Id": "org.jenkins-ci.ui",
"Hudson-Version": "2.204",
"Implementation-Title": "handlebars",
"Implementation-Version": "3.0.8",
"Plugin-Version": "3.0.8",
"Short-Name": "handlebars",
},
},
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.jenkins-ci.ui",
ArtifactID: "handlebars",
Version: "3.0.8",
},
},
},
expected: []string{
"cpe:2.3:a:handlebars:handlebars:3.0.8:*:*:*:*:*:*:*",
"cpe:2.3:a:handlebarsjs:handlebars:3.0.8:*:*:*:*:*:*:*", // important!
"cpe:2.3:a:jenkins-ci:handlebars:3.0.8:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:handlebars:3.0.8:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins_ci:handlebars:3.0.8:*:*:*:*:*:*:*",
"cpe:2.3:a:ui:handlebars:3.0.8:*:*:*:*:*:*:*",
},
},
{
name: "regression: jenkins plugin active-directory",
p: pkg.Package{
Name: "active-directory",
Version: "2.25.1",
Type: pkg.JenkinsPluginPkg,
FoundBy: "java-cataloger",
Language: pkg.Java,
Metadata: pkg.JavaArchive{
Manifest: &pkg.JavaManifest{
Main: map[string]string{
"Extension-Name": "active-directory",
"Group-Id": "org.jenkins-ci.plugins",
},
},
PomProperties: &pkg.JavaPomProperties{
GroupID: "org.jenkins-ci.plugins",
ArtifactID: "org.jenkins-ci.plugins",
Version: "2.25.1",
},
},
},
expected: []string{
"cpe:2.3:a:active-directory:active-directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:active-directory:active_directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:active:active-directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:active:active_directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:active_directory:active-directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:active_directory:active_directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins-ci:active-directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins-ci:active_directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins:active-directory:2.25.1:*:*:*:*:*:*:*", // important!
"cpe:2.3:a:jenkins:active_directory:2.25.1:*:*:*:*:*:*:*", // important!
"cpe:2.3:a:jenkins_ci:active-directory:2.25.1:*:*:*:*:*:*:*",
"cpe:2.3:a:jenkins_ci:active_directory:2.25.1:*:*:*:*:*:*:*",
},
},
{
name: "regression: special characters in CPE should result in no generation",
p: pkg.Package{
Name: "bundler",
Version: "2.1.4",
Type: pkg.GemPkg,
FoundBy: "gem-cataloger",
Language: pkg.Ruby,
Metadata: pkg.RubyGemspec{
Name: "bundler",
Version: "2.1.4",
Authors: []string{
"jessica lynn suttles",
"stephanie morillo",
"david rodríguez",
"andré medeiros",
},
},
},
expected: []string{
"cpe:2.3:a:bundler:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-lang:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_lang:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:jessica-lynn-suttles:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:jessica_lynn_suttles:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:stephanie-morillo:bundler:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:stephanie_morillo:bundler:2.1.4:*:*:*:*:*:*:*",
},
},
{
name: "regression: python redis shadows normal redis",
p: pkg.Package{
Name: "redis",
Version: "2.1.4",
Type: pkg.PythonPkg,
FoundBy: "some-analyzer",
Language: pkg.Python,
},
expected: []string{
"cpe:2.3:a:python-redis:python-redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python-redis:python_redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python-redis:redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python-redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python:python_redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python:redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python_redis:python-redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python_redis:python_redis:2.1.4:*:*:*:*:*:*:*",
"cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*",
},
},
{
name: "regression: ruby-rake apk missing expected ruby-lang:rake CPE",
p: pkg.Package{
Name: "ruby-rake",
Version: "2.7.6-r0",
Type: pkg.ApkPkg,
FoundBy: "apk-db-analyzer",
Language: pkg.UnknownLanguage,
Metadata: pkg.ApkDBEntry{
Package: "ruby-rake",
URL: "https://www.ruby-lang.org/",
OriginPackage: "ruby",
},
},
expected: []string{
"cpe:2.3:a:ruby-lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby-rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby:rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
"cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := Generate(test.p)
expectedCpeSet := set.NewStringSet(test.expected...)
actualCpeSet := set.NewStringSet()
for _, a := range actual {
actualCpeSet.Add(cpe.String(a))
}
extra := strset.Difference(actualCpeSet, expectedCpeSet).List()
sort.Strings(extra)
if len(extra) > 0 {
t.Errorf("found extra CPEs:")
for _, d := range extra {
t.Logf(" %q,\n", d)
}
}
missing := strset.Difference(expectedCpeSet, actualCpeSet).List()
sort.Strings(missing)
if len(missing) > 0 {
t.Errorf("missing CPEs:")
for _, d := range missing {
t.Logf(" %q,\n", d)
}
}
})
}
}
func TestCandidateProducts(t *testing.T) {
tests := []struct {
name string
p pkg.Package
expected []string
}{
{
name: "apache-cassandra",
p: pkg.Package{
Name: "apache-cassandra",
Type: pkg.JavaPkg,
},
expected: []string{"cassandra" /* <-- known good names | default guess --> */, "apache-cassandra", "apache_cassandra"},
},
{
name: "springframework",
p: pkg.Package{
Name: "springframework",
Type: pkg.JavaPkg,
},
expected: []string{"spring_framework", "springsource_spring_framework" /* <-- known good names | default guess --> */, "springframework"},
},
{
name: "spring-security-core",
p: pkg.Package{
Name: "spring-security-core",
Type: pkg.JavaPkg,
},
expected: []string{"spring-security-core", "spring_security", "spring_security_core"},
},
{
name: "java",
p: pkg.Package{
Name: "some-java-package-with-group-id",
Type: pkg.JavaPkg,
Language: pkg.Java,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "com.apple.itunes",
},
},
},
expected: []string{"itunes", "some-java-package-with-group-id", "some_java_package_with_group_id"},
},
{
name: "java-with-asterisk",
p: pkg.Package{
Name: "some-java-package-with-group-id",
Type: pkg.JavaPkg,
Language: pkg.Java,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "com.apple.itunes.*",
},
},
},
expected: []string{"itunes", "some-java-package-with-group-id", "some_java_package_with_group_id"},
},
{
name: "jenkins-plugin",
p: pkg.Package{
Name: "some-jenkins-plugin",
Type: pkg.JenkinsPluginPkg,
Language: pkg.Java,
Metadata: pkg.JavaArchive{
PomProperties: &pkg.JavaPomProperties{
GroupID: "com.cloudbees.jenkins.plugins",
},
},
},
expected: []string{"some-jenkins-plugin", "some_jenkins_plugin", "jenkins"},
},
{
name: "javascript",
p: pkg.Package{
Name: "handlebars.js",
Type: pkg.NpmPkg,
},
expected: []string{"handlebars" /* <-- known good names | default guess --> */, "handlebars.js"},
},
{
name: "gem",
p: pkg.Package{
Name: "RedCloth",
Type: pkg.GemPkg,
},
expected: []string{"redcloth_library" /* <-- known good names | default guess --> */, "RedCloth"},
},
{
name: "python",
p: pkg.Package{
Name: "python-rrdtool",
Type: pkg.PythonPkg,
},
expected: []string{"rrdtool" /* <-- known good names | default guess --> */, "python-rrdtool", "python_rrdtool"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.ElementsMatch(t, test.expected, candidateProducts(test.p))
})
}
}
func TestCandidateVendor(t *testing.T) {
tests := []struct {
name string
p pkg.Package
expected []string
}{
{
name: "elasticsearch",
p: pkg.Package{
Name: "elasticsearch",
Type: pkg.JavaPkg,
},
expected: []string{"elastic" /* <-- known good names | default guess --> */, "elasticsearch"},
},
{
name: "spring-security",
p: pkg.Package{
Name: "spring-security-core",
Type: pkg.JavaPkg,
},
expected: []string{"vmware" /* <-- known good names | default guess --> */, "spring", "spring-security", "spring-security-core", "spring_security_core", "spring_security"},
},
{
name: "log4j",
p: pkg.Package{
Name: "log4j",
Type: pkg.JavaPkg,
},
expected: []string{"apache"},
},
{
name: "Django",
p: pkg.Package{
Name: "Django",
Type: pkg.PythonPkg,
},
expected: []string{"djangoproject" /* <-- known good names | default guess --> */, "Django"},
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%+v %+v", test.p, test.expected), func(t *testing.T) {
assert.ElementsMatch(t, test.expected, candidateVendors(test.p))
})
}
}
func Test_generateSubSelections(t *testing.T) {
tests := []struct {
field string
expected []string
}{
{
field: "jenkins",
expected: []string{"jenkins"},
},
{
field: "jenkins-ci",
expected: []string{"jenkins", "jenkins-ci"},
},
{
field: "jenkins--ci",
expected: []string{"jenkins", "jenkins-ci"},
},
{
field: "jenkins_ci_tools",
expected: []string{"jenkins", "jenkins_ci", "jenkins_ci_tools"},
},
{
field: "-jenkins",
expected: []string{"jenkins"},
},
{
field: "jenkins_",
expected: []string{"jenkins"},
},
{
field: "",
expected: nil,
},
{
field: "-",
expected: nil,
},
{
field: "_",
expected: nil,
},
}
for _, test := range tests {
t.Run(test.field, func(t *testing.T) {
assert.ElementsMatch(t, test.expected, generateSubSelections(test.field))
})
}
}
func Test_addSeparatorVariations(t *testing.T) {
tests := []struct {
input []string
expected []string
}{
{
input: []string{"jenkins-ci"},
expected: []string{"jenkins-ci", "jenkins_ci"}, //, "jenkinsci"},
},
{
input: []string{"jenkins_ci"},
expected: []string{"jenkins_ci", "jenkins-ci"}, //, "jenkinsci"},
},
{
input: []string{"jenkins"},
expected: []string{"jenkins"},
},
{
input: []string{"jenkins-ci", "circle-ci"},
expected: []string{"jenkins-ci", "jenkins_ci", "circle-ci", "circle_ci"}, //, "jenkinsci", "circleci"},
},
}
for _, test := range tests {
t.Run(strings.Join(test.input, ","), func(t *testing.T) {
val := newFieldCandidateSet(test.input...)
addDelimiterVariations(val)
assert.ElementsMatch(t, test.expected, val.values())
})
}
}
func TestDictionaryFindIsWired(t *testing.T) {
tests := []struct {
name string
pkg pkg.Package
want string
wantExists bool
}{
{
name: "sanity check that cpe data is wired up",
pkg: pkg.Package{
Name: "openssl",
Version: "1.0.2k",
Type: pkg.GemPkg,
},
want: "cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*",
// without the cpe data wired up, this would be empty (generation also creates cpe:2.3:a:openssl:openssl:1.0.2k:*:*:*:*:*:*:*)
wantExists: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, gotExists := DictionaryFind(tt.pkg)
assert.Equal(t, tt.want, got.BindToFmtString())
assert.Equal(t, tt.wantExists, gotExists)
})
}
}