github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/common/cpe/generate_test.go (about)

     1  package cpe
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/scylladb/go-set"
    10  	"github.com/scylladb/go-set/strset"
    11  	"github.com/stretchr/testify/assert"
    12  
    13  	"github.com/anchore/syft/syft/cpe"
    14  	"github.com/anchore/syft/syft/pkg"
    15  )
    16  
    17  func TestGeneratePackageCPEs(t *testing.T) {
    18  	tests := []struct {
    19  		name     string
    20  		p        pkg.Package
    21  		expected []string
    22  	}{
    23  		{
    24  			name: "hyphen replacement",
    25  			p: pkg.Package{
    26  				Name:     "name-part",
    27  				Version:  "3.2",
    28  				FoundBy:  "some-analyzer",
    29  				Language: pkg.Python,
    30  				Type:     pkg.DebPkg,
    31  			},
    32  			expected: []string{
    33  				"cpe:2.3:a:name-part:name-part:3.2:*:*:*:*:*:*:*",
    34  				"cpe:2.3:a:name-part:name_part:3.2:*:*:*:*:*:*:*",
    35  				"cpe:2.3:a:name-part:python-name-part:3.2:*:*:*:*:*:*:*",
    36  				"cpe:2.3:a:name-part:python_name_part:3.2:*:*:*:*:*:*:*",
    37  				"cpe:2.3:a:name:name-part:3.2:*:*:*:*:*:*:*",
    38  				"cpe:2.3:a:name:name_part:3.2:*:*:*:*:*:*:*",
    39  				"cpe:2.3:a:name:python-name-part:3.2:*:*:*:*:*:*:*",
    40  				"cpe:2.3:a:name:python_name_part:3.2:*:*:*:*:*:*:*",
    41  				"cpe:2.3:a:name_part:name-part:3.2:*:*:*:*:*:*:*",
    42  				"cpe:2.3:a:name_part:name_part:3.2:*:*:*:*:*:*:*",
    43  				"cpe:2.3:a:name_part:python-name-part:3.2:*:*:*:*:*:*:*",
    44  				"cpe:2.3:a:name_part:python_name_part:3.2:*:*:*:*:*:*:*",
    45  				"cpe:2.3:a:python-name-part:name-part:3.2:*:*:*:*:*:*:*",
    46  				"cpe:2.3:a:python-name-part:name_part:3.2:*:*:*:*:*:*:*",
    47  				"cpe:2.3:a:python-name-part:python-name-part:3.2:*:*:*:*:*:*:*",
    48  				"cpe:2.3:a:python-name-part:python_name_part:3.2:*:*:*:*:*:*:*",
    49  				"cpe:2.3:a:python-name:name-part:3.2:*:*:*:*:*:*:*",
    50  				"cpe:2.3:a:python-name:name_part:3.2:*:*:*:*:*:*:*",
    51  				"cpe:2.3:a:python-name:python-name-part:3.2:*:*:*:*:*:*:*",
    52  				"cpe:2.3:a:python-name:python_name_part:3.2:*:*:*:*:*:*:*",
    53  				"cpe:2.3:a:python:name-part:3.2:*:*:*:*:*:*:*",
    54  				"cpe:2.3:a:python:name_part:3.2:*:*:*:*:*:*:*",
    55  				"cpe:2.3:a:python:python-name-part:3.2:*:*:*:*:*:*:*",
    56  				"cpe:2.3:a:python:python_name_part:3.2:*:*:*:*:*:*:*",
    57  				"cpe:2.3:a:python_name:name-part:3.2:*:*:*:*:*:*:*",
    58  				"cpe:2.3:a:python_name:name_part:3.2:*:*:*:*:*:*:*",
    59  				"cpe:2.3:a:python_name:python-name-part:3.2:*:*:*:*:*:*:*",
    60  				"cpe:2.3:a:python_name:python_name_part:3.2:*:*:*:*:*:*:*",
    61  				"cpe:2.3:a:python_name_part:name-part:3.2:*:*:*:*:*:*:*",
    62  				"cpe:2.3:a:python_name_part:name_part:3.2:*:*:*:*:*:*:*",
    63  				"cpe:2.3:a:python_name_part:python-name-part:3.2:*:*:*:*:*:*:*",
    64  				"cpe:2.3:a:python_name_part:python_name_part:3.2:*:*:*:*:*:*:*",
    65  			},
    66  		},
    67  		{
    68  			name: "python language",
    69  			p: pkg.Package{
    70  				Name:         "name",
    71  				Version:      "3.2",
    72  				FoundBy:      "some-analyzer",
    73  				Language:     pkg.Python,
    74  				Type:         pkg.DebPkg,
    75  				MetadataType: pkg.PythonPackageMetadataType,
    76  				Metadata: pkg.PythonPackageMetadata{
    77  					Author:      "alex goodman",
    78  					AuthorEmail: "william.goodman@anchore.com",
    79  				},
    80  			},
    81  			expected: []string{
    82  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
    83  				"cpe:2.3:a:name:python-name:3.2:*:*:*:*:*:*:*",
    84  				"cpe:2.3:a:name:python_name:3.2:*:*:*:*:*:*:*",
    85  				"cpe:2.3:a:python-name:name:3.2:*:*:*:*:*:*:*",
    86  				"cpe:2.3:a:python-name:python-name:3.2:*:*:*:*:*:*:*",
    87  				"cpe:2.3:a:python-name:python_name:3.2:*:*:*:*:*:*:*",
    88  				"cpe:2.3:a:python:name:3.2:*:*:*:*:*:*:*",
    89  				"cpe:2.3:a:python:python-name:3.2:*:*:*:*:*:*:*",
    90  				"cpe:2.3:a:python:python_name:3.2:*:*:*:*:*:*:*",
    91  				"cpe:2.3:a:python_name:name:3.2:*:*:*:*:*:*:*",
    92  				"cpe:2.3:a:python_name:python-name:3.2:*:*:*:*:*:*:*",
    93  				"cpe:2.3:a:python_name:python_name:3.2:*:*:*:*:*:*:*",
    94  				"cpe:2.3:a:alex_goodman:name:3.2:*:*:*:*:*:*:*",
    95  				"cpe:2.3:a:alex_goodman:python-name:3.2:*:*:*:*:*:*:*",
    96  				"cpe:2.3:a:alex_goodman:python_name:3.2:*:*:*:*:*:*:*",
    97  				"cpe:2.3:a:william-goodman:name:3.2:*:*:*:*:*:*:*",
    98  				"cpe:2.3:a:william-goodman:python-name:3.2:*:*:*:*:*:*:*",
    99  				"cpe:2.3:a:william-goodman:python_name:3.2:*:*:*:*:*:*:*",
   100  				"cpe:2.3:a:william_goodman:name:3.2:*:*:*:*:*:*:*",
   101  				"cpe:2.3:a:william_goodman:python-name:3.2:*:*:*:*:*:*:*",
   102  				"cpe:2.3:a:william_goodman:python_name:3.2:*:*:*:*:*:*:*",
   103  				"cpe:2.3:a:alex_goodman_project:python_name:3.2:*:*:*:*:*:*:*",
   104  				"cpe:2.3:a:alex_goodman_project:name:3.2:*:*:*:*:*:*:*",
   105  				"cpe:2.3:a:alex_goodman_project:python-name:3.2:*:*:*:*:*:*:*",
   106  				"cpe:2.3:a:alex_goodmanproject:name:3.2:*:*:*:*:*:*:*",
   107  				"cpe:2.3:a:alex_goodmanproject:python-name:3.2:*:*:*:*:*:*:*",
   108  				"cpe:2.3:a:alex_goodmanproject:python_name:3.2:*:*:*:*:*:*:*",
   109  				"cpe:2.3:a:william_goodman_project:name:3.2:*:*:*:*:*:*:*",
   110  				"cpe:2.3:a:william_goodman_project:python-name:3.2:*:*:*:*:*:*:*",
   111  				"cpe:2.3:a:william_goodman_project:python_name:3.2:*:*:*:*:*:*:*",
   112  				"cpe:2.3:a:william_goodmanproject:name:3.2:*:*:*:*:*:*:*",
   113  				"cpe:2.3:a:william_goodmanproject:python-name:3.2:*:*:*:*:*:*:*",
   114  				"cpe:2.3:a:william_goodmanproject:python_name:3.2:*:*:*:*:*:*:*",
   115  			},
   116  		},
   117  		{
   118  			name: "javascript language",
   119  			p: pkg.Package{
   120  				Name:         "name",
   121  				Version:      "3.2",
   122  				FoundBy:      "some-analyzer",
   123  				Language:     pkg.JavaScript,
   124  				MetadataType: pkg.NpmPackageJSONMetadataType,
   125  				Metadata: pkg.NpmPackageJSONMetadata{
   126  					Author: "jon",
   127  					URL:    "https://github.com/bob/npm-name",
   128  				},
   129  			},
   130  			expected: []string{
   131  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   132  				"cpe:2.3:a:bob:name:3.2:*:*:*:*:*:*:*",
   133  			},
   134  		},
   135  		{
   136  			name: "ruby language",
   137  			p: pkg.Package{
   138  				Name:         "name",
   139  				Version:      "3.2",
   140  				FoundBy:      "some-analyzer",
   141  				Language:     pkg.Ruby,
   142  				Type:         pkg.DebPkg,
   143  				MetadataType: pkg.GemMetadataType,
   144  				Metadata: pkg.GemMetadata{
   145  					Authors: []string{
   146  						"someones name",
   147  						"someones.elses.name@gmail.com",
   148  					},
   149  					Homepage: "https://github.com/tom/ruby-name",
   150  				},
   151  			},
   152  			expected: []string{
   153  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   154  				"cpe:2.3:a:ruby-lang:name:3.2:*:*:*:*:*:*:*",
   155  				"cpe:2.3:a:ruby:name:3.2:*:*:*:*:*:*:*",
   156  				"cpe:2.3:a:ruby_lang:name:3.2:*:*:*:*:*:*:*",
   157  				"cpe:2.3:a:someones-elses-name:name:3.2:*:*:*:*:*:*:*",
   158  				"cpe:2.3:a:someones-name:name:3.2:*:*:*:*:*:*:*",
   159  				"cpe:2.3:a:someones_elses_name:name:3.2:*:*:*:*:*:*:*",
   160  				"cpe:2.3:a:someones_name:name:3.2:*:*:*:*:*:*:*",
   161  				"cpe:2.3:a:tom:name:3.2:*:*:*:*:*:*:*",
   162  			},
   163  		},
   164  		{
   165  			name: "java language",
   166  			p: pkg.Package{
   167  				Name:     "name",
   168  				Version:  "3.2",
   169  				FoundBy:  "some-analyzer",
   170  				Language: pkg.Java,
   171  				Type:     pkg.JavaPkg,
   172  			},
   173  			expected: []string{
   174  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   175  			},
   176  		},
   177  		{
   178  			name: "java language with groupID",
   179  			p: pkg.Package{
   180  				Name:         "name",
   181  				Version:      "3.2",
   182  				FoundBy:      "some-analyzer",
   183  				Language:     pkg.Java,
   184  				Type:         pkg.JavaPkg,
   185  				MetadataType: pkg.JavaMetadataType,
   186  				Metadata: pkg.JavaMetadata{
   187  					PomProperties: &pkg.PomProperties{
   188  						GroupID: "org.sonatype.nexus",
   189  					},
   190  				},
   191  			},
   192  			expected: []string{
   193  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   194  				"cpe:2.3:a:name:nexus:3.2:*:*:*:*:*:*:*",
   195  				"cpe:2.3:a:nexus:name:3.2:*:*:*:*:*:*:*",
   196  				"cpe:2.3:a:nexus:nexus:3.2:*:*:*:*:*:*:*",
   197  				"cpe:2.3:a:sonatype:name:3.2:*:*:*:*:*:*:*",
   198  				"cpe:2.3:a:sonatype:nexus:3.2:*:*:*:*:*:*:*",
   199  			},
   200  		},
   201  		{
   202  			name: "java with URL in metadata", // regression: https://github.com/anchore/grype/issues/417
   203  			p: pkg.Package{
   204  				Name:         "wstx-asl",
   205  				Version:      "3.2.7",
   206  				Type:         pkg.JavaPkg,
   207  				MetadataType: pkg.JavaMetadataType,
   208  				Metadata: pkg.JavaMetadata{
   209  					Manifest: &pkg.JavaManifest{
   210  						Main: map[string]string{
   211  							"Ant-Version":            "Apache Ant 1.6.5",
   212  							"Built-By":               "tatu",
   213  							"Created-By":             "1.4.2_03-b02 (Sun Microsystems Inc.)",
   214  							"Implementation-Title":   "WoodSToX XML-processor",
   215  							"Implementation-Vendor":  "woodstox.codehaus.org",
   216  							"Implementation-Version": "3.2.7",
   217  							"Manifest-Version":       "1.0",
   218  							"Specification-Title":    "StAX 1.0 API",
   219  							"Specification-Vendor":   "http://jcp.org/en/jsr/detail?id=173",
   220  							"Specification-Version":  "1.0",
   221  						},
   222  					},
   223  				},
   224  			},
   225  			expected: []string{
   226  				"cpe:2.3:a:woodstox_codehaus_org:wstx-asl:3.2.7:*:*:*:*:*:*:*",
   227  				"cpe:2.3:a:woodstox_codehaus_org:wstx_asl:3.2.7:*:*:*:*:*:*:*",
   228  				"cpe:2.3:a:woodstox-codehaus-org:wstx_asl:3.2.7:*:*:*:*:*:*:*",
   229  				"cpe:2.3:a:woodstox-codehaus-org:wstx-asl:3.2.7:*:*:*:*:*:*:*",
   230  				"cpe:2.3:a:wstx_asl:wstx-asl:3.2.7:*:*:*:*:*:*:*",
   231  				"cpe:2.3:a:wstx-asl:wstx-asl:3.2.7:*:*:*:*:*:*:*",
   232  				"cpe:2.3:a:wstx-asl:wstx_asl:3.2.7:*:*:*:*:*:*:*",
   233  				"cpe:2.3:a:wstx_asl:wstx_asl:3.2.7:*:*:*:*:*:*:*",
   234  				"cpe:2.3:a:wstx:wstx_asl:3.2.7:*:*:*:*:*:*:*",
   235  				"cpe:2.3:a:wstx:wstx-asl:3.2.7:*:*:*:*:*:*:*",
   236  			},
   237  		},
   238  		{
   239  			name: "jenkins package identified via pkg type",
   240  			p: pkg.Package{
   241  				Name:     "name",
   242  				Version:  "3.2",
   243  				FoundBy:  "some-analyzer",
   244  				Language: pkg.Java,
   245  				Type:     pkg.JenkinsPluginPkg,
   246  			},
   247  			expected: []string{
   248  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   249  			},
   250  		},
   251  		{
   252  			name: "java language - multi tier manifest fields",
   253  			p: pkg.Package{
   254  				Name:         "cxf-rt-bindings-xml",
   255  				Version:      "3.3.10",
   256  				FoundBy:      "java-cataloger",
   257  				Language:     pkg.Java,
   258  				Type:         pkg.JavaPkg,
   259  				MetadataType: pkg.JavaMetadataType,
   260  				Metadata: pkg.JavaMetadata{
   261  					VirtualPath: "/opt/jboss/keycloak/modules/system/layers/base/org/apache/cxf/impl/main/cxf-rt-bindings-xml-3.3.10.jar",
   262  					Manifest: &pkg.JavaManifest{
   263  						Main: map[string]string{
   264  							"Automatic-Module-Name":    "org.apache.cxf.binding.xml",
   265  							"Bnd-LastModified":         "1615836524860",
   266  							"Build-Jdk":                "1.8.0_261",
   267  							"Built-By":                 "dkulp",
   268  							"Bundle-ActivationPolicy":  "lazy",
   269  							"Bundle-Description":       "Apache CXF Runtime XML Binding",
   270  							"Bundle-DocURL":            "http://cxf.apache.org",
   271  							"Bundle-License":           "https://www.apache.org/licenses/LICENSE-2.0.txt",
   272  							"Bundle-ManifestVersion":   "2",
   273  							"Bundle-Name":              "Apache CXF Runtime XML Binding",
   274  							"Bundle-SymbolicName":      "org.apache.cxf.cxf-rt-bindings-xml",
   275  							"Bundle-Vendor":            "The Apache Software Foundation",
   276  							"Bundle-Version":           "3.3.10",
   277  							"Created-By":               "Apache Maven Bundle Plugin",
   278  							"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\"",
   279  							"Implementation-Vendor":    "The Apache Software Foundation",
   280  							"Implementation-Vendor-Id": "org.apache",
   281  							"Implementation-Version":   "3.3.10",
   282  							"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",
   283  							"Manifest-Version":         "1.0",
   284  							"Require-Capability":       "osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"",
   285  							"Specification-Vendor":     "The Apache Software Foundation",
   286  							"Specification-Version":    "3.3.10",
   287  							"Tool":                     "Bnd-4.2.0.201903051501",
   288  						},
   289  					},
   290  					PomProperties: &pkg.PomProperties{
   291  						Path:       "META-INF/maven/org.apache.cxf/cxf-rt-bindings-xml/pom.properties",
   292  						GroupID:    "org.apache.cxf",
   293  						ArtifactID: "cxf-rt-bindings-xml",
   294  						Version:    "3.3.10",
   295  					},
   296  				},
   297  			},
   298  			expected: []string{
   299  				"cpe:2.3:a:apache:cxf-rt-bindings-xml:3.3.10:*:*:*:*:*:*:*",
   300  				"cpe:2.3:a:apache:cxf:3.3.10:*:*:*:*:*:*:*",
   301  				"cpe:2.3:a:apache:cxf_rt_bindings_xml:3.3.10:*:*:*:*:*:*:*",
   302  			},
   303  		},
   304  		{
   305  			name: "rpm vendor selection",
   306  			p: pkg.Package{
   307  				Name:         "name",
   308  				Version:      "3.2",
   309  				FoundBy:      "some-analyzer",
   310  				Type:         pkg.RpmPkg,
   311  				MetadataType: pkg.RpmMetadataType,
   312  				Metadata: pkg.RpmMetadata{
   313  					Vendor: "some-vendor",
   314  				},
   315  			},
   316  			expected: []string{
   317  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   318  				"cpe:2.3:a:some-vendor:name:3.2:*:*:*:*:*:*:*",
   319  				"cpe:2.3:a:some_vendor:name:3.2:*:*:*:*:*:*:*",
   320  			},
   321  		},
   322  		{
   323  			name: "rpm with epoch",
   324  			p: pkg.Package{
   325  				Name:         "name",
   326  				Version:      "1:3.2",
   327  				FoundBy:      "some-analyzer",
   328  				Type:         pkg.RpmPkg,
   329  				MetadataType: pkg.RpmMetadataType,
   330  				Metadata: pkg.RpmMetadata{
   331  					Vendor: "some-vendor",
   332  				},
   333  			},
   334  			expected: []string{
   335  				"cpe:2.3:a:name:name:1\\:3.2:*:*:*:*:*:*:*",
   336  				"cpe:2.3:a:some-vendor:name:1\\:3.2:*:*:*:*:*:*:*",
   337  				"cpe:2.3:a:some_vendor:name:1\\:3.2:*:*:*:*:*:*:*",
   338  			},
   339  		},
   340  		{
   341  			name: "deb with epoch",
   342  			p: pkg.Package{
   343  				Name:         "name",
   344  				Version:      "1:3.2",
   345  				FoundBy:      "some-analyzer",
   346  				Type:         pkg.DebPkg,
   347  				MetadataType: pkg.DpkgMetadataType,
   348  				Metadata:     pkg.DpkgMetadata{},
   349  			},
   350  			expected: []string{
   351  				"cpe:2.3:a:name:name:1\\:3.2:*:*:*:*:*:*:*",
   352  			},
   353  		},
   354  		{
   355  			name: "cloudbees jenkins package identified via groupId",
   356  			p: pkg.Package{
   357  				Name:         "name",
   358  				Version:      "3.2",
   359  				FoundBy:      "some-analyzer",
   360  				Language:     pkg.Java,
   361  				Type:         pkg.JenkinsPluginPkg,
   362  				MetadataType: pkg.JavaMetadataType,
   363  				Metadata: pkg.JavaMetadata{
   364  					PomProperties: &pkg.PomProperties{
   365  						GroupID: "com.cloudbees.jenkins.plugins",
   366  					},
   367  				},
   368  			},
   369  			expected: []string{
   370  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   371  				"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
   372  				"cpe:2.3:a:cloudbees:name:3.2:*:*:*:*:*:*:*",
   373  			},
   374  		},
   375  		{
   376  			name: "jenkins.io package identified via groupId prefix",
   377  			p: pkg.Package{
   378  				Name:         "name",
   379  				Version:      "3.2",
   380  				FoundBy:      "some-analyzer",
   381  				Language:     pkg.Java,
   382  				Type:         pkg.JenkinsPluginPkg,
   383  				MetadataType: pkg.JavaMetadataType,
   384  				Metadata: pkg.JavaMetadata{
   385  					PomProperties: &pkg.PomProperties{
   386  						GroupID: "io.jenkins.plugins.name.something",
   387  					},
   388  				},
   389  			},
   390  			expected: []string{
   391  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   392  				"cpe:2.3:a:name:something:3.2:*:*:*:*:*:*:*",
   393  				"cpe:2.3:a:something:name:3.2:*:*:*:*:*:*:*",
   394  				"cpe:2.3:a:something:something:3.2:*:*:*:*:*:*:*",
   395  				"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
   396  				"cpe:2.3:a:jenkins:something:3.2:*:*:*:*:*:*:*",
   397  			},
   398  		},
   399  		{
   400  			name: "jenkins.io package identified via groupId",
   401  			p: pkg.Package{
   402  				Name:         "name",
   403  				Version:      "3.2",
   404  				FoundBy:      "some-analyzer",
   405  				Language:     pkg.Java,
   406  				Type:         pkg.JenkinsPluginPkg,
   407  				MetadataType: pkg.JavaMetadataType,
   408  				Metadata: pkg.JavaMetadata{
   409  					PomProperties: &pkg.PomProperties{
   410  						GroupID: "io.jenkins.plugins",
   411  					},
   412  				},
   413  			},
   414  			expected: []string{
   415  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   416  				"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
   417  			},
   418  		},
   419  		{
   420  			name: "jenkins-ci.io package identified via groupId",
   421  			p: pkg.Package{
   422  				Name:         "name",
   423  				Version:      "3.2",
   424  				FoundBy:      "some-analyzer",
   425  				Language:     pkg.Java,
   426  				Type:         pkg.JenkinsPluginPkg,
   427  				MetadataType: pkg.JavaMetadataType,
   428  				Metadata: pkg.JavaMetadata{
   429  					PomProperties: &pkg.PomProperties{
   430  						GroupID: "io.jenkins-ci.plugins",
   431  					},
   432  				},
   433  			},
   434  			expected: []string{
   435  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   436  				"cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*",
   437  				"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
   438  				"cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*",
   439  			},
   440  		},
   441  		{
   442  			name: "jenkins-ci.org package identified via groupId",
   443  			p: pkg.Package{
   444  				Name:         "name",
   445  				Version:      "3.2",
   446  				FoundBy:      "some-analyzer",
   447  				Language:     pkg.Java,
   448  				Type:         pkg.JenkinsPluginPkg,
   449  				MetadataType: pkg.JavaMetadataType,
   450  				Metadata: pkg.JavaMetadata{
   451  					PomProperties: &pkg.PomProperties{
   452  						GroupID: "org.jenkins-ci.plugins",
   453  					},
   454  				},
   455  			},
   456  			expected: []string{
   457  				"cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*",
   458  				"cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*",
   459  				"cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*",
   460  				"cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*",
   461  			},
   462  		},
   463  		{
   464  			name: "jira-atlassian filtering",
   465  			p: pkg.Package{
   466  				Name:         "jira_client_core",
   467  				Version:      "3.2",
   468  				FoundBy:      "some-analyzer",
   469  				Language:     pkg.Java,
   470  				Type:         pkg.JavaPkg,
   471  				MetadataType: pkg.JavaMetadataType,
   472  				Metadata: pkg.JavaMetadata{
   473  					PomProperties: &pkg.PomProperties{
   474  						GroupID:    "org.atlassian.jira",
   475  						ArtifactID: "jira_client_core",
   476  					},
   477  				},
   478  			},
   479  			expected: []string{
   480  				"cpe:2.3:a:atlassian:jira-client-core:3.2:*:*:*:*:*:*:*",
   481  				"cpe:2.3:a:atlassian:jira_client_core:3.2:*:*:*:*:*:*:*",
   482  				"cpe:2.3:a:jira-client-core:jira-client-core:3.2:*:*:*:*:*:*:*",
   483  				"cpe:2.3:a:jira-client-core:jira:3.2:*:*:*:*:*:*:*",
   484  				"cpe:2.3:a:jira-client-core:jira_client_core:3.2:*:*:*:*:*:*:*",
   485  				"cpe:2.3:a:jira-client:jira-client-core:3.2:*:*:*:*:*:*:*",
   486  				"cpe:2.3:a:jira-client:jira:3.2:*:*:*:*:*:*:*",
   487  				"cpe:2.3:a:jira-client:jira_client_core:3.2:*:*:*:*:*:*:*",
   488  				"cpe:2.3:a:jira:jira-client-core:3.2:*:*:*:*:*:*:*",
   489  				"cpe:2.3:a:jira:jira_client_core:3.2:*:*:*:*:*:*:*",
   490  				"cpe:2.3:a:jira_client:jira-client-core:3.2:*:*:*:*:*:*:*",
   491  				"cpe:2.3:a:jira_client:jira:3.2:*:*:*:*:*:*:*",
   492  				"cpe:2.3:a:jira_client:jira_client_core:3.2:*:*:*:*:*:*:*",
   493  				"cpe:2.3:a:jira_client_core:jira-client-core:3.2:*:*:*:*:*:*:*",
   494  				"cpe:2.3:a:jira_client_core:jira:3.2:*:*:*:*:*:*:*",
   495  				"cpe:2.3:a:jira_client_core:jira_client_core:3.2:*:*:*:*:*:*:*",
   496  			},
   497  		},
   498  		{
   499  			name: "jenkins filtering",
   500  			p: pkg.Package{
   501  				Name:         "cloudbees-installation-manager",
   502  				Version:      "2.89.0.33",
   503  				FoundBy:      "some-analyzer",
   504  				Language:     pkg.Java,
   505  				Type:         pkg.JavaPkg,
   506  				MetadataType: pkg.JavaMetadataType,
   507  				Metadata: pkg.JavaMetadata{
   508  					PomProperties: &pkg.PomProperties{
   509  						GroupID:    "com.cloudbees.jenkins.modules",
   510  						ArtifactID: "cloudbees-installation-manager",
   511  					},
   512  				},
   513  			},
   514  			expected: []string{
   515  				"cpe:2.3:a:cloudbees-installation-manager:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   516  				"cpe:2.3:a:cloudbees-installation-manager:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   517  				"cpe:2.3:a:cloudbees-installation:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   518  				"cpe:2.3:a:cloudbees-installation:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   519  				"cpe:2.3:a:cloudbees:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   520  				"cpe:2.3:a:cloudbees:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   521  				"cpe:2.3:a:cloudbees_installation:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   522  				"cpe:2.3:a:cloudbees_installation:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   523  				"cpe:2.3:a:cloudbees_installation_manager:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   524  				"cpe:2.3:a:cloudbees_installation_manager:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   525  				"cpe:2.3:a:jenkins:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   526  				"cpe:2.3:a:jenkins:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   527  				"cpe:2.3:a:modules:cloudbees-installation-manager:2.89.0.33:*:*:*:*:*:*:*",
   528  				"cpe:2.3:a:modules:cloudbees_installation_manager:2.89.0.33:*:*:*:*:*:*:*",
   529  			},
   530  		},
   531  		{
   532  			name: "go product and vendor candidates are wired up",
   533  			p: pkg.Package{
   534  				Name:     "github.com/someone/something",
   535  				Version:  "3.2",
   536  				FoundBy:  "go-cataloger",
   537  				Language: pkg.Go,
   538  				Type:     pkg.GoModulePkg,
   539  			},
   540  			expected: []string{
   541  				"cpe:2.3:a:someone:something:3.2:*:*:*:*:*:*:*",
   542  			},
   543  		},
   544  		{
   545  			name: "go product with vendor candidates and an extra sub-item",
   546  			p: pkg.Package{
   547  				Name:     "github.com/someone/something/more",
   548  				Version:  "3.2",
   549  				FoundBy:  "go-cataloger",
   550  				Language: pkg.Go,
   551  				Type:     pkg.GoModulePkg,
   552  			},
   553  			expected: []string{
   554  				"cpe:2.3:a:someone:something\\/more:3.2:*:*:*:*:*:*:*",
   555  			},
   556  		},
   557  		{
   558  			name: "generate no CPEs for indeterminate golang package name",
   559  			p: pkg.Package{
   560  				Name:     "github.com/what",
   561  				Version:  "3.2",
   562  				FoundBy:  "go-cataloger",
   563  				Language: pkg.Go,
   564  				Type:     pkg.GoModulePkg,
   565  			},
   566  			expected: []string{},
   567  		},
   568  		{
   569  			name: "regression: handlebars within java archive",
   570  			p: pkg.Package{
   571  				Name:         "handlebars",
   572  				Version:      "3.0.8",
   573  				Type:         pkg.JavaPkg,
   574  				Language:     pkg.Java,
   575  				FoundBy:      "java-cataloger",
   576  				MetadataType: pkg.JavaMetadataType,
   577  				Metadata: pkg.JavaMetadata{
   578  					Manifest: &pkg.JavaManifest{
   579  						Main: map[string]string{
   580  							"Extension-Name":         "handlebars",
   581  							"Group-Id":               "org.jenkins-ci.ui",
   582  							"Hudson-Version":         "2.204",
   583  							"Implementation-Title":   "handlebars",
   584  							"Implementation-Version": "3.0.8",
   585  							"Plugin-Version":         "3.0.8",
   586  							"Short-Name":             "handlebars",
   587  						},
   588  					},
   589  					PomProperties: &pkg.PomProperties{
   590  						GroupID:    "org.jenkins-ci.ui",
   591  						ArtifactID: "handlebars",
   592  						Version:    "3.0.8",
   593  					},
   594  				},
   595  			},
   596  			expected: []string{
   597  				"cpe:2.3:a:handlebars:handlebars:3.0.8:*:*:*:*:*:*:*",
   598  				"cpe:2.3:a:handlebarsjs:handlebars:3.0.8:*:*:*:*:*:*:*", // important!
   599  				"cpe:2.3:a:jenkins-ci:handlebars:3.0.8:*:*:*:*:*:*:*",
   600  				"cpe:2.3:a:jenkins:handlebars:3.0.8:*:*:*:*:*:*:*",
   601  				"cpe:2.3:a:jenkins_ci:handlebars:3.0.8:*:*:*:*:*:*:*",
   602  				"cpe:2.3:a:ui:handlebars:3.0.8:*:*:*:*:*:*:*",
   603  			},
   604  		},
   605  		{
   606  			name: "regression: jenkins plugin active-directory",
   607  			p: pkg.Package{
   608  				Name:         "active-directory",
   609  				Version:      "2.25.1",
   610  				Type:         pkg.JenkinsPluginPkg,
   611  				FoundBy:      "java-cataloger",
   612  				Language:     pkg.Java,
   613  				MetadataType: pkg.JavaMetadataType,
   614  				Metadata: pkg.JavaMetadata{
   615  					Manifest: &pkg.JavaManifest{
   616  						Main: map[string]string{
   617  							"Extension-Name": "active-directory",
   618  							"Group-Id":       "org.jenkins-ci.plugins",
   619  						},
   620  					},
   621  					PomProperties: &pkg.PomProperties{
   622  						GroupID:    "org.jenkins-ci.plugins",
   623  						ArtifactID: "org.jenkins-ci.plugins",
   624  						Version:    "2.25.1",
   625  					},
   626  				},
   627  			},
   628  			expected: []string{
   629  				"cpe:2.3:a:active-directory:active-directory:2.25.1:*:*:*:*:*:*:*",
   630  				"cpe:2.3:a:active-directory:active_directory:2.25.1:*:*:*:*:*:*:*",
   631  				"cpe:2.3:a:active:active-directory:2.25.1:*:*:*:*:*:*:*",
   632  				"cpe:2.3:a:active:active_directory:2.25.1:*:*:*:*:*:*:*",
   633  				"cpe:2.3:a:active_directory:active-directory:2.25.1:*:*:*:*:*:*:*",
   634  				"cpe:2.3:a:active_directory:active_directory:2.25.1:*:*:*:*:*:*:*",
   635  				"cpe:2.3:a:jenkins-ci:active-directory:2.25.1:*:*:*:*:*:*:*",
   636  				"cpe:2.3:a:jenkins-ci:active_directory:2.25.1:*:*:*:*:*:*:*",
   637  				"cpe:2.3:a:jenkins:active-directory:2.25.1:*:*:*:*:*:*:*", // important!
   638  				"cpe:2.3:a:jenkins:active_directory:2.25.1:*:*:*:*:*:*:*", // important!
   639  				"cpe:2.3:a:jenkins_ci:active-directory:2.25.1:*:*:*:*:*:*:*",
   640  				"cpe:2.3:a:jenkins_ci:active_directory:2.25.1:*:*:*:*:*:*:*",
   641  			},
   642  		},
   643  		{
   644  			name: "regression: special characters in CPE should result in no generation",
   645  			p: pkg.Package{
   646  				Name:         "bundler",
   647  				Version:      "2.1.4",
   648  				Type:         pkg.GemPkg,
   649  				FoundBy:      "gem-cataloger",
   650  				Language:     pkg.Ruby,
   651  				MetadataType: pkg.GemMetadataType,
   652  				Metadata: pkg.GemMetadata{
   653  					Name:    "bundler",
   654  					Version: "2.1.4",
   655  					Authors: []string{
   656  						"jessica lynn suttles",
   657  						"stephanie morillo",
   658  						"david rodríguez",
   659  						"andré medeiros",
   660  					},
   661  				},
   662  			},
   663  			expected: []string{
   664  				"cpe:2.3:a:bundler:bundler:2.1.4:*:*:*:*:*:*:*",
   665  				"cpe:2.3:a:ruby-lang:bundler:2.1.4:*:*:*:*:*:*:*",
   666  				"cpe:2.3:a:ruby:bundler:2.1.4:*:*:*:*:*:*:*",
   667  				"cpe:2.3:a:ruby_lang:bundler:2.1.4:*:*:*:*:*:*:*",
   668  				"cpe:2.3:a:jessica-lynn-suttles:bundler:2.1.4:*:*:*:*:*:*:*",
   669  				"cpe:2.3:a:jessica_lynn_suttles:bundler:2.1.4:*:*:*:*:*:*:*",
   670  				"cpe:2.3:a:stephanie-morillo:bundler:2.1.4:*:*:*:*:*:*:*",
   671  				"cpe:2.3:a:stephanie_morillo:bundler:2.1.4:*:*:*:*:*:*:*",
   672  			},
   673  		},
   674  		{
   675  			name: "regression: python redis shadows normal redis",
   676  			p: pkg.Package{
   677  				Name:     "redis",
   678  				Version:  "2.1.4",
   679  				Type:     pkg.PythonPkg,
   680  				FoundBy:  "some-analyzer",
   681  				Language: pkg.Python,
   682  			},
   683  			expected: []string{
   684  				"cpe:2.3:a:python-redis:python-redis:2.1.4:*:*:*:*:*:*:*",
   685  				"cpe:2.3:a:python-redis:python_redis:2.1.4:*:*:*:*:*:*:*",
   686  				"cpe:2.3:a:python-redis:redis:2.1.4:*:*:*:*:*:*:*",
   687  				"cpe:2.3:a:python:python-redis:2.1.4:*:*:*:*:*:*:*",
   688  				"cpe:2.3:a:python:python_redis:2.1.4:*:*:*:*:*:*:*",
   689  				"cpe:2.3:a:python:redis:2.1.4:*:*:*:*:*:*:*",
   690  				"cpe:2.3:a:python_redis:python-redis:2.1.4:*:*:*:*:*:*:*",
   691  				"cpe:2.3:a:python_redis:python_redis:2.1.4:*:*:*:*:*:*:*",
   692  				"cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*",
   693  			},
   694  		},
   695  		{
   696  			name: "regression: ruby-rake apk missing expected ruby-lang:rake CPE",
   697  			p: pkg.Package{
   698  				Name:         "ruby-rake",
   699  				Version:      "2.7.6-r0",
   700  				Type:         pkg.ApkPkg,
   701  				FoundBy:      "apk-db-analyzer",
   702  				Language:     pkg.UnknownLanguage,
   703  				MetadataType: pkg.ApkMetadataType,
   704  				Metadata: pkg.ApkMetadata{
   705  					Package:       "ruby-rake",
   706  					URL:           "https://www.ruby-lang.org/",
   707  					OriginPackage: "ruby",
   708  				},
   709  			},
   710  			expected: []string{
   711  				"cpe:2.3:a:ruby-lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
   712  				"cpe:2.3:a:rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
   713  				"cpe:2.3:a:rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
   714  				"cpe:2.3:a:rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
   715  				"cpe:2.3:a:ruby-lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
   716  				"cpe:2.3:a:ruby-lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
   717  				"cpe:2.3:a:ruby-rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
   718  				"cpe:2.3:a:ruby-rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
   719  				"cpe:2.3:a:ruby-rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
   720  				"cpe:2.3:a:ruby:rake:2.7.6-r0:*:*:*:*:*:*:*",
   721  				"cpe:2.3:a:ruby:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
   722  				"cpe:2.3:a:ruby:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
   723  				"cpe:2.3:a:ruby_lang:rake:2.7.6-r0:*:*:*:*:*:*:*",
   724  				"cpe:2.3:a:ruby_lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
   725  				"cpe:2.3:a:ruby_lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
   726  				"cpe:2.3:a:ruby_rake:rake:2.7.6-r0:*:*:*:*:*:*:*",
   727  				"cpe:2.3:a:ruby_rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*",
   728  				"cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*",
   729  			},
   730  		},
   731  	}
   732  
   733  	for _, test := range tests {
   734  		t.Run(test.name, func(t *testing.T) {
   735  			actual := Generate(test.p)
   736  
   737  			expectedCpeSet := set.NewStringSet(test.expected...)
   738  			actualCpeSet := set.NewStringSet()
   739  			for _, a := range actual {
   740  				actualCpeSet.Add(cpe.String(a))
   741  			}
   742  
   743  			extra := strset.Difference(actualCpeSet, expectedCpeSet).List()
   744  			sort.Strings(extra)
   745  			if len(extra) > 0 {
   746  				t.Errorf("found extra CPEs:")
   747  			}
   748  			for _, d := range extra {
   749  				fmt.Printf("   %q,\n", d)
   750  			}
   751  
   752  			missing := strset.Difference(expectedCpeSet, actualCpeSet).List()
   753  			sort.Strings(missing)
   754  			if len(missing) > 0 {
   755  				t.Errorf("missing CPEs:")
   756  			}
   757  			for _, d := range missing {
   758  				fmt.Printf("   %q,\n", d)
   759  			}
   760  		})
   761  	}
   762  }
   763  
   764  func TestCandidateProducts(t *testing.T) {
   765  	tests := []struct {
   766  		name     string
   767  		p        pkg.Package
   768  		expected []string
   769  	}{
   770  		{
   771  			name: "apache-cassandra",
   772  			p: pkg.Package{
   773  				Name: "apache-cassandra",
   774  				Type: pkg.JavaPkg,
   775  			},
   776  			expected: []string{"cassandra" /* <-- known good names | default guess --> */, "apache-cassandra", "apache_cassandra"},
   777  		},
   778  		{
   779  			name: "springframework",
   780  			p: pkg.Package{
   781  				Name: "springframework",
   782  				Type: pkg.JavaPkg,
   783  			},
   784  			expected: []string{"spring_framework", "springsource_spring_framework" /* <-- known good names | default guess --> */, "springframework"},
   785  		},
   786  		{
   787  			name: "spring-security-core",
   788  			p: pkg.Package{
   789  				Name: "spring-security-core",
   790  				Type: pkg.JavaPkg,
   791  			},
   792  			expected: []string{"spring-security-core", "spring_security", "spring_security_core"},
   793  		},
   794  		{
   795  			name: "java",
   796  			p: pkg.Package{
   797  				Name:     "some-java-package-with-group-id",
   798  				Type:     pkg.JavaPkg,
   799  				Language: pkg.Java,
   800  				Metadata: pkg.JavaMetadata{
   801  					PomProperties: &pkg.PomProperties{
   802  						GroupID: "com.apple.itunes",
   803  					},
   804  				},
   805  			},
   806  			expected: []string{"itunes", "some-java-package-with-group-id", "some_java_package_with_group_id"},
   807  		},
   808  		{
   809  			name: "java-with-asterisk",
   810  			p: pkg.Package{
   811  				Name:     "some-java-package-with-group-id",
   812  				Type:     pkg.JavaPkg,
   813  				Language: pkg.Java,
   814  				Metadata: pkg.JavaMetadata{
   815  					PomProperties: &pkg.PomProperties{
   816  						GroupID: "com.apple.itunes.*",
   817  					},
   818  				},
   819  			},
   820  			expected: []string{"itunes", "some-java-package-with-group-id", "some_java_package_with_group_id"},
   821  		},
   822  		{
   823  			name: "jenkins-plugin",
   824  			p: pkg.Package{
   825  				Name:     "some-jenkins-plugin",
   826  				Type:     pkg.JenkinsPluginPkg,
   827  				Language: pkg.Java,
   828  				Metadata: pkg.JavaMetadata{
   829  					PomProperties: &pkg.PomProperties{
   830  						GroupID: "com.cloudbees.jenkins.plugins",
   831  					},
   832  				},
   833  			},
   834  			expected: []string{"some-jenkins-plugin", "some_jenkins_plugin", "jenkins"},
   835  		},
   836  		{
   837  			name: "javascript",
   838  			p: pkg.Package{
   839  				Name: "handlebars.js",
   840  				Type: pkg.NpmPkg,
   841  			},
   842  			expected: []string{"handlebars" /* <-- known good names | default guess --> */, "handlebars.js"},
   843  		},
   844  		{
   845  			name: "gem",
   846  			p: pkg.Package{
   847  				Name: "RedCloth",
   848  				Type: pkg.GemPkg,
   849  			},
   850  			expected: []string{"redcloth_library" /* <-- known good names | default guess --> */, "RedCloth"},
   851  		},
   852  		{
   853  			name: "python",
   854  			p: pkg.Package{
   855  				Name: "python-rrdtool",
   856  				Type: pkg.PythonPkg,
   857  			},
   858  			expected: []string{"rrdtool" /* <-- known good names | default guess --> */, "python-rrdtool", "python_rrdtool"},
   859  		},
   860  	}
   861  
   862  	for _, test := range tests {
   863  		t.Run(test.name, func(t *testing.T) {
   864  			assert.ElementsMatch(t, test.expected, candidateProducts(test.p))
   865  		})
   866  	}
   867  }
   868  
   869  func TestCandidateVendor(t *testing.T) {
   870  	tests := []struct {
   871  		name     string
   872  		p        pkg.Package
   873  		expected []string
   874  	}{
   875  		{
   876  			name: "elasticsearch",
   877  			p: pkg.Package{
   878  				Name: "elasticsearch",
   879  				Type: pkg.JavaPkg,
   880  			},
   881  			expected: []string{"elastic" /* <-- known good names | default guess --> */, "elasticsearch"},
   882  		},
   883  		{
   884  			name: "spring-security",
   885  			p: pkg.Package{
   886  				Name: "spring-security-core",
   887  				Type: pkg.JavaPkg,
   888  			},
   889  			expected: []string{"vmware" /* <-- known good names | default guess --> */, "spring", "spring-security", "spring-security-core", "spring_security_core", "spring_security"},
   890  		},
   891  		{
   892  			name: "log4j",
   893  			p: pkg.Package{
   894  				Name: "log4j",
   895  				Type: pkg.JavaPkg,
   896  			},
   897  			expected: []string{"apache"},
   898  		},
   899  		{
   900  			name: "Django",
   901  			p: pkg.Package{
   902  				Name: "Django",
   903  				Type: pkg.PythonPkg,
   904  			},
   905  			expected: []string{"djangoproject" /* <-- known good names | default guess --> */, "Django"},
   906  		},
   907  	}
   908  
   909  	for _, test := range tests {
   910  		t.Run(fmt.Sprintf("%+v %+v", test.p, test.expected), func(t *testing.T) {
   911  			assert.ElementsMatch(t, test.expected, candidateVendors(test.p))
   912  		})
   913  	}
   914  }
   915  
   916  func Test_generateSubSelections(t *testing.T) {
   917  	tests := []struct {
   918  		field    string
   919  		expected []string
   920  	}{
   921  		{
   922  			field:    "jenkins",
   923  			expected: []string{"jenkins"},
   924  		},
   925  		{
   926  			field:    "jenkins-ci",
   927  			expected: []string{"jenkins", "jenkins-ci"},
   928  		},
   929  		{
   930  			field:    "jenkins--ci",
   931  			expected: []string{"jenkins", "jenkins-ci"},
   932  		},
   933  		{
   934  			field:    "jenkins_ci_tools",
   935  			expected: []string{"jenkins", "jenkins_ci", "jenkins_ci_tools"},
   936  		},
   937  		{
   938  			field:    "-jenkins",
   939  			expected: []string{"jenkins"},
   940  		},
   941  		{
   942  			field:    "jenkins_",
   943  			expected: []string{"jenkins"},
   944  		},
   945  		{
   946  			field:    "",
   947  			expected: nil,
   948  		},
   949  		{
   950  			field:    "-",
   951  			expected: nil,
   952  		},
   953  		{
   954  			field:    "_",
   955  			expected: nil,
   956  		},
   957  	}
   958  	for _, test := range tests {
   959  		t.Run(test.field, func(t *testing.T) {
   960  			assert.ElementsMatch(t, test.expected, generateSubSelections(test.field))
   961  		})
   962  	}
   963  }
   964  
   965  func Test_addSeparatorVariations(t *testing.T) {
   966  	tests := []struct {
   967  		input    []string
   968  		expected []string
   969  	}{
   970  		{
   971  			input:    []string{"jenkins-ci"},
   972  			expected: []string{"jenkins-ci", "jenkins_ci"}, //, "jenkinsci"},
   973  		},
   974  		{
   975  			input:    []string{"jenkins_ci"},
   976  			expected: []string{"jenkins_ci", "jenkins-ci"}, //, "jenkinsci"},
   977  		},
   978  		{
   979  			input:    []string{"jenkins"},
   980  			expected: []string{"jenkins"},
   981  		},
   982  		{
   983  			input:    []string{"jenkins-ci", "circle-ci"},
   984  			expected: []string{"jenkins-ci", "jenkins_ci", "circle-ci", "circle_ci"}, //, "jenkinsci", "circleci"},
   985  		},
   986  	}
   987  	for _, test := range tests {
   988  		t.Run(strings.Join(test.input, ","), func(t *testing.T) {
   989  			val := newFieldCandidateSet(test.input...)
   990  			addDelimiterVariations(val)
   991  			assert.ElementsMatch(t, test.expected, val.values())
   992  		})
   993  	}
   994  }
   995  
   996  func TestDictionaryFindIsWired(t *testing.T) {
   997  
   998  	tests := []struct {
   999  		name       string
  1000  		pkg        pkg.Package
  1001  		want       string
  1002  		wantExists bool
  1003  	}{
  1004  		{
  1005  			name: "sanity check that cpe data is wired up",
  1006  			pkg: pkg.Package{
  1007  				Name:    "openssl",
  1008  				Version: "1.0.2k",
  1009  				Type:    pkg.GemPkg,
  1010  			},
  1011  			want: "cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*",
  1012  			// without the cpe data wired up, this would be empty (generation also creates cpe:2.3:a:openssl:openssl:1.0.2k:*:*:*:*:*:*:*)
  1013  			wantExists: true,
  1014  		},
  1015  	}
  1016  	for _, tt := range tests {
  1017  		t.Run(tt.name, func(t *testing.T) {
  1018  			got, gotExists := DictionaryFind(tt.pkg)
  1019  
  1020  			assert.Equal(t, tt.want, got.BindToFmtString())
  1021  			assert.Equal(t, tt.wantExists, gotExists)
  1022  		})
  1023  	}
  1024  }