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