github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/syft/pkg/cataloger/common/cpe/java_test.go (about) 1 package cpe 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: "from artifactID", 166 pkg: pkg.Package{ 167 Metadata: pkg.JavaArchive{ 168 PomProperties: &pkg.JavaPomProperties{ 169 ArtifactID: "io.jenkins-ci.plugin.thing; version='[2,3)' ; org.something.else", 170 }, 171 }, 172 }, 173 expects: []string{"io.jenkins-ci.plugin.thing"}, 174 }, 175 { 176 name: "from main Extension-Name field", 177 pkg: pkg.Package{ 178 Metadata: pkg.JavaArchive{ 179 Manifest: &pkg.JavaManifest{ 180 Main: map[string]string{ 181 "Extension-Name": "io.jenkins-ci.plugin.thing", 182 }, 183 }, 184 }, 185 }, 186 expects: []string{"io.jenkins-ci.plugin.thing"}, 187 }, 188 { 189 name: "from named section Extension-Name field", 190 pkg: pkg.Package{ 191 Metadata: pkg.JavaArchive{ 192 Manifest: &pkg.JavaManifest{ 193 NamedSections: map[string]map[string]string{ 194 "section": { 195 "Extension-Name": "io.jenkins-ci.plugin.thing", 196 }, 197 }, 198 }, 199 }, 200 }, 201 expects: []string{"io.jenkins-ci.plugin.thing"}, 202 }, 203 { 204 name: "from main field - tier 1", 205 pkg: pkg.Package{ 206 Metadata: pkg.JavaArchive{ 207 Manifest: &pkg.JavaManifest{ 208 Main: map[string]string{ 209 // positive cases 210 // tier 1 211 "Extension-Name": "io.jenkins-ci.plugin.1", 212 "Specification-Vendor": "io.jenkins-ci.plugin.2", 213 "Implementation-Vendor": "io.jenkins-ci.plugin.3", 214 "Bundle-SymbolicName": "io.jenkins-ci.plugin.4", 215 "Implementation-Vendor-Id": "io.jenkins-ci.plugin.5", 216 "Implementation-Title": "io.jenkins-ci.plugin.6", 217 "Bundle-Activator": "io.jenkins-ci.plugin.7", 218 // tier 2 219 "Automatic-Module-Name": "io.jenkins-ci.plugin.8", 220 "Main-Class": "io.jenkins-ci.plugin.9", 221 "Package": "io.jenkins-ci.plugin.10", 222 }, 223 }, 224 }, 225 }, 226 expects: []string{ 227 "io.jenkins-ci.plugin.1", 228 "io.jenkins-ci.plugin.2", 229 "io.jenkins-ci.plugin.3", 230 "io.jenkins-ci.plugin.4", 231 "io.jenkins-ci.plugin.5", 232 "io.jenkins-ci.plugin.6", 233 "io.jenkins-ci.plugin.7", 234 }, 235 }, 236 { 237 name: "from main field - tier 2", 238 pkg: pkg.Package{ 239 Metadata: pkg.JavaArchive{ 240 Manifest: &pkg.JavaManifest{ 241 Main: map[string]string{ 242 // positive cases 243 "Automatic-Module-Name": "io.jenkins-ci.plugin.8", 244 "Main-Class": "io.jenkins-ci.plugin.9", 245 "Package": "io.jenkins-ci.plugin.10", 246 }, 247 }, 248 }, 249 }, 250 expects: []string{ 251 "io.jenkins-ci.plugin.8", 252 "io.jenkins-ci.plugin.9", 253 "io.jenkins-ci.plugin.10", 254 }, 255 }, 256 { 257 name: "from main field - negative cases", 258 pkg: pkg.Package{ 259 Metadata: pkg.JavaArchive{ 260 Manifest: &pkg.JavaManifest{ 261 Main: map[string]string{ 262 // negative cases 263 "Extension-Name": "not.a-group.id", 264 "bogus": "io.jenkins-ci.plugin.please-dont-find-me", 265 }, 266 }, 267 }, 268 }, 269 expects: nil, 270 }, 271 { 272 name: "from named section field - tier 1", 273 pkg: pkg.Package{ 274 Metadata: pkg.JavaArchive{ 275 Manifest: &pkg.JavaManifest{ 276 NamedSections: map[string]map[string]string{ 277 "section": { 278 // positive cases 279 // tier 1 280 "Extension-Name": "io.jenkins-ci.plugin.1", 281 "Specification-Vendor": "io.jenkins-ci.plugin.2", 282 "Implementation-Vendor": "io.jenkins-ci.plugin.3", 283 "Bundle-SymbolicName": "io.jenkins-ci.plugin.4", 284 "Implementation-Vendor-Id": "io.jenkins-ci.plugin.5", 285 "Implementation-Title": "io.jenkins-ci.plugin.6", 286 "Bundle-Activator": "io.jenkins-ci.plugin.7", 287 // tier 2 288 "Automatic-Module-Name": "io.jenkins-ci.plugin.8", 289 "Main-Class": "io.jenkins-ci.plugin.9", 290 "Package": "io.jenkins-ci.plugin.10", 291 }, 292 }, 293 }, 294 }, 295 }, 296 expects: []string{ 297 "io.jenkins-ci.plugin.1", 298 "io.jenkins-ci.plugin.2", 299 "io.jenkins-ci.plugin.3", 300 "io.jenkins-ci.plugin.4", 301 "io.jenkins-ci.plugin.5", 302 "io.jenkins-ci.plugin.6", 303 "io.jenkins-ci.plugin.7", 304 }, 305 }, 306 { 307 name: "from named section field - negative cases", 308 pkg: pkg.Package{ 309 Metadata: pkg.JavaArchive{ 310 Manifest: &pkg.JavaManifest{ 311 NamedSections: map[string]map[string]string{ 312 "section": { 313 // negative cases 314 "Extension-Name": "not.a-group.id", 315 "bogus": "io.jenkins-ci.plugin.please-dont-find-me", 316 }, 317 }, 318 }, 319 }, 320 }, 321 expects: nil, 322 }, 323 { 324 name: "no manifest or pom info", 325 pkg: pkg.Package{ 326 Metadata: pkg.JavaArchive{}, 327 }, 328 expects: nil, 329 }, 330 { 331 name: "no java info", 332 pkg: pkg.Package{}, 333 expects: nil, 334 }, 335 } 336 for _, test := range tests { 337 t.Run(test.name, func(t *testing.T) { 338 assert.ElementsMatch(t, test.expects, GroupIDsFromJavaPackage(test.pkg)) 339 }) 340 } 341 } 342 343 func Test_artifactIDFromJavaPackage(t *testing.T) { 344 tests := []struct { 345 name string 346 pkg pkg.Package 347 expects string 348 }{ 349 { 350 name: "go case", 351 pkg: pkg.Package{ 352 Metadata: pkg.JavaArchive{ 353 PomProperties: &pkg.JavaPomProperties{ 354 ArtifactID: "cloudbees-installation-manager", 355 }, 356 }, 357 }, 358 expects: "cloudbees-installation-manager", 359 }, 360 { 361 name: "ignore groupID-like things", 362 pkg: pkg.Package{ 363 Metadata: pkg.JavaArchive{ 364 PomProperties: &pkg.JavaPomProperties{ 365 ArtifactID: "io.jenkins-ci.plugin.thing", 366 }, 367 }, 368 }, 369 expects: "", 370 }, 371 { 372 name: "no java info", 373 pkg: pkg.Package{}, 374 expects: "", 375 }, 376 } 377 for _, test := range tests { 378 t.Run(test.name, func(t *testing.T) { 379 assert.Equal(t, test.expects, artifactIDFromJavaPackage(test.pkg)) 380 }) 381 } 382 } 383 384 func Test_vendorsFromJavaManifestNames(t *testing.T) { 385 tests := []struct { 386 name string 387 pkg pkg.Package 388 expects []string 389 }{ 390 { 391 name: "from manifest named section fields", 392 pkg: pkg.Package{ 393 Metadata: pkg.JavaArchive{ 394 Manifest: &pkg.JavaManifest{ 395 NamedSections: map[string]map[string]string{ 396 "section": { 397 // positive cases 398 "Specification-Vendor": "Alex Goodman", 399 "Implementation-Vendor": "William Goodman", 400 }, 401 }, 402 }, 403 }, 404 }, 405 expects: []string{"alex_goodman", "william_goodman"}, 406 }, 407 { 408 name: "from manifest named section fields - negative cases", 409 pkg: pkg.Package{ 410 Metadata: pkg.JavaArchive{ 411 Manifest: &pkg.JavaManifest{ 412 NamedSections: map[string]map[string]string{ 413 "section": { 414 // negative cases 415 "Specification-Vendor": "io.jenkins-ci.plugin.thing", 416 "Implementation-Vendor-ID": "William Goodman", 417 }, 418 }, 419 }, 420 }, 421 }, 422 expects: nil, 423 }, 424 } 425 for _, test := range tests { 426 t.Run(test.name, func(t *testing.T) { 427 assert.ElementsMatch(t, test.expects, vendorsFromJavaManifestNames(test.pkg).values()) 428 }) 429 } 430 } 431 432 func Test_groupIDsFromJavaManifest(t *testing.T) { 433 tests := []struct { 434 name string 435 manifest pkg.JavaManifest 436 expected []string 437 }{ 438 { 439 name: "spring-security-core", 440 manifest: pkg.JavaManifest{}, 441 expected: []string{"org.springframework.security"}, 442 }, 443 { 444 name: "spring-web", 445 manifest: pkg.JavaManifest{}, 446 expected: []string{"org.springframework"}, 447 }, 448 { 449 name: "spring-foo", 450 manifest: pkg.JavaManifest{ 451 Main: map[string]string{ 452 "Implementation-Vendor": "org.foo", 453 }, 454 }, 455 expected: []string{"org.foo"}, 456 }, 457 } 458 459 for _, test := range tests { 460 t.Run(test.name, func(t *testing.T) { 461 got := groupIDsFromJavaManifest(test.name, &test.manifest) 462 require.Equal(t, test.expected, got) 463 }) 464 } 465 }