github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/binary/cataloger_test.go (about) 1 package binary 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "strings" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 "github.com/google/go-cmp/cmp/cmpopts" 12 "github.com/nextlinux/gosbom/gosbom/file" 13 "github.com/nextlinux/gosbom/gosbom/pkg" 14 "github.com/nextlinux/gosbom/gosbom/source" 15 "github.com/stretchr/testify/assert" 16 "github.com/stretchr/testify/require" 17 18 "github.com/anchore/stereoscope/pkg/imagetest" 19 ) 20 21 func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { 22 tests := []struct { 23 name string 24 fixtureDir string 25 expected pkg.Package 26 }{ 27 { 28 name: "positive-postgresql-15beta4", 29 fixtureDir: "test-fixtures/classifiers/positive/postgresql-15beta4", 30 expected: pkg.Package{ 31 Name: "postgresql", 32 Version: "15beta4", 33 Type: "binary", 34 PURL: "pkg:generic/postgresql@15beta4", 35 Locations: locations("postgres"), 36 Metadata: metadata("postgresql-binary"), 37 }, 38 }, 39 { 40 name: "positive-postgresql-15.1", 41 fixtureDir: "test-fixtures/classifiers/positive/postgresql-15.1", 42 expected: pkg.Package{ 43 Name: "postgresql", 44 Version: "15.1", 45 Type: "binary", 46 PURL: "pkg:generic/postgresql@15.1", 47 Locations: locations("postgres"), 48 Metadata: metadata("postgresql-binary"), 49 }, 50 }, 51 { 52 name: "positive-postgresql-9.6.24", 53 fixtureDir: "test-fixtures/classifiers/positive/postgresql-9.6.24", 54 expected: pkg.Package{ 55 Name: "postgresql", 56 Version: "9.6.24", 57 Type: "binary", 58 PURL: "pkg:generic/postgresql@9.6.24", 59 Locations: locations("postgres"), 60 Metadata: metadata("postgresql-binary"), 61 }, 62 }, 63 { 64 name: "positive-postgresql-9.5alpha1", 65 fixtureDir: "test-fixtures/classifiers/positive/postgresql-9.5alpha1", 66 expected: pkg.Package{ 67 Name: "postgresql", 68 Version: "9.5alpha1", 69 Type: "binary", 70 PURL: "pkg:generic/postgresql@9.5alpha1", 71 Locations: locations("postgres"), 72 Metadata: metadata("postgresql-binary"), 73 }, 74 }, 75 { 76 name: "positive-traefik-2.9.6", 77 fixtureDir: "test-fixtures/classifiers/positive/traefik-2.9.6", 78 expected: pkg.Package{ 79 Name: "traefik", 80 Version: "2.9.6", 81 Type: "binary", 82 PURL: "pkg:generic/traefik@2.9.6", 83 Locations: locations("traefik"), 84 Metadata: metadata("traefik-binary"), 85 }, 86 }, 87 { 88 name: "positive-traefik-1.7.34", 89 fixtureDir: "test-fixtures/classifiers/positive/traefik-1.7.34", 90 expected: pkg.Package{ 91 Name: "traefik", 92 Version: "1.7.34", 93 Type: "binary", 94 PURL: "pkg:generic/traefik@1.7.34", 95 Locations: locations("traefik"), 96 Metadata: metadata("traefik-binary"), 97 }, 98 }, 99 { 100 name: "positive-memcached-1.6.18", 101 fixtureDir: "test-fixtures/classifiers/positive/memcached-1.6.18", 102 expected: pkg.Package{ 103 Name: "memcached", 104 Version: "1.6.18", 105 Type: "binary", 106 PURL: "pkg:generic/memcached@1.6.18", 107 Locations: locations("memcached"), 108 Metadata: metadata("memcached-binary"), 109 }, 110 }, 111 { 112 name: "positive-httpd-2.4.54", 113 fixtureDir: "test-fixtures/classifiers/positive/httpd-2.4.54", 114 expected: pkg.Package{ 115 Name: "httpd", 116 Version: "2.4.54", 117 Type: "binary", 118 PURL: "pkg:generic/httpd@2.4.54", 119 Locations: locations("httpd"), 120 Metadata: metadata("httpd-binary"), 121 }, 122 }, 123 { 124 name: "positive-php-cli-8.2.1", 125 fixtureDir: "test-fixtures/classifiers/positive/php-cli-8.2.1", 126 expected: pkg.Package{ 127 Name: "php-cli", 128 Version: "8.2.1", 129 Type: "binary", 130 PURL: "pkg:generic/php-cli@8.2.1", 131 Locations: locations("php"), 132 Metadata: metadata("php-cli-binary"), 133 }, 134 }, 135 { 136 name: "positive-php-fpm-8.2.1", 137 fixtureDir: "test-fixtures/classifiers/positive/php-fpm-8.2.1", 138 expected: pkg.Package{ 139 Name: "php-fpm", 140 Version: "8.2.1", 141 Type: "binary", 142 PURL: "pkg:generic/php-fpm@8.2.1", 143 Locations: locations("php-fpm"), 144 Metadata: metadata("php-fpm-binary"), 145 }, 146 }, 147 { 148 name: "positive-php-apache-8.2.1", 149 fixtureDir: "test-fixtures/classifiers/positive/php-apache-8.2.1", 150 expected: pkg.Package{ 151 Name: "libphp", 152 Version: "8.2.1", 153 Type: "binary", 154 PURL: "pkg:generic/php@8.2.1", 155 Locations: locations("libphp.so"), 156 Metadata: metadata("php-apache-binary"), 157 }, 158 }, 159 { 160 name: "positive-perl-5.12.5", 161 fixtureDir: "test-fixtures/classifiers/positive/perl-5.12.5", 162 expected: pkg.Package{ 163 Name: "perl", 164 Version: "5.12.5", 165 Type: "binary", 166 PURL: "pkg:generic/perl@5.12.5", 167 Locations: locations("perl"), 168 Metadata: metadata("perl-binary"), 169 }, 170 }, 171 { 172 name: "positive-perl-5.20.0", 173 fixtureDir: "test-fixtures/classifiers/positive/perl-5.20.0", 174 expected: pkg.Package{ 175 Name: "perl", 176 Version: "5.20.0", 177 Type: "binary", 178 PURL: "pkg:generic/perl@5.20.0", 179 Locations: locations("perl"), 180 Metadata: metadata("perl-binary"), 181 }, 182 }, 183 { 184 name: "positive-perl-5.37.8", 185 fixtureDir: "test-fixtures/classifiers/positive/perl-5.37.8", 186 expected: pkg.Package{ 187 Name: "perl", 188 Version: "5.37.8", 189 Type: "binary", 190 PURL: "pkg:generic/perl@5.37.8", 191 Locations: locations("perl"), 192 Metadata: metadata("perl-binary"), 193 }, 194 }, 195 { 196 name: "positive-haproxy-1.5.14", 197 fixtureDir: "test-fixtures/classifiers/positive/haproxy-1.5.14", 198 expected: pkg.Package{ 199 Name: "haproxy", 200 Version: "1.5.14", 201 Type: "binary", 202 PURL: "pkg:generic/haproxy@1.5.14", 203 Locations: locations("haproxy"), 204 Metadata: metadata("haproxy-binary"), 205 }, 206 }, 207 { 208 name: "positive-haproxy-1.8.22", 209 fixtureDir: "test-fixtures/classifiers/positive/haproxy-1.8.22", 210 expected: pkg.Package{ 211 Name: "haproxy", 212 Version: "1.8.22", 213 Type: "binary", 214 PURL: "pkg:generic/haproxy@1.8.22", 215 Locations: locations("haproxy"), 216 Metadata: metadata("haproxy-binary"), 217 }, 218 }, 219 { 220 name: "positive-haproxy-2.7.3", 221 fixtureDir: "test-fixtures/classifiers/positive/haproxy-2.7.3", 222 expected: pkg.Package{ 223 Name: "haproxy", 224 Version: "2.7.3", 225 Type: "binary", 226 PURL: "pkg:generic/haproxy@2.7.3", 227 Locations: locations("haproxy"), 228 Metadata: metadata("haproxy-binary"), 229 }, 230 }, 231 { 232 name: "positive-redis-2.8.23", 233 fixtureDir: "test-fixtures/classifiers/positive/redis-server-2.8.23", 234 expected: pkg.Package{ 235 Name: "redis", 236 Version: "2.8.23", 237 Type: "binary", 238 PURL: "pkg:generic/redis@2.8.23", 239 Locations: locations("redis-server"), 240 Metadata: metadata("redis-binary"), 241 }, 242 }, 243 { 244 name: "positive-helm-3.11.1", 245 fixtureDir: "test-fixtures/classifiers/dynamic/helm-3.11.1", 246 expected: pkg.Package{ 247 Name: "helm", 248 Version: "3.11.1", 249 Type: "binary", 250 PURL: "pkg:golang/helm.sh/helm@3.11.1", 251 Locations: locations("helm"), 252 Metadata: metadata("helm"), 253 }, 254 }, 255 { 256 name: "positive-helm-3.10.3", 257 fixtureDir: "test-fixtures/classifiers/dynamic/helm-3.10.3", 258 expected: pkg.Package{ 259 Name: "helm", 260 Version: "3.10.3", 261 Type: "binary", 262 PURL: "pkg:golang/helm.sh/helm@3.10.3", 263 Locations: locations("helm"), 264 Metadata: metadata("helm"), 265 }, 266 }, 267 { 268 name: "positive-redis-4.0.11", 269 fixtureDir: "test-fixtures/classifiers/positive/redis-server-4.0.11", 270 expected: pkg.Package{ 271 Name: "redis", 272 Version: "4.0.11", 273 Type: "binary", 274 PURL: "pkg:generic/redis@4.0.11", 275 Locations: locations("redis-server"), 276 Metadata: metadata("redis-binary"), 277 }, 278 }, 279 { 280 name: "positive-redis-5.0.0", 281 fixtureDir: "test-fixtures/classifiers/positive/redis-server-5.0.0", 282 expected: pkg.Package{ 283 Name: "redis", 284 Version: "5.0.0", 285 Type: "binary", 286 PURL: "pkg:generic/redis@5.0.0", 287 Locations: locations("redis-server"), 288 Metadata: metadata("redis-binary"), 289 }, 290 }, 291 { 292 name: "positive-redis-6.0.16", 293 fixtureDir: "test-fixtures/classifiers/positive/redis-server-6.0.16", 294 expected: pkg.Package{ 295 Name: "redis", 296 Version: "6.0.16", 297 Type: "binary", 298 PURL: "pkg:generic/redis@6.0.16", 299 Locations: locations("redis-server"), 300 Metadata: metadata("redis-binary"), 301 }, 302 }, 303 { 304 name: "positive-redis-7.0.0", 305 fixtureDir: "test-fixtures/classifiers/positive/redis-server-7.0.0", 306 expected: pkg.Package{ 307 Name: "redis", 308 Version: "7.0.0", 309 Type: "binary", 310 PURL: "pkg:generic/redis@7.0.0", 311 Locations: locations("redis-server"), 312 Metadata: metadata("redis-binary"), 313 }, 314 }, 315 { 316 name: "positive-libpython3.7.so", 317 fixtureDir: "test-fixtures/classifiers/positive/python-binary-lib-3.7", 318 expected: pkg.Package{ 319 Name: "python", 320 Version: "3.7.4", 321 PURL: "pkg:generic/python@3.7.4", 322 Locations: locations("libpython3.7.so"), 323 Metadata: metadata("python-binary-lib"), 324 }, 325 }, 326 { 327 name: "positive-python-3.11.2-from-shared-lib", 328 fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-shared-lib-3.11", 329 expected: pkg.Package{ 330 Name: "python", 331 Version: "3.11.2", 332 PURL: "pkg:generic/python@3.11.2", 333 Locations: locations("python3", "libpython3.11.so.1.0"), 334 Metadata: pkg.BinaryMetadata{ 335 Matches: []pkg.ClassifierMatch{ 336 match("python-binary", "python3"), 337 match("python-binary", "libpython3.11.so.1.0"), 338 match("python-binary-lib", "libpython3.11.so.1.0"), 339 }, 340 }, 341 }, 342 }, 343 { 344 name: "positive-python-3.9-from-shared-redhat-lib", 345 fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-shared-lib-redhat-3.9", 346 expected: pkg.Package{ 347 Name: "python", 348 Version: "3.9.13", 349 PURL: "pkg:generic/python@3.9.13", 350 Locations: locations("python3.9", "libpython3.9.so.1.0"), 351 Metadata: pkg.BinaryMetadata{ 352 Matches: []pkg.ClassifierMatch{ 353 match("python-binary", "python3.9"), 354 match("python-binary", "libpython3.9.so.1.0"), 355 match("python-binary-lib", "libpython3.9.so.1.0"), 356 }, 357 }, 358 }, 359 }, 360 { 361 name: "positive-python-binary-with-version-3.9", 362 fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-with-version-3.9", 363 expected: pkg.Package{ 364 Name: "python", 365 Version: "3.9.2", 366 PURL: "pkg:generic/python@3.9.2", 367 Locations: locations("python3.9"), 368 Metadata: pkg.BinaryMetadata{ 369 Matches: []pkg.ClassifierMatch{ 370 match("python-binary", "python3.9"), 371 }, 372 }, 373 }, 374 }, 375 { 376 name: "positive-python-binary-3.4-alpine", 377 fixtureDir: "test-fixtures/classifiers/dynamic/python-binary-3.4-alpine", 378 expected: pkg.Package{ 379 Name: "python", 380 Version: "3.4.10", 381 PURL: "pkg:generic/python@3.4.10", 382 Locations: locations("python3.4", "libpython3.4m.so.1.0"), 383 Metadata: pkg.BinaryMetadata{ 384 Matches: []pkg.ClassifierMatch{ 385 match("python-binary", "python3.4"), 386 match("python-binary", "libpython3.4m.so.1.0"), 387 match("python-binary-lib", "libpython3.4m.so.1.0"), 388 }, 389 }, 390 }, 391 }, 392 { 393 name: "positive-python-3.5-with-incorrect-match", 394 fixtureDir: "test-fixtures/classifiers/positive/python-3.5-with-incorrect-match", 395 expected: pkg.Package{ 396 Name: "python", 397 Version: "3.5.3", 398 PURL: "pkg:generic/python@3.5.3", 399 Locations: locations("python3.5"), 400 Metadata: metadata("python-binary"), 401 }, 402 }, 403 { 404 name: "positive-python3.6", 405 fixtureDir: "test-fixtures/classifiers/positive/python-binary-3.6", 406 expected: pkg.Package{ 407 Name: "python", 408 Version: "3.6.3", 409 PURL: "pkg:generic/python@3.6.3", 410 Locations: locations("python3.6"), 411 Metadata: metadata("python-binary"), 412 }, 413 }, 414 { 415 name: "positive-python-duplicates", 416 fixtureDir: "test-fixtures/classifiers/positive/python-duplicates", 417 expected: pkg.Package{ 418 Name: "python", 419 Version: "3.8.16", 420 Type: "binary", 421 PURL: "pkg:generic/python@3.8.16", 422 Locations: locations("dir/python3.8", "python3.8", "libpython3.8.so"), 423 Metadata: pkg.BinaryMetadata{ 424 Matches: []pkg.ClassifierMatch{ 425 match("python-binary", "dir/python3.8"), 426 match("python-binary", "python3.8"), 427 match("python-binary-lib", "libpython3.8.so"), 428 }, 429 }, 430 }, 431 }, 432 { 433 name: "positive-go", 434 fixtureDir: "test-fixtures/classifiers/positive/go-1.14", 435 expected: pkg.Package{ 436 Name: "go", 437 Version: "1.14", 438 PURL: "pkg:generic/go@1.14", 439 Locations: locations("go"), 440 Metadata: metadata("go-binary"), 441 }, 442 }, 443 { 444 name: "positive-node", 445 fixtureDir: "test-fixtures/classifiers/positive/node-19.2.1", 446 expected: pkg.Package{ 447 Name: "node", 448 Version: "19.2.1", 449 PURL: "pkg:generic/node@19.2.1", 450 Locations: locations("node"), 451 Metadata: metadata("nodejs-binary"), 452 }, 453 }, 454 { 455 name: "positive-go-hint", 456 fixtureDir: "test-fixtures/classifiers/positive/go-hint-1.15", 457 expected: pkg.Package{ 458 Name: "go", 459 Version: "1.15", 460 PURL: "pkg:generic/go@1.15", 461 Locations: locations("VERSION"), 462 Metadata: metadata("go-binary-hint"), 463 }, 464 }, 465 { 466 name: "positive-busybox", 467 fixtureDir: "test-fixtures/classifiers/positive/busybox-3.33.3", 468 expected: pkg.Package{ 469 Name: "busybox", 470 Version: "3.33.3", 471 Locations: locations("["), // note: busybox is a link to [ 472 Metadata: metadata("busybox-binary", "[", "busybox"), 473 }, 474 }, 475 { 476 name: "positive-java-openjdk", 477 fixtureDir: "test-fixtures/classifiers/positive/openjdk", 478 expected: pkg.Package{ 479 Name: "java", 480 Version: "1.8.0_352-b08", 481 Type: "binary", 482 PURL: "pkg:generic/java@1.8.0_352-b08", 483 Locations: locations("java"), 484 Metadata: metadata("java-binary-openjdk", "java"), 485 }, 486 }, 487 { 488 name: "positive-java-openjdk-lts", 489 fixtureDir: "test-fixtures/classifiers/positive/openjdk-lts", 490 expected: pkg.Package{ 491 Name: "java", 492 Version: "11.0.17+8-LTS", 493 Type: "binary", 494 PURL: "pkg:generic/java@11.0.17+8-LTS", 495 Locations: locations("java"), 496 Metadata: metadata("java-binary-openjdk", "java"), 497 }, 498 }, 499 { 500 name: "positive-java-oracle", 501 fixtureDir: "test-fixtures/classifiers/positive/oracle", 502 expected: pkg.Package{ 503 Name: "java", 504 Version: "19.0.1+10-21", 505 Type: "binary", 506 PURL: "pkg:generic/java@19.0.1+10-21", 507 Locations: locations("java"), 508 Metadata: metadata("java-binary-oracle", "java"), 509 }, 510 }, 511 { 512 name: "positive-java-oracle-macos", 513 fixtureDir: "test-fixtures/classifiers/positive/oracle-macos", 514 expected: pkg.Package{ 515 Name: "java", 516 Version: "19.0.1+10-21", 517 Type: "binary", 518 PURL: "pkg:generic/java@19.0.1+10-21", 519 Locations: locations("java"), 520 Metadata: metadata("java-binary-oracle", "java"), 521 }, 522 }, 523 { 524 name: "positive-java-ibm", 525 fixtureDir: "test-fixtures/classifiers/positive/ibm", 526 expected: pkg.Package{ 527 Name: "java", 528 Version: "1.8.0-foreman_2022_09_22_15_30-b00", 529 Type: "binary", 530 PURL: "pkg:generic/java@1.8.0-foreman_2022_09_22_15_30-b00", 531 Locations: locations("java"), 532 Metadata: metadata("java-binary-ibm", "java"), 533 }, 534 }, 535 { 536 name: "positive-rust-1.50.0-macos", 537 fixtureDir: "test-fixtures/classifiers/positive/rust-1.50.0", 538 expected: pkg.Package{ 539 Name: "rust", 540 Version: "1.50.0", 541 Type: "binary", 542 PURL: "pkg:generic/rust@1.50.0", 543 Locations: locations("lib/rustlib/aarch64-apple-darwin/lib/libstd-f6f9eec1635e636a.dylib"), 544 Metadata: metadata("rust-standard-library-macos"), 545 }, 546 }, 547 { 548 name: "positive-rust-1.67.1-macos", 549 fixtureDir: "test-fixtures/classifiers/positive/rust-1.67.1/toolchains/stable-aarch64-apple-darwin", 550 expected: pkg.Package{ 551 Name: "rust", 552 Version: "1.67.1", 553 Type: "binary", 554 PURL: "pkg:generic/rust@1.67.1", 555 Locations: locations("lib/libstd-16f2b65e77054c42.dylib"), 556 Metadata: metadata("rust-standard-library-macos"), 557 }, 558 }, 559 { 560 name: "positive-rust-1.67.1-linux", 561 fixtureDir: "test-fixtures/classifiers/positive/rust-1.67.1/toolchains/stable-x86_64-unknown-linux-musl", 562 expected: pkg.Package{ 563 Name: "rust", 564 Version: "1.67.1", 565 Type: "binary", 566 PURL: "pkg:generic/rust@1.67.1", 567 Locations: locations("lib/libstd-86aefecbddda356d.so"), 568 Metadata: metadata("rust-standard-library-linux"), 569 }, 570 }, 571 { 572 name: "positive-ruby-3.2.1", 573 fixtureDir: "test-fixtures/classifiers/dynamic/ruby-library-3.2.1", 574 expected: pkg.Package{ 575 Name: "ruby", 576 Version: "3.2.1", 577 Type: "binary", 578 PURL: "pkg:generic/ruby@3.2.1", 579 Locations: locations("ruby", "libruby.so.3.2.1"), 580 Metadata: pkg.BinaryMetadata{ 581 Matches: []pkg.ClassifierMatch{ 582 match("ruby-binary", "ruby"), 583 match("ruby-binary", "libruby.so.3.2.1"), 584 }, 585 }, 586 }, 587 }, 588 { 589 name: "positive-ruby-2.7.7", 590 fixtureDir: "test-fixtures/classifiers/dynamic/ruby-library-2.7.7", 591 expected: pkg.Package{ 592 Name: "ruby", 593 Version: "2.7.7p221", 594 Type: "binary", 595 PURL: "pkg:generic/ruby@2.7.7p221", 596 Locations: locations("ruby", "libruby.so.2.7.7"), 597 Metadata: pkg.BinaryMetadata{ 598 Matches: []pkg.ClassifierMatch{ 599 match("ruby-binary", "ruby"), 600 match("ruby-binary", "libruby.so.2.7.7"), 601 }, 602 }, 603 }, 604 }, 605 { 606 name: "positive-ruby-2.6.10", 607 fixtureDir: "test-fixtures/classifiers/dynamic/ruby-library-2.6.10", 608 expected: pkg.Package{ 609 Name: "ruby", 610 Version: "2.6.10p210", 611 Type: "binary", 612 PURL: "pkg:generic/ruby@2.6.10p210", 613 Locations: locations("ruby", "libruby.so.2.6.10"), 614 Metadata: pkg.BinaryMetadata{ 615 Matches: []pkg.ClassifierMatch{ 616 match("ruby-binary", "ruby"), 617 match("ruby-binary", "libruby.so.2.6.10"), 618 }, 619 }, 620 }, 621 }, 622 { 623 name: "positive-ruby-1.9.3p551", 624 fixtureDir: "test-fixtures/classifiers/positive/ruby-1.9.3p551", 625 expected: pkg.Package{ 626 Name: "ruby", 627 Version: "1.9.3p551", 628 Type: "binary", 629 PURL: "pkg:generic/ruby@1.9.3p551", 630 Locations: locations("ruby"), 631 Metadata: metadata("ruby-binary"), 632 }, 633 }, 634 { 635 name: "positive-consul-1.15.2", 636 fixtureDir: "test-fixtures/classifiers/dynamic/consul-1.15.2", 637 expected: pkg.Package{ 638 Name: "consul", 639 Version: "1.15.2", 640 Type: "binary", 641 PURL: "pkg:golang/github.com/hashicorp/consul@1.15.2", 642 Locations: locations("consul"), 643 Metadata: metadata("consul-binary"), 644 }, 645 }, 646 } 647 648 for _, test := range tests { 649 t.Run(test.name, func(t *testing.T) { 650 c := NewCataloger() 651 652 src, err := source.NewFromDirectory(test.fixtureDir) 653 require.NoError(t, err) 654 655 resolver, err := src.FileResolver(source.SquashedScope) 656 require.NoError(t, err) 657 658 packages, _, err := c.Catalog(resolver) 659 require.NoError(t, err) 660 661 require.Len(t, packages, 1) 662 663 assertPackagesAreEqual(t, test.expected, packages[0]) 664 }) 665 } 666 } 667 668 func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) { 669 tests := []struct { 670 name string 671 fixtureImage string 672 expected pkg.Package 673 }{ 674 { 675 name: "busybox-regression", 676 fixtureImage: "image-busybox", 677 expected: pkg.Package{ 678 Name: "busybox", 679 Version: "1.35.0", 680 Locations: locations("/bin/["), 681 Metadata: metadata("busybox-binary", "/bin/[", "/bin/busybox"), 682 }, 683 }, 684 } 685 686 for _, test := range tests { 687 t.Run(test.name, func(t *testing.T) { 688 c := NewCataloger() 689 690 img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage) 691 src, err := source.NewFromImage(img, "test-img") 692 require.NoError(t, err) 693 694 resolver, err := src.FileResolver(source.SquashedScope) 695 require.NoError(t, err) 696 697 packages, _, err := c.Catalog(resolver) 698 require.NoError(t, err) 699 700 for _, p := range packages { 701 expectedLocations := test.expected.Locations.ToSlice() 702 gotLocations := p.Locations.ToSlice() 703 require.Len(t, gotLocations, len(expectedLocations)) 704 705 for i, expectedLocation := range expectedLocations { 706 gotLocation := gotLocations[i] 707 if expectedLocation.RealPath != gotLocation.RealPath { 708 t.Fatalf("locations do not match; expected: %v got: %v", expectedLocations, gotLocations) 709 } 710 } 711 712 assertPackagesAreEqual(t, test.expected, p) 713 } 714 }) 715 } 716 } 717 718 func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) { 719 c := NewCataloger() 720 721 src, err := source.NewFromDirectory("test-fixtures/classifiers/negative") 722 assert.NoError(t, err) 723 724 resolver, err := src.FileResolver(source.SquashedScope) 725 assert.NoError(t, err) 726 727 actualResults, _, err := c.Catalog(resolver) 728 assert.NoError(t, err) 729 assert.Equal(t, 0, len(actualResults)) 730 } 731 732 func locations(locations ...string) file.LocationSet { 733 var locs []file.Location 734 for _, s := range locations { 735 locs = append(locs, file.NewLocation(s)) 736 } 737 return file.NewLocationSet(locs...) 738 } 739 740 // metadata paths are: realPath, virtualPath 741 func metadata(classifier string, paths ...string) pkg.BinaryMetadata { 742 return pkg.BinaryMetadata{ 743 Matches: []pkg.ClassifierMatch{ 744 match(classifier, paths...), 745 }, 746 } 747 } 748 749 // match paths are: realPath, virtualPath 750 func match(classifier string, paths ...string) pkg.ClassifierMatch { 751 realPath := "" 752 if len(paths) > 0 { 753 realPath = paths[0] 754 } 755 virtualPath := "" 756 if len(paths) > 1 { 757 virtualPath = paths[1] 758 } 759 return pkg.ClassifierMatch{ 760 Classifier: classifier, 761 Location: file.NewVirtualLocationFromCoordinates( 762 file.Coordinates{ 763 RealPath: realPath, 764 }, 765 virtualPath, 766 ), 767 } 768 } 769 770 func assertPackagesAreEqual(t *testing.T, expected pkg.Package, p pkg.Package) { 771 var failMessages []string 772 expectedLocations := expected.Locations.ToSlice() 773 gotLocations := p.Locations.ToSlice() 774 775 if len(expectedLocations) != len(gotLocations) { 776 failMessages = append(failMessages, "locations are not equal length") 777 } else { 778 for i, expectedLocation := range expectedLocations { 779 gotLocation := gotLocations[i] 780 if expectedLocation.RealPath != gotLocation.RealPath { 781 failMessages = append(failMessages, fmt.Sprintf("locations do not match; expected: %v got: %v", expectedLocation.RealPath, gotLocation.RealPath)) 782 } 783 } 784 } 785 786 m1 := expected.Metadata.(pkg.BinaryMetadata).Matches 787 m2 := p.Metadata.(pkg.BinaryMetadata).Matches 788 matches := true 789 if len(m1) == len(m2) { 790 for i, m1 := range m1 { 791 m2 := m2[i] 792 if m1.Classifier != m2.Classifier { 793 matches = false 794 break 795 } 796 if m1.Location.RealPath != "" && m1.Location.RealPath != m2.Location.RealPath { 797 matches = false 798 break 799 } 800 if m1.Location.VirtualPath != "" && m1.Location.VirtualPath != m2.Location.VirtualPath { 801 matches = false 802 break 803 } 804 } 805 } else { 806 matches = false 807 } 808 809 if !matches { 810 failMessages = append(failMessages, "classifier matches not equal") 811 } 812 if expected.Name != p.Name || 813 expected.Version != p.Version || 814 expected.PURL != p.PURL { 815 failMessages = append(failMessages, "packages do not match") 816 } 817 818 if len(failMessages) > 0 { 819 assert.Failf(t, strings.Join(failMessages, "; "), "diff: %s", 820 cmp.Diff(expected, p, 821 cmp.Transformer("Locations", func(l file.LocationSet) []file.Location { 822 return l.ToSlice() 823 }), 824 cmpopts.IgnoreUnexported(pkg.Package{}, file.Location{}), 825 cmpopts.IgnoreFields(pkg.Package{}, "CPEs", "FoundBy", "MetadataType", "Type"), 826 )) 827 } 828 } 829 830 type panicyResolver struct { 831 searchCalled bool 832 } 833 834 func (p *panicyResolver) FilesByExtension(_ ...string) ([]file.Location, error) { 835 p.searchCalled = true 836 return nil, errors.New("not implemented") 837 } 838 839 func (p *panicyResolver) FilesByBasename(_ ...string) ([]file.Location, error) { 840 p.searchCalled = true 841 return nil, errors.New("not implemented") 842 } 843 844 func (p *panicyResolver) FilesByBasenameGlob(_ ...string) ([]file.Location, error) { 845 p.searchCalled = true 846 return nil, errors.New("not implemented") 847 } 848 849 func (p *panicyResolver) FileContentsByLocation(_ file.Location) (io.ReadCloser, error) { 850 p.searchCalled = true 851 return nil, errors.New("not implemented") 852 } 853 854 func (p *panicyResolver) HasPath(_ string) bool { 855 return true 856 } 857 858 func (p *panicyResolver) FilesByPath(_ ...string) ([]file.Location, error) { 859 p.searchCalled = true 860 return nil, errors.New("not implemented") 861 } 862 863 func (p *panicyResolver) FilesByGlob(_ ...string) ([]file.Location, error) { 864 p.searchCalled = true 865 return nil, errors.New("not implemented") 866 } 867 868 func (p *panicyResolver) FilesByMIMEType(_ ...string) ([]file.Location, error) { 869 p.searchCalled = true 870 return nil, errors.New("not implemented") 871 } 872 873 func (p *panicyResolver) RelativeFileByPath(_ file.Location, _ string) *file.Location { 874 return nil 875 } 876 877 func (p *panicyResolver) AllLocations() <-chan file.Location { 878 return nil 879 } 880 881 func (p *panicyResolver) FileMetadataByLocation(_ file.Location) (file.Metadata, error) { 882 return file.Metadata{}, errors.New("not implemented") 883 } 884 885 var _ file.Resolver = (*panicyResolver)(nil) 886 887 func Test_Cataloger_ResilientToErrors(t *testing.T) { 888 c := NewCataloger() 889 890 resolver := &panicyResolver{} 891 _, _, err := c.Catalog(resolver) 892 assert.NoError(t, err) 893 assert.True(t, resolver.searchCalled) 894 }