github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/internal/cpegenerate/generate_test.go (about)

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