github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/internal/cpegenerate/java_test.go (about) 1 package cpegenerate 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 10 "github.com/anchore/syft/syft/pkg" 11 ) 12 13 func Test_productsFromArtifactAndGroupIDs(t *testing.T) { 14 tests := []struct { 15 groupIDs []string 16 artifactID string 17 expected []string 18 }{ 19 { 20 groupIDs: []string{"org.sonatype.nexus"}, 21 artifactID: "nexus-extender", 22 expected: []string{"nexus", "nexus-extender"}, 23 }, 24 { 25 groupIDs: []string{"org.sonatype.nexus"}, 26 expected: []string{"nexus"}, 27 }, 28 { 29 groupIDs: []string{"org.jenkins-ci.plugins"}, 30 artifactID: "ant", 31 expected: []string{"ant"}, 32 }, 33 { 34 groupIDs: []string{"org.jenkins-ci.plugins"}, 35 artifactID: "antisamy-markup-formatter", 36 expected: []string{"antisamy-markup-formatter"}, 37 }, 38 { 39 groupIDs: []string{"io.jenkins.plugins"}, 40 artifactID: "aws-global-configuration", 41 expected: []string{"aws-global-configuration"}, 42 }, 43 { 44 groupIDs: []string{"com.cloudbees.jenkins.plugins"}, 45 artifactID: "cloudbees-servicenow-jenkins-plugin", 46 expected: []string{"cloudbees-servicenow-jenkins-plugin"}, 47 }, 48 { 49 groupIDs: []string{"com.atlassian.confluence.plugins"}, 50 artifactID: "confluence-mobile-plugin", 51 expected: []string{"confluence-mobile-plugin"}, 52 }, 53 { 54 groupIDs: []string{"com.atlassian.confluence.plugins"}, 55 artifactID: "confluence-view-file-macro", 56 expected: []string{"confluence-view-file-macro"}, 57 }, 58 { 59 groupIDs: []string{"com.google.guava"}, 60 artifactID: "failureaccess", 61 expected: []string{"failureaccess"}, 62 }, 63 } 64 for _, test := range tests { 65 t.Run(strings.Join(test.groupIDs, ",")+":"+test.artifactID, func(t *testing.T) { 66 actual := productsFromArtifactAndGroupIDs(test.artifactID, test.groupIDs) 67 assert.ElementsMatch(t, test.expected, actual, "different products") 68 }) 69 } 70 } 71 72 func Test_candidateProductsForJava(t *testing.T) { 73 tests := []struct { 74 name string 75 pkg pkg.Package 76 expected []string 77 }{ 78 { 79 name: "duplicate groupID in artifactID field", 80 pkg: pkg.Package{ 81 Metadata: pkg.JavaArchive{ 82 PomProperties: &pkg.JavaPomProperties{ 83 GroupID: "org.sonatype.nexus", 84 ArtifactID: "org.sonatype.nexus", 85 }, 86 }, 87 }, 88 expected: []string{"nexus"}, 89 }, 90 { 91 name: "detect groupID-like value in artifactID field", 92 pkg: pkg.Package{ 93 Metadata: pkg.JavaArchive{ 94 PomProperties: &pkg.JavaPomProperties{ 95 ArtifactID: "org.sonatype.nexus", 96 }, 97 }, 98 }, 99 expected: []string{"nexus"}, 100 }, 101 } 102 for _, test := range tests { 103 t.Run(test.name, func(t *testing.T) { 104 actual := candidateProductsForJava(test.pkg) 105 assert.ElementsMatch(t, test.expected, actual, "different products") 106 }) 107 } 108 } 109 110 func Test_vendorsFromGroupIDs(t *testing.T) { 111 tests := []struct { 112 groupID string 113 expected []string 114 }{ 115 { 116 groupID: "org.sonatype.nexus", 117 expected: []string{"sonatype", "nexus"}, 118 }, 119 { 120 groupID: "org.jenkins-ci.plugins", 121 expected: []string{"jenkins-ci"}, 122 }, 123 { 124 groupID: "io.jenkins.plugins", 125 expected: []string{"jenkins"}, 126 }, 127 { 128 groupID: "com.cloudbees.jenkins.plugins", 129 expected: []string{"cloudbees", "jenkins"}, 130 }, 131 { 132 groupID: "com.atlassian.confluence.plugins", 133 expected: []string{"atlassian", "confluence"}, 134 }, 135 { 136 groupID: "com.google.guava", 137 expected: []string{"google", "guava"}, 138 }, 139 } 140 for _, test := range tests { 141 t.Run(test.groupID, func(t *testing.T) { 142 assert.ElementsMatch(t, test.expected, vendorsFromGroupIDs([]string{test.groupID}).values(), "different vendors") 143 }) 144 } 145 } 146 147 func Test_groupIDsFromJavaPackage(t *testing.T) { 148 tests := []struct { 149 name string 150 pkg pkg.Package 151 expects []string 152 }{ 153 { 154 name: "go case", 155 pkg: pkg.Package{ 156 Metadata: pkg.JavaArchive{ 157 PomProperties: &pkg.JavaPomProperties{ 158 GroupID: "io.jenkins-ci.plugin.thing;version='[2,3)'", 159 }, 160 }, 161 }, 162 expects: []string{"io.jenkins-ci.plugin.thing"}, 163 }, 164 { 165 name: "clean # suffixes", 166 pkg: pkg.Package{ 167 Metadata: pkg.JavaArchive{ 168 PomProperties: &pkg.JavaPomProperties{ 169 GroupID: "org.elasticsearch.plugin#parent-join;6.8.15", 170 }, 171 }, 172 }, 173 expects: []string{"org.elasticsearch.plugin"}, 174 }, 175 { 176 name: "from artifactID", 177 pkg: pkg.Package{ 178 Metadata: pkg.JavaArchive{ 179 PomProperties: &pkg.JavaPomProperties{ 180 ArtifactID: "io.jenkins-ci.plugin.thing; version='[2,3)' ; org.something.else", 181 }, 182 }, 183 }, 184 expects: []string{"io.jenkins-ci.plugin.thing"}, 185 }, 186 { 187 name: "from main Extension-Name field", 188 pkg: pkg.Package{ 189 Metadata: pkg.JavaArchive{ 190 Manifest: &pkg.JavaManifest{ 191 Main: pkg.KeyValues{ 192 { 193 Key: "Extension-Name", 194 Value: "io.jenkins-ci.plugin.thing", 195 }, 196 }, 197 }, 198 }, 199 }, 200 expects: []string{"io.jenkins-ci.plugin.thing"}, 201 }, 202 { 203 name: "from named section Extension-Name field", 204 pkg: pkg.Package{ 205 Metadata: pkg.JavaArchive{ 206 Manifest: &pkg.JavaManifest{ 207 Sections: []pkg.KeyValues{ 208 { 209 { 210 Key: "Name", 211 Value: "section", 212 }, 213 { 214 Key: "Extension-Name", 215 Value: "io.jenkins-ci.plugin.thing", 216 }, 217 }, 218 }, 219 }, 220 }, 221 }, 222 expects: []string{"io.jenkins-ci.plugin.thing"}, 223 }, 224 { 225 name: "from main field - tier 1", 226 pkg: pkg.Package{ 227 Metadata: pkg.JavaArchive{ 228 Manifest: &pkg.JavaManifest{ 229 Main: []pkg.KeyValue{ 230 // positive cases 231 // tier 1 232 {Key: "Extension-Name", Value: "io.jenkins-ci.plugin.1"}, 233 {Key: "Specification-Vendor", Value: "io.jenkins-ci.plugin.2"}, 234 {Key: "Implementation-Vendor", Value: "io.jenkins-ci.plugin.3"}, 235 {Key: "Bundle-SymbolicName", Value: "io.jenkins-ci.plugin.4"}, 236 {Key: "Implementation-Vendor-Id", Value: "io.jenkins-ci.plugin.5"}, 237 {Key: "Implementation-Title", Value: "io.jenkins-ci.plugin.6"}, 238 {Key: "Bundle-Activator", Value: "io.jenkins-ci.plugin.7"}, 239 // tier 2 240 {Key: "Automatic-Module-Name", Value: "io.jenkins-ci.plugin.8"}, 241 {Key: "Main-Class", Value: "io.jenkins-ci.plugin.9"}, 242 {Key: "Package", Value: "io.jenkins-ci.plugin.10"}, 243 }, 244 }, 245 }, 246 }, 247 expects: []string{ 248 "io.jenkins-ci.plugin.1", 249 "io.jenkins-ci.plugin.2", 250 "io.jenkins-ci.plugin.3", 251 "io.jenkins-ci.plugin.4", 252 "io.jenkins-ci.plugin.5", 253 "io.jenkins-ci.plugin.6", 254 "io.jenkins-ci.plugin.7", 255 }, 256 }, 257 { 258 name: "from main field - tier 2", 259 pkg: pkg.Package{ 260 Metadata: pkg.JavaArchive{ 261 Manifest: &pkg.JavaManifest{ 262 Main: []pkg.KeyValue{ 263 // positive cases 264 {Key: "Automatic-Module-Name", Value: "io.jenkins-ci.plugin.8"}, 265 {Key: "Main-Class", Value: "io.jenkins-ci.plugin.9"}, 266 {Key: "Package", Value: "io.jenkins-ci.plugin.10"}, 267 }, 268 }, 269 }, 270 }, 271 expects: []string{ 272 "io.jenkins-ci.plugin.8", 273 "io.jenkins-ci.plugin.9", 274 "io.jenkins-ci.plugin.10", 275 }, 276 }, 277 { 278 name: "from main field - negative cases", 279 pkg: pkg.Package{ 280 Metadata: pkg.JavaArchive{ 281 Manifest: &pkg.JavaManifest{ 282 Main: []pkg.KeyValue{ 283 // negative cases 284 {Key: "Extension-Name", Value: "not.a-group.id"}, 285 {Key: "bogus", Value: "io.jenkins-ci.plugin.please-dont-find-me"}, 286 }, 287 }, 288 }, 289 }, 290 expects: nil, 291 }, 292 { 293 name: "from named section field - tier 1", 294 pkg: pkg.Package{ 295 Metadata: pkg.JavaArchive{ 296 Manifest: &pkg.JavaManifest{ 297 Sections: []pkg.KeyValues{ 298 { 299 { 300 Key: "Name", 301 Value: "section", 302 }, 303 // positive cases 304 // tier 1 305 { 306 Key: "Extension-Name", 307 Value: "io.jenkins-ci.plugin.1", 308 }, 309 { 310 Key: "Specification-Vendor", 311 Value: "io.jenkins-ci.plugin.2", 312 }, 313 { 314 Key: "Implementation-Vendor", 315 Value: "io.jenkins-ci.plugin.3", 316 }, 317 { 318 Key: "Bundle-SymbolicName", 319 Value: "io.jenkins-ci.plugin.4", 320 }, 321 { 322 Key: "Implementation-Vendor-Id", 323 Value: "io.jenkins-ci.plugin.5", 324 }, 325 { 326 Key: "Implementation-Title", 327 Value: "io.jenkins-ci.plugin.6", 328 }, 329 { 330 Key: "Bundle-Activator", 331 Value: "io.jenkins-ci.plugin.7", 332 }, 333 // tier 2 334 { 335 Key: "Automatic-Module-Name", 336 Value: "io.jenkins-ci.plugin.8", 337 }, 338 { 339 Key: "Main-Class", 340 Value: "io.jenkins-ci.plugin.9", 341 }, 342 { 343 Key: "Package", 344 Value: "io.jenkins-ci.plugin.10", 345 }, 346 }, 347 }, 348 }, 349 }, 350 }, 351 expects: []string{ 352 "io.jenkins-ci.plugin.1", 353 "io.jenkins-ci.plugin.2", 354 "io.jenkins-ci.plugin.3", 355 "io.jenkins-ci.plugin.4", 356 "io.jenkins-ci.plugin.5", 357 "io.jenkins-ci.plugin.6", 358 "io.jenkins-ci.plugin.7", 359 }, 360 }, 361 { 362 name: "from named section field - negative cases", 363 pkg: pkg.Package{ 364 Metadata: pkg.JavaArchive{ 365 Manifest: &pkg.JavaManifest{ 366 Sections: []pkg.KeyValues{ 367 { 368 { 369 Key: "Name", 370 Value: "section", 371 }, 372 { 373 Key: "Extension-Name", 374 Value: "not.a-group.id", 375 }, 376 { 377 Key: "bogus", 378 Value: "io.jenkins-ci.plugin.please-dont-find-me", 379 }, 380 }, 381 }, 382 }, 383 }, 384 }, 385 expects: nil, 386 }, 387 { 388 name: "no manifest or pom info", 389 pkg: pkg.Package{ 390 Metadata: pkg.JavaArchive{}, 391 }, 392 expects: nil, 393 }, 394 { 395 name: "no java info", 396 pkg: pkg.Package{}, 397 expects: nil, 398 }, 399 } 400 for _, test := range tests { 401 t.Run(test.name, func(t *testing.T) { 402 assert.ElementsMatch(t, test.expects, GroupIDsFromJavaPackage(test.pkg)) 403 }) 404 } 405 } 406 407 func Test_artifactIDFromJavaPackage(t *testing.T) { 408 tests := []struct { 409 name string 410 pkg pkg.Package 411 expects string 412 }{ 413 { 414 name: "go case", 415 pkg: pkg.Package{ 416 Metadata: pkg.JavaArchive{ 417 PomProperties: &pkg.JavaPomProperties{ 418 ArtifactID: "cloudbees-installation-manager", 419 }, 420 }, 421 }, 422 expects: "cloudbees-installation-manager", 423 }, 424 { 425 name: "ignore groupID-like things", 426 pkg: pkg.Package{ 427 Metadata: pkg.JavaArchive{ 428 PomProperties: &pkg.JavaPomProperties{ 429 ArtifactID: "io.jenkins-ci.plugin.thing", 430 }, 431 }, 432 }, 433 expects: "", 434 }, 435 { 436 name: "no java info", 437 pkg: pkg.Package{}, 438 expects: "", 439 }, 440 } 441 for _, test := range tests { 442 t.Run(test.name, func(t *testing.T) { 443 assert.Equal(t, test.expects, artifactIDFromJavaPackage(test.pkg)) 444 }) 445 } 446 } 447 448 func Test_vendorsFromJavaManifestNames(t *testing.T) { 449 tests := []struct { 450 name string 451 pkg pkg.Package 452 expects []string 453 }{ 454 { 455 name: "from manifest named section fields", 456 pkg: pkg.Package{ 457 Metadata: pkg.JavaArchive{ 458 Manifest: &pkg.JavaManifest{ 459 Sections: []pkg.KeyValues{ 460 { 461 { 462 Key: "Name", 463 Value: "section", 464 }, 465 // positive cases 466 { 467 Key: "Specification-Vendor", 468 Value: "Alex Goodman", 469 }, 470 { 471 Key: "Implementation-Vendor", 472 Value: "William Goodman", 473 }, 474 }, 475 }, 476 }, 477 }, 478 }, 479 expects: []string{"alex_goodman", "william_goodman"}, 480 }, 481 { 482 name: "from manifest named section fields - negative cases", 483 pkg: pkg.Package{ 484 Metadata: pkg.JavaArchive{ 485 Manifest: &pkg.JavaManifest{ 486 Sections: []pkg.KeyValues{ 487 { 488 { 489 Key: "Name", 490 Value: "section", 491 }, 492 // negative cases 493 494 { 495 Key: "Specification-Vendor", 496 Value: "io.jenkins-ci.plugin.thing", 497 }, 498 { 499 Key: "Implementation-Vendor-ID", 500 Value: "William Goodman", 501 }, 502 }, 503 }, 504 }, 505 }, 506 }, 507 expects: nil, 508 }, 509 } 510 for _, test := range tests { 511 t.Run(test.name, func(t *testing.T) { 512 assert.ElementsMatch(t, test.expects, vendorsFromJavaManifestNames(test.pkg).values()) 513 }) 514 } 515 } 516 517 func Test_groupIDsFromJavaManifest(t *testing.T) { 518 tests := []struct { 519 name string 520 manifest pkg.JavaManifest 521 expected []string 522 }{ 523 { 524 name: "spring-security-core", 525 manifest: pkg.JavaManifest{}, 526 expected: []string{"org.springframework.security"}, 527 }, 528 { 529 name: "spring-web", 530 manifest: pkg.JavaManifest{}, 531 expected: []string{"org.springframework"}, 532 }, 533 { 534 name: "spring-foo", 535 manifest: pkg.JavaManifest{ 536 Main: []pkg.KeyValue{ 537 { 538 Key: "Implementation-Vendor", 539 Value: "org.foo", 540 }, 541 }, 542 }, 543 expected: []string{"org.foo"}, 544 }, 545 } 546 547 for _, test := range tests { 548 t.Run(test.name, func(t *testing.T) { 549 got := groupIDsFromJavaManifest(test.name, &test.manifest) 550 require.Equal(t, test.expected, got) 551 }) 552 } 553 }