github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/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 Metadata: pkg.PythonPackage{ 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 Metadata: pkg.NpmPackage{ 124 Author: "jon", 125 URL: "https://github.com/bob/npm-name", 126 }, 127 }, 128 expected: []string{ 129 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 130 "cpe:2.3:a:bob:name:3.2:*:*:*:*:*:*:*", 131 }, 132 }, 133 { 134 name: "ruby language", 135 p: pkg.Package{ 136 Name: "name", 137 Version: "3.2", 138 FoundBy: "some-analyzer", 139 Language: pkg.Ruby, 140 Type: pkg.DebPkg, 141 Metadata: pkg.RubyGemspec{ 142 Authors: []string{ 143 "someones name", 144 "someones.elses.name@gmail.com", 145 }, 146 Homepage: "https://github.com/tom/ruby-name", 147 }, 148 }, 149 expected: []string{ 150 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 151 "cpe:2.3:a:ruby-lang:name:3.2:*:*:*:*:*:*:*", 152 "cpe:2.3:a:ruby:name:3.2:*:*:*:*:*:*:*", 153 "cpe:2.3:a:ruby_lang:name:3.2:*:*:*:*:*:*:*", 154 "cpe:2.3:a:someones-elses-name:name:3.2:*:*:*:*:*:*:*", 155 "cpe:2.3:a:someones-name: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:tom:name:3.2:*:*:*:*:*:*:*", 159 }, 160 }, 161 { 162 name: "java language", 163 p: pkg.Package{ 164 Name: "name", 165 Version: "3.2", 166 FoundBy: "some-analyzer", 167 Language: pkg.Java, 168 Type: pkg.JavaPkg, 169 }, 170 expected: []string{ 171 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 172 }, 173 }, 174 { 175 name: "java language with groupID", 176 p: pkg.Package{ 177 Name: "name", 178 Version: "3.2", 179 FoundBy: "some-analyzer", 180 Language: pkg.Java, 181 Type: pkg.JavaPkg, 182 Metadata: pkg.JavaArchive{ 183 PomProperties: &pkg.JavaPomProperties{ 184 GroupID: "org.sonatype.nexus", 185 }, 186 }, 187 }, 188 expected: []string{ 189 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 190 "cpe:2.3:a:name:nexus:3.2:*:*:*:*:*:*:*", 191 "cpe:2.3:a:nexus:name:3.2:*:*:*:*:*:*:*", 192 "cpe:2.3:a:nexus:nexus:3.2:*:*:*:*:*:*:*", 193 "cpe:2.3:a:sonatype:name:3.2:*:*:*:*:*:*:*", 194 "cpe:2.3:a:sonatype:nexus:3.2:*:*:*:*:*:*:*", 195 }, 196 }, 197 { 198 name: "java with URL in metadata", // regression: https://github.com/anchore/grype/issues/417 199 p: pkg.Package{ 200 Name: "wstx-asl", 201 Version: "3.2.7", 202 Type: pkg.JavaPkg, 203 Metadata: pkg.JavaArchive{ 204 Manifest: &pkg.JavaManifest{ 205 Main: map[string]string{ 206 "Ant-Version": "Apache Ant 1.6.5", 207 "Built-By": "tatu", 208 "Created-By": "1.4.2_03-b02 (Sun Microsystems Inc.)", 209 "Implementation-Title": "WoodSToX XML-processor", 210 "Implementation-Vendor": "woodstox.codehaus.org", 211 "Implementation-Version": "3.2.7", 212 "Manifest-Version": "1.0", 213 "Specification-Title": "StAX 1.0 API", 214 "Specification-Vendor": "http://jcp.org/en/jsr/detail?id=173", 215 "Specification-Version": "1.0", 216 }, 217 }, 218 }, 219 }, 220 expected: []string{ 221 "cpe:2.3:a:woodstox_codehaus_org:wstx-asl:3.2.7:*:*:*:*:*:*:*", 222 "cpe:2.3:a:woodstox_codehaus_org:wstx_asl:3.2.7:*:*:*:*:*:*:*", 223 "cpe:2.3:a:woodstox-codehaus-org:wstx_asl:3.2.7:*:*:*:*:*:*:*", 224 "cpe:2.3:a:woodstox-codehaus-org:wstx-asl:3.2.7:*:*:*:*:*:*:*", 225 "cpe:2.3:a:wstx_asl:wstx-asl:3.2.7:*:*:*:*:*:*:*", 226 "cpe:2.3:a:wstx-asl:wstx-asl:3.2.7:*:*:*:*:*:*:*", 227 "cpe:2.3:a:wstx-asl:wstx_asl:3.2.7:*:*:*:*:*:*:*", 228 "cpe:2.3:a:wstx_asl:wstx_asl:3.2.7:*:*:*:*:*:*:*", 229 "cpe:2.3:a:wstx:wstx_asl:3.2.7:*:*:*:*:*:*:*", 230 "cpe:2.3:a:wstx:wstx-asl:3.2.7:*:*:*:*:*:*:*", 231 }, 232 }, 233 { 234 name: "jenkins package identified via pkg type", 235 p: pkg.Package{ 236 Name: "name", 237 Version: "3.2", 238 FoundBy: "some-analyzer", 239 Language: pkg.Java, 240 Type: pkg.JenkinsPluginPkg, 241 }, 242 expected: []string{ 243 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 244 }, 245 }, 246 { 247 name: "java language - multi tier manifest fields", 248 p: pkg.Package{ 249 Name: "cxf-rt-bindings-xml", 250 Version: "3.3.10", 251 FoundBy: "java-cataloger", 252 Language: pkg.Java, 253 Type: pkg.JavaPkg, 254 Metadata: pkg.JavaArchive{ 255 VirtualPath: "/opt/jboss/keycloak/modules/system/layers/base/org/apache/cxf/impl/main/cxf-rt-bindings-xml-3.3.10.jar", 256 Manifest: &pkg.JavaManifest{ 257 Main: map[string]string{ 258 "Automatic-Module-Name": "org.apache.cxf.binding.xml", 259 "Bnd-LastModified": "1615836524860", 260 "Build-Jdk": "1.8.0_261", 261 "Built-By": "dkulp", 262 "Bundle-ActivationPolicy": "lazy", 263 "Bundle-Description": "Apache CXF Runtime XML Binding", 264 "Bundle-DocURL": "http://cxf.apache.org", 265 "Bundle-License": "https://www.apache.org/licenses/LICENSE-2.0.txt", 266 "Bundle-ManifestVersion": "2", 267 "Bundle-Name": "Apache CXF Runtime XML Binding", 268 "Bundle-SymbolicName": "org.apache.cxf.cxf-rt-bindings-xml", 269 "Bundle-Vendor": "The Apache Software Foundation", 270 "Bundle-Version": "3.3.10", 271 "Created-By": "Apache Maven Bundle Plugin", 272 "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\"", 273 "Implementation-Vendor": "The Apache Software Foundation", 274 "Implementation-Vendor-Id": "org.apache", 275 "Implementation-Version": "3.3.10", 276 "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", 277 "Manifest-Version": "1.0", 278 "Require-Capability": "osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"", 279 "Specification-Vendor": "The Apache Software Foundation", 280 "Specification-Version": "3.3.10", 281 "Tool": "Bnd-4.2.0.201903051501", 282 }, 283 }, 284 PomProperties: &pkg.JavaPomProperties{ 285 Path: "META-INF/maven/org.apache.cxf/cxf-rt-bindings-xml/pom.properties", 286 GroupID: "org.apache.cxf", 287 ArtifactID: "cxf-rt-bindings-xml", 288 Version: "3.3.10", 289 }, 290 }, 291 }, 292 expected: []string{ 293 "cpe:2.3:a:apache:cxf-rt-bindings-xml:3.3.10:*:*:*:*:*:*:*", 294 "cpe:2.3:a:apache:cxf:3.3.10:*:*:*:*:*:*:*", 295 "cpe:2.3:a:apache:cxf_rt_bindings_xml:3.3.10:*:*:*:*:*:*:*", 296 }, 297 }, 298 { 299 name: "rpm vendor selection", 300 p: pkg.Package{ 301 Name: "name", 302 Version: "3.2", 303 FoundBy: "some-analyzer", 304 Type: pkg.RpmPkg, 305 Metadata: pkg.RpmDBEntry{ 306 Vendor: "some-vendor", 307 }, 308 }, 309 expected: []string{ 310 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 311 "cpe:2.3:a:some-vendor:name:3.2:*:*:*:*:*:*:*", 312 "cpe:2.3:a:some_vendor:name:3.2:*:*:*:*:*:*:*", 313 }, 314 }, 315 { 316 name: "rpm with epoch", 317 p: pkg.Package{ 318 Name: "name", 319 Version: "1:3.2", 320 FoundBy: "some-analyzer", 321 Type: pkg.RpmPkg, 322 Metadata: pkg.RpmDBEntry{ 323 Vendor: "some-vendor", 324 }, 325 }, 326 expected: []string{ 327 "cpe:2.3:a:name:name:1\\:3.2:*:*:*:*:*:*:*", 328 "cpe:2.3:a:some-vendor:name:1\\:3.2:*:*:*:*:*:*:*", 329 "cpe:2.3:a:some_vendor:name:1\\:3.2:*:*:*:*:*:*:*", 330 }, 331 }, 332 { 333 name: "deb with epoch", 334 p: pkg.Package{ 335 Name: "name", 336 Version: "1:3.2", 337 FoundBy: "some-analyzer", 338 Type: pkg.DebPkg, 339 Metadata: pkg.DpkgDBEntry{}, 340 }, 341 expected: []string{ 342 "cpe:2.3:a:name:name:1\\:3.2:*:*:*:*:*:*:*", 343 }, 344 }, 345 { 346 name: "cloudbees jenkins package identified via groupId", 347 p: pkg.Package{ 348 Name: "name", 349 Version: "3.2", 350 FoundBy: "some-analyzer", 351 Language: pkg.Java, 352 Type: pkg.JenkinsPluginPkg, 353 Metadata: pkg.JavaArchive{ 354 PomProperties: &pkg.JavaPomProperties{ 355 GroupID: "com.cloudbees.jenkins.plugins", 356 }, 357 }, 358 }, 359 expected: []string{ 360 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 361 "cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*", 362 "cpe:2.3:a:cloudbees:name:3.2:*:*:*:*:*:*:*", 363 }, 364 }, 365 { 366 name: "jenkins.io package identified via groupId prefix", 367 p: pkg.Package{ 368 Name: "name", 369 Version: "3.2", 370 FoundBy: "some-analyzer", 371 Language: pkg.Java, 372 Type: pkg.JenkinsPluginPkg, 373 Metadata: pkg.JavaArchive{ 374 PomProperties: &pkg.JavaPomProperties{ 375 GroupID: "io.jenkins.plugins.name.something", 376 }, 377 }, 378 }, 379 expected: []string{ 380 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 381 "cpe:2.3:a:name:something:3.2:*:*:*:*:*:*:*", 382 "cpe:2.3:a:something:name:3.2:*:*:*:*:*:*:*", 383 "cpe:2.3:a:something:something:3.2:*:*:*:*:*:*:*", 384 "cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*", 385 "cpe:2.3:a:jenkins:something:3.2:*:*:*:*:*:*:*", 386 }, 387 }, 388 { 389 name: "jenkins.io package identified via groupId", 390 p: pkg.Package{ 391 Name: "name", 392 Version: "3.2", 393 FoundBy: "some-analyzer", 394 Language: pkg.Java, 395 Type: pkg.JenkinsPluginPkg, 396 Metadata: pkg.JavaArchive{ 397 PomProperties: &pkg.JavaPomProperties{ 398 GroupID: "io.jenkins.plugins", 399 }, 400 }, 401 }, 402 expected: []string{ 403 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 404 "cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*", 405 }, 406 }, 407 { 408 name: "jenkins-ci.io package identified via groupId", 409 p: pkg.Package{ 410 Name: "name", 411 Version: "3.2", 412 FoundBy: "some-analyzer", 413 Language: pkg.Java, 414 Type: pkg.JenkinsPluginPkg, 415 Metadata: pkg.JavaArchive{ 416 PomProperties: &pkg.JavaPomProperties{ 417 GroupID: "io.jenkins-ci.plugins", 418 }, 419 }, 420 }, 421 expected: []string{ 422 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 423 "cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*", 424 "cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*", 425 "cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*", 426 }, 427 }, 428 { 429 name: "jenkins-ci.org package identified via groupId", 430 p: pkg.Package{ 431 Name: "name", 432 Version: "3.2", 433 FoundBy: "some-analyzer", 434 Language: pkg.Java, 435 Type: pkg.JenkinsPluginPkg, 436 Metadata: pkg.JavaArchive{ 437 PomProperties: &pkg.JavaPomProperties{ 438 GroupID: "org.jenkins-ci.plugins", 439 }, 440 }, 441 }, 442 expected: []string{ 443 "cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*", 444 "cpe:2.3:a:jenkins-ci:name:3.2:*:*:*:*:*:*:*", 445 "cpe:2.3:a:jenkins:name:3.2:*:*:*:*:*:*:*", 446 "cpe:2.3:a:jenkins_ci:name:3.2:*:*:*:*:*:*:*", 447 }, 448 }, 449 { 450 name: "jira-atlassian filtering", 451 p: pkg.Package{ 452 Name: "jira_client_core", 453 Version: "3.2", 454 FoundBy: "some-analyzer", 455 Language: pkg.Java, 456 Type: pkg.JavaPkg, 457 Metadata: pkg.JavaArchive{ 458 PomProperties: &pkg.JavaPomProperties{ 459 GroupID: "org.atlassian.jira", 460 ArtifactID: "jira_client_core", 461 }, 462 }, 463 }, 464 expected: []string{ 465 "cpe:2.3:a:atlassian:jira-client-core:3.2:*:*:*:*:*:*:*", 466 "cpe:2.3:a:atlassian:jira_client_core:3.2:*:*:*:*:*:*:*", 467 "cpe:2.3:a:jira-client-core:jira-client-core:3.2:*:*:*:*:*:*:*", 468 "cpe:2.3:a:jira-client-core:jira:3.2:*:*:*:*:*:*:*", 469 "cpe:2.3:a:jira-client-core:jira_client_core:3.2:*:*:*:*:*:*:*", 470 "cpe:2.3:a:jira-client:jira-client-core:3.2:*:*:*:*:*:*:*", 471 "cpe:2.3:a:jira-client:jira:3.2:*:*:*:*:*:*:*", 472 "cpe:2.3:a:jira-client:jira_client_core:3.2:*:*:*:*:*:*:*", 473 "cpe:2.3:a:jira:jira-client-core:3.2:*:*:*:*:*:*:*", 474 "cpe:2.3:a:jira:jira_client_core:3.2:*:*:*:*:*:*:*", 475 "cpe:2.3:a:jira_client:jira-client-core:3.2:*:*:*:*:*:*:*", 476 "cpe:2.3:a:jira_client:jira:3.2:*:*:*:*:*:*:*", 477 "cpe:2.3:a:jira_client: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 }, 482 }, 483 { 484 name: "jenkins filtering", 485 p: pkg.Package{ 486 Name: "cloudbees-installation-manager", 487 Version: "2.89.0.33", 488 FoundBy: "some-analyzer", 489 Language: pkg.Java, 490 Type: pkg.JavaPkg, 491 Metadata: pkg.JavaArchive{ 492 PomProperties: &pkg.JavaPomProperties{ 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 Metadata: pkg.JavaArchive{ 561 Manifest: &pkg.JavaManifest{ 562 Main: map[string]string{ 563 "Extension-Name": "handlebars", 564 "Group-Id": "org.jenkins-ci.ui", 565 "Hudson-Version": "2.204", 566 "Implementation-Title": "handlebars", 567 "Implementation-Version": "3.0.8", 568 "Plugin-Version": "3.0.8", 569 "Short-Name": "handlebars", 570 }, 571 }, 572 PomProperties: &pkg.JavaPomProperties{ 573 GroupID: "org.jenkins-ci.ui", 574 ArtifactID: "handlebars", 575 Version: "3.0.8", 576 }, 577 }, 578 }, 579 expected: []string{ 580 "cpe:2.3:a:handlebars:handlebars:3.0.8:*:*:*:*:*:*:*", 581 "cpe:2.3:a:handlebarsjs:handlebars:3.0.8:*:*:*:*:*:*:*", // important! 582 "cpe:2.3:a:jenkins-ci:handlebars:3.0.8:*:*:*:*:*:*:*", 583 "cpe:2.3:a:jenkins:handlebars:3.0.8:*:*:*:*:*:*:*", 584 "cpe:2.3:a:jenkins_ci:handlebars:3.0.8:*:*:*:*:*:*:*", 585 "cpe:2.3:a:ui:handlebars:3.0.8:*:*:*:*:*:*:*", 586 }, 587 }, 588 { 589 name: "regression: jenkins plugin active-directory", 590 p: pkg.Package{ 591 Name: "active-directory", 592 Version: "2.25.1", 593 Type: pkg.JenkinsPluginPkg, 594 FoundBy: "java-cataloger", 595 Language: pkg.Java, 596 Metadata: pkg.JavaArchive{ 597 Manifest: &pkg.JavaManifest{ 598 Main: map[string]string{ 599 "Extension-Name": "active-directory", 600 "Group-Id": "org.jenkins-ci.plugins", 601 }, 602 }, 603 PomProperties: &pkg.JavaPomProperties{ 604 GroupID: "org.jenkins-ci.plugins", 605 ArtifactID: "org.jenkins-ci.plugins", 606 Version: "2.25.1", 607 }, 608 }, 609 }, 610 expected: []string{ 611 "cpe:2.3:a:active-directory:active-directory:2.25.1:*:*:*:*:*:*:*", 612 "cpe:2.3:a:active-directory:active_directory:2.25.1:*:*:*:*:*:*:*", 613 "cpe:2.3:a:active:active-directory:2.25.1:*:*:*:*:*:*:*", 614 "cpe:2.3:a:active:active_directory:2.25.1:*:*:*:*:*:*:*", 615 "cpe:2.3:a:active_directory:active-directory:2.25.1:*:*:*:*:*:*:*", 616 "cpe:2.3:a:active_directory:active_directory:2.25.1:*:*:*:*:*:*:*", 617 "cpe:2.3:a:jenkins-ci:active-directory:2.25.1:*:*:*:*:*:*:*", 618 "cpe:2.3:a:jenkins-ci:active_directory:2.25.1:*:*:*:*:*:*:*", 619 "cpe:2.3:a:jenkins:active-directory:2.25.1:*:*:*:*:*:*:*", // important! 620 "cpe:2.3:a:jenkins:active_directory:2.25.1:*:*:*:*:*:*:*", // important! 621 "cpe:2.3:a:jenkins_ci:active-directory:2.25.1:*:*:*:*:*:*:*", 622 "cpe:2.3:a:jenkins_ci:active_directory:2.25.1:*:*:*:*:*:*:*", 623 }, 624 }, 625 { 626 name: "regression: special characters in CPE should result in no generation", 627 p: pkg.Package{ 628 Name: "bundler", 629 Version: "2.1.4", 630 Type: pkg.GemPkg, 631 FoundBy: "gem-cataloger", 632 Language: pkg.Ruby, 633 Metadata: pkg.RubyGemspec{ 634 Name: "bundler", 635 Version: "2.1.4", 636 Authors: []string{ 637 "jessica lynn suttles", 638 "stephanie morillo", 639 "david rodrÃguez", 640 "andré medeiros", 641 }, 642 }, 643 }, 644 expected: []string{ 645 "cpe:2.3:a:bundler:bundler:2.1.4:*:*:*:*:*:*:*", 646 "cpe:2.3:a:ruby-lang:bundler:2.1.4:*:*:*:*:*:*:*", 647 "cpe:2.3:a:ruby:bundler:2.1.4:*:*:*:*:*:*:*", 648 "cpe:2.3:a:ruby_lang:bundler:2.1.4:*:*:*:*:*:*:*", 649 "cpe:2.3:a:jessica-lynn-suttles:bundler:2.1.4:*:*:*:*:*:*:*", 650 "cpe:2.3:a:jessica_lynn_suttles:bundler:2.1.4:*:*:*:*:*:*:*", 651 "cpe:2.3:a:stephanie-morillo:bundler:2.1.4:*:*:*:*:*:*:*", 652 "cpe:2.3:a:stephanie_morillo:bundler:2.1.4:*:*:*:*:*:*:*", 653 }, 654 }, 655 { 656 name: "regression: python redis shadows normal redis", 657 p: pkg.Package{ 658 Name: "redis", 659 Version: "2.1.4", 660 Type: pkg.PythonPkg, 661 FoundBy: "some-analyzer", 662 Language: pkg.Python, 663 }, 664 expected: []string{ 665 "cpe:2.3:a:python-redis:python-redis:2.1.4:*:*:*:*:*:*:*", 666 "cpe:2.3:a:python-redis:python_redis:2.1.4:*:*:*:*:*:*:*", 667 "cpe:2.3:a:python-redis:redis:2.1.4:*:*:*:*:*:*:*", 668 "cpe:2.3:a:python:python-redis:2.1.4:*:*:*:*:*:*:*", 669 "cpe:2.3:a:python:python_redis:2.1.4:*:*:*:*:*:*:*", 670 "cpe:2.3:a:python:redis:2.1.4:*:*:*:*:*:*:*", 671 "cpe:2.3:a:python_redis:python-redis:2.1.4:*:*:*:*:*:*:*", 672 "cpe:2.3:a:python_redis:python_redis:2.1.4:*:*:*:*:*:*:*", 673 "cpe:2.3:a:python_redis:redis:2.1.4:*:*:*:*:*:*:*", 674 }, 675 }, 676 { 677 name: "regression: ruby-rake apk missing expected ruby-lang:rake CPE", 678 p: pkg.Package{ 679 Name: "ruby-rake", 680 Version: "2.7.6-r0", 681 Type: pkg.ApkPkg, 682 FoundBy: "apk-db-analyzer", 683 Language: pkg.UnknownLanguage, 684 Metadata: pkg.ApkDBEntry{ 685 Package: "ruby-rake", 686 URL: "https://www.ruby-lang.org/", 687 OriginPackage: "ruby", 688 }, 689 }, 690 expected: []string{ 691 "cpe:2.3:a:ruby-lang:rake:2.7.6-r0:*:*:*:*:*:*:*", 692 "cpe:2.3:a:rake:rake:2.7.6-r0:*:*:*:*:*:*:*", 693 "cpe:2.3:a:rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*", 694 "cpe:2.3:a:rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", 695 "cpe:2.3:a:ruby-lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*", 696 "cpe:2.3:a:ruby-lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", 697 "cpe:2.3:a:ruby-rake:rake:2.7.6-r0:*:*:*:*:*:*:*", 698 "cpe:2.3:a:ruby-rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*", 699 "cpe:2.3:a:ruby-rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", 700 "cpe:2.3:a:ruby:rake:2.7.6-r0:*:*:*:*:*:*:*", 701 "cpe:2.3:a:ruby:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*", 702 "cpe:2.3:a:ruby:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", 703 "cpe:2.3:a:ruby_lang:rake:2.7.6-r0:*:*:*:*:*:*:*", 704 "cpe:2.3:a:ruby_lang:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*", 705 "cpe:2.3:a:ruby_lang:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", 706 "cpe:2.3:a:ruby_rake:rake:2.7.6-r0:*:*:*:*:*:*:*", 707 "cpe:2.3:a:ruby_rake:ruby-rake:2.7.6-r0:*:*:*:*:*:*:*", 708 "cpe:2.3:a:ruby_rake:ruby_rake:2.7.6-r0:*:*:*:*:*:*:*", 709 }, 710 }, 711 } 712 713 for _, test := range tests { 714 t.Run(test.name, func(t *testing.T) { 715 actual := Generate(test.p) 716 717 expectedCpeSet := set.NewStringSet(test.expected...) 718 actualCpeSet := set.NewStringSet() 719 for _, a := range actual { 720 actualCpeSet.Add(cpe.String(a)) 721 } 722 723 extra := strset.Difference(actualCpeSet, expectedCpeSet).List() 724 sort.Strings(extra) 725 if len(extra) > 0 { 726 t.Errorf("found extra CPEs:") 727 for _, d := range extra { 728 t.Logf(" %q,\n", d) 729 } 730 } 731 732 missing := strset.Difference(expectedCpeSet, actualCpeSet).List() 733 sort.Strings(missing) 734 if len(missing) > 0 { 735 t.Errorf("missing CPEs:") 736 for _, d := range missing { 737 t.Logf(" %q,\n", d) 738 } 739 } 740 }) 741 } 742 } 743 744 func TestCandidateProducts(t *testing.T) { 745 tests := []struct { 746 name string 747 p pkg.Package 748 expected []string 749 }{ 750 { 751 name: "apache-cassandra", 752 p: pkg.Package{ 753 Name: "apache-cassandra", 754 Type: pkg.JavaPkg, 755 }, 756 expected: []string{"cassandra" /* <-- known good names | default guess --> */, "apache-cassandra", "apache_cassandra"}, 757 }, 758 { 759 name: "springframework", 760 p: pkg.Package{ 761 Name: "springframework", 762 Type: pkg.JavaPkg, 763 }, 764 expected: []string{"spring_framework", "springsource_spring_framework" /* <-- known good names | default guess --> */, "springframework"}, 765 }, 766 { 767 name: "spring-security-core", 768 p: pkg.Package{ 769 Name: "spring-security-core", 770 Type: pkg.JavaPkg, 771 }, 772 expected: []string{"spring-security-core", "spring_security", "spring_security_core"}, 773 }, 774 { 775 name: "java", 776 p: pkg.Package{ 777 Name: "some-java-package-with-group-id", 778 Type: pkg.JavaPkg, 779 Language: pkg.Java, 780 Metadata: pkg.JavaArchive{ 781 PomProperties: &pkg.JavaPomProperties{ 782 GroupID: "com.apple.itunes", 783 }, 784 }, 785 }, 786 expected: []string{"itunes", "some-java-package-with-group-id", "some_java_package_with_group_id"}, 787 }, 788 { 789 name: "java-with-asterisk", 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: "jenkins-plugin", 804 p: pkg.Package{ 805 Name: "some-jenkins-plugin", 806 Type: pkg.JenkinsPluginPkg, 807 Language: pkg.Java, 808 Metadata: pkg.JavaArchive{ 809 PomProperties: &pkg.JavaPomProperties{ 810 GroupID: "com.cloudbees.jenkins.plugins", 811 }, 812 }, 813 }, 814 expected: []string{"some-jenkins-plugin", "some_jenkins_plugin", "jenkins"}, 815 }, 816 { 817 name: "javascript", 818 p: pkg.Package{ 819 Name: "handlebars.js", 820 Type: pkg.NpmPkg, 821 }, 822 expected: []string{"handlebars" /* <-- known good names | default guess --> */, "handlebars.js"}, 823 }, 824 { 825 name: "gem", 826 p: pkg.Package{ 827 Name: "RedCloth", 828 Type: pkg.GemPkg, 829 }, 830 expected: []string{"redcloth_library" /* <-- known good names | default guess --> */, "RedCloth"}, 831 }, 832 { 833 name: "python", 834 p: pkg.Package{ 835 Name: "python-rrdtool", 836 Type: pkg.PythonPkg, 837 }, 838 expected: []string{"rrdtool" /* <-- known good names | default guess --> */, "python-rrdtool", "python_rrdtool"}, 839 }, 840 } 841 842 for _, test := range tests { 843 t.Run(test.name, func(t *testing.T) { 844 assert.ElementsMatch(t, test.expected, candidateProducts(test.p)) 845 }) 846 } 847 } 848 849 func TestCandidateVendor(t *testing.T) { 850 tests := []struct { 851 name string 852 p pkg.Package 853 expected []string 854 }{ 855 { 856 name: "elasticsearch", 857 p: pkg.Package{ 858 Name: "elasticsearch", 859 Type: pkg.JavaPkg, 860 }, 861 expected: []string{"elastic" /* <-- known good names | default guess --> */, "elasticsearch"}, 862 }, 863 { 864 name: "spring-security", 865 p: pkg.Package{ 866 Name: "spring-security-core", 867 Type: pkg.JavaPkg, 868 }, 869 expected: []string{"vmware" /* <-- known good names | default guess --> */, "spring", "spring-security", "spring-security-core", "spring_security_core", "spring_security"}, 870 }, 871 { 872 name: "log4j", 873 p: pkg.Package{ 874 Name: "log4j", 875 Type: pkg.JavaPkg, 876 }, 877 expected: []string{"apache"}, 878 }, 879 { 880 name: "Django", 881 p: pkg.Package{ 882 Name: "Django", 883 Type: pkg.PythonPkg, 884 }, 885 expected: []string{"djangoproject" /* <-- known good names | default guess --> */, "Django"}, 886 }, 887 } 888 889 for _, test := range tests { 890 t.Run(fmt.Sprintf("%+v %+v", test.p, test.expected), func(t *testing.T) { 891 assert.ElementsMatch(t, test.expected, candidateVendors(test.p)) 892 }) 893 } 894 } 895 896 func Test_generateSubSelections(t *testing.T) { 897 tests := []struct { 898 field string 899 expected []string 900 }{ 901 { 902 field: "jenkins", 903 expected: []string{"jenkins"}, 904 }, 905 { 906 field: "jenkins-ci", 907 expected: []string{"jenkins", "jenkins-ci"}, 908 }, 909 { 910 field: "jenkins--ci", 911 expected: []string{"jenkins", "jenkins-ci"}, 912 }, 913 { 914 field: "jenkins_ci_tools", 915 expected: []string{"jenkins", "jenkins_ci", "jenkins_ci_tools"}, 916 }, 917 { 918 field: "-jenkins", 919 expected: []string{"jenkins"}, 920 }, 921 { 922 field: "jenkins_", 923 expected: []string{"jenkins"}, 924 }, 925 { 926 field: "", 927 expected: nil, 928 }, 929 { 930 field: "-", 931 expected: nil, 932 }, 933 { 934 field: "_", 935 expected: nil, 936 }, 937 } 938 for _, test := range tests { 939 t.Run(test.field, func(t *testing.T) { 940 assert.ElementsMatch(t, test.expected, generateSubSelections(test.field)) 941 }) 942 } 943 } 944 945 func Test_addSeparatorVariations(t *testing.T) { 946 tests := []struct { 947 input []string 948 expected []string 949 }{ 950 { 951 input: []string{"jenkins-ci"}, 952 expected: []string{"jenkins-ci", "jenkins_ci"}, //, "jenkinsci"}, 953 }, 954 { 955 input: []string{"jenkins_ci"}, 956 expected: []string{"jenkins_ci", "jenkins-ci"}, //, "jenkinsci"}, 957 }, 958 { 959 input: []string{"jenkins"}, 960 expected: []string{"jenkins"}, 961 }, 962 { 963 input: []string{"jenkins-ci", "circle-ci"}, 964 expected: []string{"jenkins-ci", "jenkins_ci", "circle-ci", "circle_ci"}, //, "jenkinsci", "circleci"}, 965 }, 966 } 967 for _, test := range tests { 968 t.Run(strings.Join(test.input, ","), func(t *testing.T) { 969 val := newFieldCandidateSet(test.input...) 970 addDelimiterVariations(val) 971 assert.ElementsMatch(t, test.expected, val.values()) 972 }) 973 } 974 } 975 976 func TestDictionaryFindIsWired(t *testing.T) { 977 978 tests := []struct { 979 name string 980 pkg pkg.Package 981 want string 982 wantExists bool 983 }{ 984 { 985 name: "sanity check that cpe data is wired up", 986 pkg: pkg.Package{ 987 Name: "openssl", 988 Version: "1.0.2k", 989 Type: pkg.GemPkg, 990 }, 991 want: "cpe:2.3:a:ruby-lang:openssl:1.0.2k:*:*:*:*:*:*:*", 992 // without the cpe data wired up, this would be empty (generation also creates cpe:2.3:a:openssl:openssl:1.0.2k:*:*:*:*:*:*:*) 993 wantExists: true, 994 }, 995 } 996 for _, tt := range tests { 997 t.Run(tt.name, func(t *testing.T) { 998 got, gotExists := DictionaryFind(tt.pkg) 999 1000 assert.Equal(t, tt.want, got.BindToFmtString()) 1001 assert.Equal(t, tt.wantExists, gotExists) 1002 }) 1003 } 1004 }