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