github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/golang/parse_go_binary_test.go (about) 1 package golang 2 3 import ( 4 "bufio" 5 "bytes" 6 "context" 7 "errors" 8 "io" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "runtime/debug" 13 "strconv" 14 "strings" 15 "syscall" 16 "testing" 17 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 21 "github.com/anchore/syft/syft/file" 22 "github.com/anchore/syft/syft/internal/fileresolver" 23 "github.com/anchore/syft/syft/internal/unionreader" 24 "github.com/anchore/syft/syft/pkg" 25 "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" 26 ) 27 28 // make will run the default make target for the given test fixture path 29 func runMakeTarget(t *testing.T, fixtureName string) { 30 cwd, err := os.Getwd() 31 require.NoError(t, err) 32 fixtureDir := filepath.Join(cwd, "test-fixtures/", fixtureName) 33 34 t.Logf("Generating Fixture in %q", fixtureDir) 35 36 cmd := exec.Command("make") 37 cmd.Dir = fixtureDir 38 39 stderr, err := cmd.StderrPipe() 40 require.NoError(t, err) 41 42 stdout, err := cmd.StdoutPipe() 43 require.NoError(t, err) 44 45 err = cmd.Start() 46 require.NoError(t, err) 47 48 show := func(label string, reader io.ReadCloser) { 49 scanner := bufio.NewScanner(reader) 50 scanner.Split(bufio.ScanLines) 51 for scanner.Scan() { 52 t.Logf("%s: %s", label, scanner.Text()) 53 } 54 } 55 go show("out", stdout) 56 go show("err", stderr) 57 58 if err := cmd.Wait(); err != nil { 59 if exiterr, ok := err.(*exec.ExitError); ok { 60 // The program has exited with an exit code != 0 61 62 // This works on both Unix and Windows. Although package 63 // syscall is generally platform dependent, WaitStatus is 64 // defined for both Unix and Windows and in both cases has 65 // an ExitStatus() method with the same signature. 66 if status, ok := exiterr.Sys().(syscall.WaitStatus); ok { 67 if status.ExitStatus() != 0 { 68 t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus()) 69 } 70 } 71 } else { 72 t.Fatalf("unable to get generate fixture result: %+v", err) 73 } 74 } 75 } 76 77 func Test_getGOARCHFromBin(t *testing.T) { 78 runMakeTarget(t, "archs") 79 80 tests := []struct { 81 name string 82 filepath string 83 expected string 84 }{ 85 { 86 name: "pe", 87 filepath: "test-fixtures/archs/binaries/hello-win-amd64", 88 // see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types 89 expected: strconv.Itoa(0x8664), 90 }, 91 { 92 name: "elf-ppc64", 93 filepath: "test-fixtures/archs/binaries/hello-linux-ppc64le", 94 expected: "ppc64", 95 }, 96 { 97 name: "mach-o-arm64", 98 filepath: "test-fixtures/archs/binaries/hello-mach-o-arm64", 99 expected: "arm64", 100 }, 101 { 102 name: "linux-arm", 103 filepath: "test-fixtures/archs/binaries/hello-linux-arm", 104 expected: "arm", 105 }, 106 { 107 name: "xcoff-32bit", 108 filepath: "internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec", 109 expected: strconv.Itoa(0x1DF), 110 }, 111 { 112 name: "xcoff-64bit", 113 filepath: "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec", 114 expected: strconv.Itoa(0x1F7), 115 }, 116 } 117 118 for _, tt := range tests { 119 f, err := os.Open(tt.filepath) 120 require.NoError(t, err) 121 arch, err := getGOARCHFromBin(f) 122 require.NoError(t, err, "test name: %s", tt.name) 123 assert.Equal(t, tt.expected, arch) 124 } 125 126 } 127 128 func TestBuildGoPkgInfo(t *testing.T) { 129 const ( 130 goCompiledVersion = "1.18" 131 archDetails = "amd64" 132 ) 133 134 defaultBuildSettings := []pkg.KeyValue{ 135 { 136 Key: "GOARCH", 137 Value: "amd64", 138 }, 139 { 140 Key: "GOOS", 141 Value: "darwin", 142 }, 143 { 144 Key: "GOAMD64", 145 Value: "v1", 146 }, 147 } 148 149 unmodifiedMain := pkg.Package{ 150 Name: "github.com/anchore/syft", 151 Language: pkg.Go, 152 Type: pkg.GoModulePkg, 153 Version: "", // this was (devel) but we cleared it explicitly 154 PURL: "pkg:golang/github.com/anchore/syft", 155 Locations: file.NewLocationSet( 156 file.NewLocationFromCoordinates( 157 file.Coordinates{ 158 RealPath: "/a-path", 159 FileSystemID: "layer-id", 160 }, 161 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 162 ), 163 Metadata: pkg.GolangBinaryBuildinfoEntry{ 164 GoCompiledVersion: goCompiledVersion, 165 Architecture: archDetails, 166 BuildSettings: defaultBuildSettings, 167 MainModule: "github.com/anchore/syft", 168 }, 169 } 170 171 tests := []struct { 172 name string 173 mod *extendedBuildInfo 174 expected []pkg.Package 175 cfg *CatalogerConfig 176 binaryContent string 177 }{ 178 { 179 name: "package without name", 180 mod: &extendedBuildInfo{ 181 BuildInfo: &debug.BuildInfo{ 182 Deps: []*debug.Module{ 183 { 184 Path: "github.com/adrg/xdg", 185 }, 186 { 187 Path: "", 188 Version: "v0.2.1", 189 }, 190 }, 191 }, 192 cryptoSettings: nil, 193 arch: "", 194 }, 195 expected: []pkg.Package{ 196 { 197 Name: "github.com/adrg/xdg", 198 PURL: "pkg:golang/github.com/adrg/xdg", 199 Language: pkg.Go, 200 Type: pkg.GoModulePkg, 201 Locations: file.NewLocationSet( 202 file.NewLocationFromCoordinates( 203 file.Coordinates{ 204 RealPath: "/a-path", 205 FileSystemID: "layer-id", 206 }, 207 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 208 ), 209 Metadata: pkg.GolangBinaryBuildinfoEntry{}, 210 }, 211 }, 212 }, 213 { 214 name: "buildGoPkgInfo parses a blank mod and returns no packages", 215 mod: &extendedBuildInfo{BuildInfo: &debug.BuildInfo{}, cryptoSettings: nil, arch: ""}, 216 expected: []pkg.Package(nil), 217 }, 218 { 219 name: "parse a mod without main module", 220 mod: &extendedBuildInfo{ 221 BuildInfo: &debug.BuildInfo{ 222 GoVersion: goCompiledVersion, 223 Settings: []debug.BuildSetting{ 224 {Key: "GOARCH", Value: archDetails}, 225 {Key: "GOOS", Value: "darwin"}, 226 {Key: "GOAMD64", Value: "v1"}, 227 }, 228 Deps: []*debug.Module{ 229 { 230 Path: "github.com/adrg/xdg", 231 Version: "v0.2.1", 232 Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", 233 }, 234 }, 235 }, 236 cryptoSettings: nil, 237 arch: archDetails, 238 }, 239 expected: []pkg.Package{ 240 { 241 Name: "github.com/adrg/xdg", 242 Version: "v0.2.1", 243 PURL: "pkg:golang/github.com/adrg/xdg@v0.2.1", 244 Language: pkg.Go, 245 Type: pkg.GoModulePkg, 246 Locations: file.NewLocationSet( 247 file.NewLocationFromCoordinates( 248 file.Coordinates{ 249 RealPath: "/a-path", 250 FileSystemID: "layer-id", 251 }, 252 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 253 ), 254 Metadata: pkg.GolangBinaryBuildinfoEntry{ 255 GoCompiledVersion: goCompiledVersion, 256 Architecture: archDetails, 257 H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", 258 }, 259 }, 260 }, 261 }, 262 { 263 name: "parse a mod with path but no main module", 264 mod: &extendedBuildInfo{ 265 BuildInfo: &debug.BuildInfo{ 266 GoVersion: goCompiledVersion, 267 Settings: []debug.BuildSetting{ 268 {Key: "GOARCH", Value: archDetails}, 269 {Key: "GOOS", Value: "darwin"}, 270 {Key: "GOAMD64", Value: "v1"}, 271 }, 272 Path: "github.com/a/b/c", 273 }, 274 cryptoSettings: []string{"boringcrypto + fips"}, 275 arch: archDetails, 276 }, 277 expected: []pkg.Package{ 278 { 279 Name: "github.com/a/b/c", 280 Version: "", // this was (devel) but we cleared it explicitly 281 PURL: "pkg:golang/github.com/a/b#c", 282 Language: pkg.Go, 283 Type: pkg.GoModulePkg, 284 Locations: file.NewLocationSet( 285 file.NewLocationFromCoordinates( 286 file.Coordinates{ 287 RealPath: "/a-path", 288 FileSystemID: "layer-id", 289 }, 290 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 291 ), 292 Metadata: pkg.GolangBinaryBuildinfoEntry{ 293 GoCompiledVersion: goCompiledVersion, 294 Architecture: archDetails, 295 H1Digest: "", 296 BuildSettings: []pkg.KeyValue{ 297 { 298 Key: "GOARCH", 299 Value: archDetails, 300 }, 301 { 302 Key: "GOOS", 303 Value: "darwin", 304 }, 305 { 306 Key: "GOAMD64", 307 Value: "v1", 308 }, 309 }, 310 MainModule: "github.com/a/b/c", 311 GoCryptoSettings: []string{"boringcrypto + fips"}, 312 }, 313 }, 314 }, 315 }, 316 { 317 name: "parse a mod without packages", 318 mod: &extendedBuildInfo{ 319 BuildInfo: &debug.BuildInfo{ 320 GoVersion: goCompiledVersion, 321 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 322 Settings: []debug.BuildSetting{ 323 {Key: "GOARCH", Value: archDetails}, 324 {Key: "GOOS", Value: "darwin"}, 325 {Key: "GOAMD64", Value: "v1"}, 326 }, 327 }, 328 cryptoSettings: nil, 329 arch: archDetails, 330 }, 331 expected: []pkg.Package{unmodifiedMain}, 332 }, 333 { 334 name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)", 335 mod: &extendedBuildInfo{ 336 BuildInfo: &debug.BuildInfo{ 337 GoVersion: goCompiledVersion, 338 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 339 Settings: []debug.BuildSetting{ 340 {Key: "GOARCH", Value: archDetails}, 341 {Key: "GOOS", Value: "darwin"}, 342 {Key: "GOAMD64", Value: "v1"}, 343 {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, 344 {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, 345 {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, 346 }, 347 }, 348 cryptoSettings: nil, 349 arch: archDetails, 350 }, 351 expected: []pkg.Package{ 352 { 353 Name: "github.com/anchore/syft", 354 Language: pkg.Go, 355 Type: pkg.GoModulePkg, 356 Version: "v0.0.0-20221014195457-41bc6bb41035", 357 PURL: "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035", 358 Locations: file.NewLocationSet( 359 file.NewLocationFromCoordinates( 360 file.Coordinates{ 361 RealPath: "/a-path", 362 FileSystemID: "layer-id", 363 }, 364 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 365 ), 366 Metadata: pkg.GolangBinaryBuildinfoEntry{ 367 GoCompiledVersion: goCompiledVersion, 368 Architecture: archDetails, 369 BuildSettings: []pkg.KeyValue{ 370 { 371 Key: "GOARCH", 372 Value: archDetails, 373 }, 374 { 375 Key: "GOOS", 376 Value: "darwin", 377 }, 378 { 379 Key: "GOAMD64", 380 Value: "v1", 381 }, 382 { 383 Key: "vcs.revision", 384 Value: "41bc6bb410352845f22766e27dd48ba93aa825a4", 385 }, 386 { 387 Key: "vcs.time", 388 Value: "2022-10-14T19:54:57Z", 389 }, 390 { 391 Key: "-ldflags", 392 Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`, 393 }, 394 }, 395 MainModule: "github.com/anchore/syft", 396 }, 397 }, 398 }, 399 }, 400 { 401 name: "parse main mod and replace devel version with one from ldflags with vcs. build settings", 402 mod: &extendedBuildInfo{ 403 BuildInfo: &debug.BuildInfo{ 404 GoVersion: goCompiledVersion, 405 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 406 Settings: []debug.BuildSetting{ 407 {Key: "GOARCH", Value: archDetails}, 408 {Key: "GOOS", Value: "darwin"}, 409 {Key: "GOAMD64", Value: "v1"}, 410 {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, 411 {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, 412 {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, 413 }, 414 }, 415 cryptoSettings: nil, 416 arch: archDetails, 417 }, 418 expected: []pkg.Package{ 419 { 420 Name: "github.com/anchore/syft", 421 Language: pkg.Go, 422 Type: pkg.GoModulePkg, 423 Version: "v0.79.0", 424 PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", 425 Locations: file.NewLocationSet( 426 file.NewLocationFromCoordinates( 427 file.Coordinates{ 428 RealPath: "/a-path", 429 FileSystemID: "layer-id", 430 }, 431 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 432 ), 433 Metadata: pkg.GolangBinaryBuildinfoEntry{ 434 GoCompiledVersion: goCompiledVersion, 435 Architecture: archDetails, 436 BuildSettings: []pkg.KeyValue{ 437 { 438 Key: "GOARCH", 439 Value: archDetails, 440 }, 441 { 442 Key: "GOOS", 443 Value: "darwin", 444 }, 445 { 446 Key: "GOAMD64", 447 Value: "v1", 448 }, 449 { 450 Key: "vcs.revision", 451 Value: "41bc6bb410352845f22766e27dd48ba93aa825a4", 452 }, 453 { 454 Key: "vcs.time", 455 Value: "2022-10-14T19:54:57Z", 456 }, 457 { 458 Key: "-ldflags", 459 Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`, 460 }, 461 }, 462 MainModule: "github.com/anchore/syft", 463 }, 464 }, 465 }, 466 }, 467 { 468 name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings", 469 mod: &extendedBuildInfo{ 470 BuildInfo: &debug.BuildInfo{ 471 GoVersion: goCompiledVersion, 472 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 473 Settings: []debug.BuildSetting{ 474 {Key: "GOARCH", Value: archDetails}, 475 {Key: "GOOS", Value: "darwin"}, 476 {Key: "GOAMD64", Value: "v1"}, 477 {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, 478 }, 479 }, 480 cryptoSettings: nil, 481 arch: archDetails, 482 }, 483 expected: []pkg.Package{ 484 { 485 Name: "github.com/anchore/syft", 486 Language: pkg.Go, 487 Type: pkg.GoModulePkg, 488 Version: "v0.79.0", 489 PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", 490 Locations: file.NewLocationSet( 491 file.NewLocationFromCoordinates( 492 file.Coordinates{ 493 RealPath: "/a-path", 494 FileSystemID: "layer-id", 495 }, 496 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 497 ), 498 Metadata: pkg.GolangBinaryBuildinfoEntry{ 499 GoCompiledVersion: goCompiledVersion, 500 Architecture: archDetails, 501 BuildSettings: []pkg.KeyValue{ 502 { 503 Key: "GOARCH", 504 Value: archDetails, 505 }, 506 { 507 Key: "GOOS", 508 Value: "darwin", 509 }, 510 { 511 Key: "GOAMD64", 512 Value: "v1", 513 }, 514 { 515 Key: "-ldflags", 516 Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`, 517 }, 518 }, 519 MainModule: "github.com/anchore/syft", 520 }, 521 }, 522 }, 523 }, 524 { 525 name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings", 526 mod: &extendedBuildInfo{ 527 BuildInfo: &debug.BuildInfo{ 528 GoVersion: goCompiledVersion, 529 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 530 Settings: []debug.BuildSetting{ 531 {Key: "GOARCH", Value: archDetails}, 532 {Key: "GOOS", Value: "darwin"}, 533 {Key: "GOAMD64", Value: "v1"}, 534 {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`}, 535 }, 536 }, 537 cryptoSettings: nil, 538 arch: archDetails, 539 }, 540 expected: []pkg.Package{ 541 { 542 Name: "github.com/anchore/syft", 543 Language: pkg.Go, 544 Type: pkg.GoModulePkg, 545 Version: "v0.79.0", 546 PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", 547 Locations: file.NewLocationSet( 548 file.NewLocationFromCoordinates( 549 file.Coordinates{ 550 RealPath: "/a-path", 551 FileSystemID: "layer-id", 552 }, 553 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 554 ), 555 Metadata: pkg.GolangBinaryBuildinfoEntry{ 556 GoCompiledVersion: goCompiledVersion, 557 Architecture: archDetails, 558 BuildSettings: []pkg.KeyValue{ 559 { 560 Key: "GOARCH", 561 Value: archDetails, 562 }, 563 { 564 Key: "GOOS", 565 Value: "darwin", 566 }, 567 { 568 Key: "GOAMD64", 569 Value: "v1", 570 }, 571 { 572 Key: "-ldflags", 573 Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`, 574 }, 575 }, 576 MainModule: "github.com/anchore/syft", 577 }, 578 }, 579 }, 580 }, 581 { 582 name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings", 583 mod: &extendedBuildInfo{ 584 BuildInfo: &debug.BuildInfo{ 585 GoVersion: goCompiledVersion, 586 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 587 Settings: []debug.BuildSetting{ 588 {Key: "GOARCH", Value: archDetails}, 589 {Key: "GOOS", Value: "darwin"}, 590 {Key: "GOAMD64", Value: "v1"}, 591 {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`}, 592 }, 593 }, 594 cryptoSettings: nil, 595 arch: archDetails, 596 }, 597 expected: []pkg.Package{ 598 { 599 Name: "github.com/anchore/syft", 600 Language: pkg.Go, 601 Type: pkg.GoModulePkg, 602 Version: "v0.79.0", 603 PURL: "pkg:golang/github.com/anchore/syft@v0.79.0", 604 Locations: file.NewLocationSet( 605 file.NewLocationFromCoordinates( 606 file.Coordinates{ 607 RealPath: "/a-path", 608 FileSystemID: "layer-id", 609 }, 610 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 611 ), 612 Metadata: pkg.GolangBinaryBuildinfoEntry{ 613 GoCompiledVersion: goCompiledVersion, 614 Architecture: archDetails, 615 BuildSettings: []pkg.KeyValue{ 616 { 617 Key: "GOARCH", 618 Value: archDetails, 619 }, 620 { 621 Key: "GOOS", 622 Value: "darwin", 623 }, 624 { 625 Key: "GOAMD64", 626 Value: "v1", 627 }, 628 { 629 Key: "-ldflags", 630 Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`, 631 }, 632 }, 633 MainModule: "github.com/anchore/syft", 634 }, 635 }, 636 }, 637 }, 638 { 639 name: "parse main mod and replace devel version with a pseudo version", 640 mod: &extendedBuildInfo{ 641 BuildInfo: &debug.BuildInfo{ 642 GoVersion: goCompiledVersion, 643 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 644 Settings: []debug.BuildSetting{ 645 {Key: "GOARCH", Value: archDetails}, 646 {Key: "GOOS", Value: "darwin"}, 647 {Key: "GOAMD64", Value: "v1"}, 648 {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, 649 {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, 650 }, 651 }, 652 cryptoSettings: nil, 653 arch: archDetails, 654 }, 655 expected: []pkg.Package{ 656 { 657 Name: "github.com/anchore/syft", 658 Language: pkg.Go, 659 Type: pkg.GoModulePkg, 660 Version: "v0.0.0-20221014195457-41bc6bb41035", 661 PURL: "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035", 662 Locations: file.NewLocationSet( 663 file.NewLocationFromCoordinates( 664 file.Coordinates{ 665 RealPath: "/a-path", 666 FileSystemID: "layer-id", 667 }, 668 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 669 ), 670 Metadata: pkg.GolangBinaryBuildinfoEntry{ 671 GoCompiledVersion: goCompiledVersion, 672 Architecture: archDetails, 673 BuildSettings: []pkg.KeyValue{ 674 { 675 Key: "GOARCH", 676 Value: archDetails, 677 }, 678 { 679 Key: "GOOS", 680 Value: "darwin", 681 }, 682 { 683 Key: "GOAMD64", 684 Value: "v1", 685 }, 686 { 687 Key: "vcs.revision", 688 Value: "41bc6bb410352845f22766e27dd48ba93aa825a4", 689 }, 690 { 691 Key: "vcs.time", 692 Value: "2022-10-14T19:54:57Z", 693 }, 694 }, 695 MainModule: "github.com/anchore/syft", 696 }, 697 }, 698 }, 699 }, 700 { 701 name: "parse a populated mod string and returns packages but no source info", 702 mod: &extendedBuildInfo{ 703 BuildInfo: &debug.BuildInfo{ 704 GoVersion: goCompiledVersion, 705 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 706 Settings: []debug.BuildSetting{ 707 {Key: "GOARCH", Value: archDetails}, 708 {Key: "GOOS", Value: "darwin"}, 709 {Key: "GOAMD64", Value: "v1"}, 710 }, 711 Deps: []*debug.Module{ 712 { 713 Path: "github.com/adrg/xdg", 714 Version: "v0.2.1", 715 Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", 716 }, 717 { 718 Path: "github.com/anchore/client-go", 719 Version: "v0.0.0-20210222170800-9c70f9b80bcf", 720 Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", 721 }, 722 }, 723 }, 724 cryptoSettings: nil, 725 arch: archDetails, 726 }, 727 expected: []pkg.Package{ 728 { 729 Name: "github.com/adrg/xdg", 730 Version: "v0.2.1", 731 PURL: "pkg:golang/github.com/adrg/xdg@v0.2.1", 732 Language: pkg.Go, 733 Type: pkg.GoModulePkg, 734 Locations: file.NewLocationSet( 735 file.NewLocationFromCoordinates( 736 file.Coordinates{ 737 RealPath: "/a-path", 738 FileSystemID: "layer-id", 739 }, 740 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 741 ), 742 Metadata: pkg.GolangBinaryBuildinfoEntry{ 743 GoCompiledVersion: goCompiledVersion, 744 Architecture: archDetails, 745 H1Digest: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", 746 MainModule: "github.com/anchore/syft", 747 }, 748 }, 749 { 750 Name: "github.com/anchore/client-go", 751 Version: "v0.0.0-20210222170800-9c70f9b80bcf", 752 PURL: "pkg:golang/github.com/anchore/client-go@v0.0.0-20210222170800-9c70f9b80bcf", 753 Language: pkg.Go, 754 Type: pkg.GoModulePkg, 755 Locations: file.NewLocationSet( 756 file.NewLocationFromCoordinates( 757 file.Coordinates{ 758 RealPath: "/a-path", 759 FileSystemID: "layer-id", 760 }, 761 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 762 ), 763 Metadata: pkg.GolangBinaryBuildinfoEntry{ 764 GoCompiledVersion: goCompiledVersion, 765 Architecture: archDetails, 766 H1Digest: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", 767 MainModule: "github.com/anchore/syft", 768 }, 769 }, 770 unmodifiedMain, 771 }, 772 }, 773 { 774 name: "parse a populated mod string and returns packages when a replace directive exists", 775 mod: &extendedBuildInfo{ 776 BuildInfo: &debug.BuildInfo{ 777 GoVersion: goCompiledVersion, 778 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 779 Settings: []debug.BuildSetting{ 780 {Key: "GOARCH", Value: archDetails}, 781 {Key: "GOOS", Value: "darwin"}, 782 {Key: "GOAMD64", Value: "v1"}, 783 }, 784 Deps: []*debug.Module{ 785 { 786 Path: "golang.org/x/sys", 787 Version: "v0.0.0-20211006194710-c8a6f5223071", 788 Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", 789 }, 790 { 791 Path: "golang.org/x/term", 792 Version: "v0.0.0-20210927222741-03fcf44c2211", 793 Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", 794 Replace: &debug.Module{ 795 Path: "golang.org/x/term", 796 Version: "v0.0.0-20210916214954-140adaaadfaf", 797 Sum: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=", 798 }, 799 }, 800 }, 801 }, 802 cryptoSettings: nil, 803 arch: archDetails, 804 }, 805 expected: []pkg.Package{ 806 { 807 Name: "golang.org/x/sys", 808 Version: "v0.0.0-20211006194710-c8a6f5223071", 809 PURL: "pkg:golang/golang.org/x/sys@v0.0.0-20211006194710-c8a6f5223071", 810 Language: pkg.Go, 811 Type: pkg.GoModulePkg, 812 Locations: file.NewLocationSet( 813 file.NewLocationFromCoordinates( 814 file.Coordinates{ 815 RealPath: "/a-path", 816 FileSystemID: "layer-id", 817 }, 818 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 819 ), 820 Metadata: pkg.GolangBinaryBuildinfoEntry{ 821 GoCompiledVersion: goCompiledVersion, 822 Architecture: archDetails, 823 H1Digest: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", 824 MainModule: "github.com/anchore/syft", 825 }}, 826 { 827 Name: "golang.org/x/term", 828 Version: "v0.0.0-20210916214954-140adaaadfaf", 829 PURL: "pkg:golang/golang.org/x/term@v0.0.0-20210916214954-140adaaadfaf", 830 Language: pkg.Go, 831 Type: pkg.GoModulePkg, 832 Locations: file.NewLocationSet( 833 file.NewLocationFromCoordinates( 834 file.Coordinates{ 835 RealPath: "/a-path", 836 FileSystemID: "layer-id", 837 }, 838 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 839 ), 840 Metadata: pkg.GolangBinaryBuildinfoEntry{ 841 GoCompiledVersion: goCompiledVersion, 842 Architecture: archDetails, 843 H1Digest: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=", 844 MainModule: "github.com/anchore/syft", 845 }, 846 }, 847 unmodifiedMain, 848 }, 849 }, 850 { 851 name: "parse a populated mod string and returns packages when a replace directive and synthetic main module 'command line arguments' exists", 852 mod: &extendedBuildInfo{ 853 BuildInfo: &debug.BuildInfo{ 854 GoVersion: goCompiledVersion, 855 Main: debug.Module{Path: "command-line-arguments", Version: devel}, 856 Settings: []debug.BuildSetting{ 857 {Key: "GOARCH", Value: archDetails}, 858 {Key: "GOOS", Value: "linux"}, 859 {Key: "GOAMD64", Value: "v1"}, 860 }, 861 Path: "command-line-arguments", 862 Deps: []*debug.Module{ 863 { 864 Path: "example.com/mylib", 865 Version: "v0.0.0", 866 Replace: &debug.Module{ 867 Path: "./mylib", 868 Version: devel, 869 }, 870 }, 871 { 872 Path: "command-line-arguments", 873 Version: devel, 874 }, 875 }, 876 }, 877 cryptoSettings: nil, 878 arch: archDetails, 879 }, 880 expected: []pkg.Package{ 881 { 882 Name: "example.com/mylib", 883 Version: "", 884 PURL: "pkg:golang/example.com/mylib", 885 Language: pkg.Go, 886 Type: pkg.GoModulePkg, 887 Locations: file.NewLocationSet( 888 file.NewLocationFromCoordinates( 889 file.Coordinates{ 890 RealPath: "/a-path", 891 FileSystemID: "layer-id", 892 }, 893 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 894 ), 895 Metadata: pkg.GolangBinaryBuildinfoEntry{ 896 GoCompiledVersion: goCompiledVersion, 897 Architecture: archDetails, 898 H1Digest: "", 899 MainModule: "command-line-arguments", 900 }, 901 }, 902 { 903 Name: "command-line-arguments", 904 Version: "", 905 PURL: "pkg:golang/command-line-arguments", 906 Language: pkg.Go, 907 Type: pkg.GoModulePkg, 908 Locations: file.NewLocationSet( 909 file.NewLocationFromCoordinates( 910 file.Coordinates{ 911 RealPath: "/a-path", 912 FileSystemID: "layer-id", 913 }, 914 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 915 ), 916 Metadata: pkg.GolangBinaryBuildinfoEntry{ 917 BuildSettings: pkg.KeyValues{ 918 {Key: "GOARCH", Value: "amd64"}, 919 {Key: "GOOS", Value: "linux"}, 920 {Key: "GOAMD64", Value: "v1"}, 921 }, 922 GoCompiledVersion: goCompiledVersion, 923 Architecture: archDetails, 924 H1Digest: "", 925 MainModule: "command-line-arguments", 926 }, 927 }, 928 }, 929 }, 930 { 931 name: "parse main mod and replace devel with pattern from binary contents", 932 cfg: func() *CatalogerConfig { 933 c := DefaultCatalogerConfig() 934 // off by default 935 assert.False(t, c.MainModuleVersion.FromContents) 936 // override to true for this test 937 c.MainModuleVersion.FromContents = true 938 return &c 939 }(), 940 mod: &extendedBuildInfo{ 941 BuildInfo: &debug.BuildInfo{ 942 GoVersion: goCompiledVersion, 943 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 944 Settings: []debug.BuildSetting{ 945 {Key: "GOARCH", Value: archDetails}, 946 {Key: "GOOS", Value: "darwin"}, 947 {Key: "GOAMD64", Value: "v1"}, 948 {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, // important! missing revision 949 {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, 950 }, 951 }, 952 cryptoSettings: nil, 953 arch: archDetails, 954 }, 955 binaryContent: "\x00v1.0.0-somethingelse+incompatible\x00", 956 expected: []pkg.Package{ 957 { 958 Name: "github.com/anchore/syft", 959 Language: pkg.Go, 960 Type: pkg.GoModulePkg, 961 Version: "v1.0.0-somethingelse+incompatible", 962 PURL: "pkg:golang/github.com/anchore/syft@v1.0.0-somethingelse%2Bincompatible", 963 Locations: file.NewLocationSet( 964 file.NewLocationFromCoordinates( 965 file.Coordinates{ 966 RealPath: "/a-path", 967 FileSystemID: "layer-id", 968 }, 969 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 970 ), 971 Metadata: pkg.GolangBinaryBuildinfoEntry{ 972 GoCompiledVersion: goCompiledVersion, 973 Architecture: archDetails, 974 BuildSettings: []pkg.KeyValue{ 975 { 976 Key: "GOARCH", 977 Value: archDetails, 978 }, 979 { 980 Key: "GOOS", 981 Value: "darwin", 982 }, 983 { 984 Key: "GOAMD64", 985 Value: "v1", 986 }, 987 { 988 Key: "vcs.time", 989 Value: "2022-10-14T19:54:57Z", 990 }, 991 { 992 Key: "-ldflags", 993 Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`, 994 }, 995 }, 996 MainModule: "github.com/anchore/syft", 997 }, 998 }, 999 }, 1000 }, 1001 { 1002 name: "parse a mod with go experiments", 1003 mod: &extendedBuildInfo{ 1004 BuildInfo: &debug.BuildInfo{ 1005 GoVersion: "go1.22.2 X:nocoverageredesign,noallocheaders,noexectracer2", 1006 Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, 1007 Settings: []debug.BuildSetting{ 1008 {Key: "GOARCH", Value: archDetails}, 1009 {Key: "GOOS", Value: "darwin"}, 1010 {Key: "GOAMD64", Value: "v1"}, 1011 }, 1012 }, 1013 cryptoSettings: nil, 1014 arch: archDetails, 1015 }, 1016 expected: []pkg.Package{{ 1017 Name: "github.com/anchore/syft", 1018 Language: pkg.Go, 1019 Type: pkg.GoModulePkg, 1020 Version: "", // this was (devel) but we cleared it explicitly 1021 PURL: "pkg:golang/github.com/anchore/syft", 1022 Locations: file.NewLocationSet( 1023 file.NewLocationFromCoordinates( 1024 file.Coordinates{ 1025 RealPath: "/a-path", 1026 FileSystemID: "layer-id", 1027 }, 1028 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 1029 ), 1030 Metadata: pkg.GolangBinaryBuildinfoEntry{ 1031 GoCompiledVersion: "go1.22.2", 1032 Architecture: archDetails, 1033 BuildSettings: defaultBuildSettings, 1034 MainModule: "github.com/anchore/syft", 1035 GoExperiments: []string{"nocoverageredesign", "noallocheaders", "noexectracer2"}, 1036 }, 1037 }}, 1038 }, 1039 { 1040 name: "parse a mod from path (partial build of package)", 1041 mod: &extendedBuildInfo{ 1042 BuildInfo: &debug.BuildInfo{ 1043 GoVersion: "go1.22.2", 1044 Main: debug.Module{Path: "command-line-arguments"}, 1045 Settings: []debug.BuildSetting{ 1046 { 1047 Key: "-ldflags", 1048 Value: `build -ldflags="-w -s -X github.com/kuskoman/logstash-exporter/config.Version=v1.7.0 -X github.com/kuskoman/logstash-exporter/config.GitCommit=db696dbcfe5a91d288d5ad44ce8ccbea97e65978 -X github.com/kuskoman/logstash-exporter/config.BuildDate=2024-07-17T08:12:17Z"`, 1049 }, 1050 {Key: "GOARCH", Value: archDetails}, 1051 {Key: "GOOS", Value: "darwin"}, 1052 {Key: "GOAMD64", Value: "v1"}, 1053 }, 1054 Deps: []*debug.Module{ 1055 { 1056 Path: "github.com/kuskoman/something-else", 1057 Version: "v1.2.3", 1058 }, 1059 { 1060 Path: "github.com/kuskoman/logstash-exporter", 1061 Version: "(devel)", 1062 }, 1063 }, 1064 }, 1065 arch: archDetails, 1066 }, 1067 expected: []pkg.Package{ 1068 { 1069 Name: "github.com/kuskoman/something-else", 1070 Language: pkg.Go, 1071 Type: pkg.GoModulePkg, 1072 Version: "v1.2.3", 1073 PURL: "pkg:golang/github.com/kuskoman/something-else@v1.2.3", 1074 Locations: file.NewLocationSet( 1075 file.NewLocationFromCoordinates( 1076 file.Coordinates{ 1077 RealPath: "/a-path", 1078 FileSystemID: "layer-id", 1079 }, 1080 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 1081 ), 1082 Metadata: pkg.GolangBinaryBuildinfoEntry{ 1083 GoCompiledVersion: "go1.22.2", 1084 Architecture: archDetails, 1085 MainModule: "github.com/kuskoman/logstash-exporter", // correctly attached the main module 1086 }, 1087 }, 1088 { 1089 Name: "github.com/kuskoman/logstash-exporter", 1090 Language: pkg.Go, 1091 Type: pkg.GoModulePkg, 1092 Version: "v1.7.0", 1093 PURL: "pkg:golang/github.com/kuskoman/logstash-exporter@v1.7.0", 1094 Locations: file.NewLocationSet( 1095 file.NewLocationFromCoordinates( 1096 file.Coordinates{ 1097 RealPath: "/a-path", 1098 FileSystemID: "layer-id", 1099 }, 1100 ).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), 1101 ), 1102 Metadata: pkg.GolangBinaryBuildinfoEntry{ 1103 GoCompiledVersion: "go1.22.2", 1104 BuildSettings: []pkg.KeyValue{ 1105 { 1106 Key: "-ldflags", 1107 Value: `build -ldflags="-w -s -X github.com/kuskoman/logstash-exporter/config.Version=v1.7.0 -X github.com/kuskoman/logstash-exporter/config.GitCommit=db696dbcfe5a91d288d5ad44ce8ccbea97e65978 -X github.com/kuskoman/logstash-exporter/config.BuildDate=2024-07-17T08:12:17Z"`, 1108 }, 1109 { 1110 Key: "GOARCH", 1111 Value: "amd64", 1112 }, 1113 { 1114 Key: "GOOS", 1115 Value: "darwin", 1116 }, 1117 { 1118 Key: "GOAMD64", 1119 Value: "v1", 1120 }, 1121 }, 1122 Architecture: archDetails, 1123 MainModule: "github.com/kuskoman/logstash-exporter", 1124 }, 1125 }, 1126 }, 1127 }, 1128 } 1129 1130 for _, test := range tests { 1131 t.Run(test.name, func(t *testing.T) { 1132 for i := range test.expected { 1133 p := &test.expected[i] 1134 p.SetID() 1135 } 1136 location := file.NewLocationFromCoordinates( 1137 file.Coordinates{ 1138 RealPath: "/a-path", 1139 FileSystemID: "layer-id", 1140 }, 1141 ) 1142 1143 if test.cfg == nil { 1144 c := DefaultCatalogerConfig() 1145 test.cfg = &c 1146 } 1147 1148 c := newGoBinaryCataloger(*test.cfg) 1149 reader, err := unionreader.GetUnionReader(io.NopCloser(strings.NewReader(test.binaryContent))) 1150 require.NoError(t, err) 1151 mainPkg, pkgs := c.buildGoPkgInfo(context.Background(), fileresolver.Empty{}, location, test.mod, test.mod.arch, reader) 1152 if mainPkg != nil { 1153 pkgs = append(pkgs, *mainPkg) 1154 } 1155 require.Len(t, pkgs, len(test.expected)) 1156 for i, p := range pkgs { 1157 pkgtest.AssertPackagesEqual(t, test.expected[i], p) 1158 } 1159 }) 1160 } 1161 } 1162 1163 func Test_extractVersionFromLDFlags(t *testing.T) { 1164 tests := []struct { 1165 name string 1166 mainModule string 1167 ldflags string 1168 wantMajorVersion string 1169 wantFullVersion string 1170 }{ 1171 { 1172 name: "empty ldflags", 1173 ldflags: "", 1174 }, 1175 { 1176 name: "syft ldflags", 1177 mainModule: "github.com/anchore/syft", 1178 ldflags: ` build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0 -X github.com/anchore/syft/internal/version.gitCommit=b2b332e8b2b66af0905e98b54ebd713a922be1a8 -X github.com/anchore/syft/internal/version.buildDate=2023-04-21T16:20:25Z -X github.com/anchore/syft/internal/version.gitDescription=v0.79.0 "`, 1179 wantMajorVersion: "0", 1180 wantFullVersion: "v0.79.0", 1181 }, 1182 { 1183 name: "kubectl ldflags", 1184 mainModule: "k8s.io/kubernetes/vendor/k8s.io/client-go", 1185 ldflags: ` build -asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes 1186 build -compiler=gc 1187 build -gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes " 1188 build -ldflags="all=-X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/client-go/pkg/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/component-base/version.buildDate=2023-04-12T12:16:51Z' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/client-go/pkg/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/component-base/version.gitCommit=a1a87a0a2bcd605820920c6b0e618a8ab7d117d4' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitVersion=v1.25.9' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitVersion=v1.25.9' -X 'k8s.io/client-go/pkg/version.gitVersion=v1.25.9' -X 'k8s.io/component-base/version.gitVersion=v1.25.9' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMinor=25' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMinor=25' -X 'k8s.io/client-go/pkg/version.gitMinor=25' -X 'k8s.io/component-base/version.gitMinor=25' -s -w"`, 1189 wantMajorVersion: "1", 1190 wantFullVersion: "v1.25.9", 1191 }, 1192 { 1193 name: "nerdctl ldflags", 1194 mainModule: "github.com/containerd/nerdctl", 1195 ldflags: ` build -ldflags="-s -w -X github.com/containerd/nerdctl/pkg/version.Version=v1.3.1 -X github.com/containerd/nerdctl/pkg/version.Revision=b224b280ff3086516763c7335fc0e0997aca617a"`, 1196 wantMajorVersion: "1", 1197 wantFullVersion: "v1.3.1", 1198 }, 1199 { 1200 name: "limactl ldflags", 1201 mainModule: "github.com/lima-vm/lima", 1202 ldflags: ` build -ldflags="-s -w -X github.com/lima-vm/lima/pkg/version.Version=v0.15.1"`, 1203 wantMajorVersion: "0", 1204 wantFullVersion: "v0.15.1", 1205 }, 1206 { 1207 name: "terraform ldflags", 1208 mainModule: "github.com/hashicorp/terraform", 1209 ldflags: ` build -ldflags="-w -s -X 'github.com/hashicorp/terraform/version.Version=1.4.6' -X 'github.com/hashicorp/terraform/version.Prerelease='"`, 1210 wantMajorVersion: "1", 1211 wantFullVersion: "v1.4.6", 1212 }, 1213 { 1214 name: "kube-apiserver ldflags", 1215 mainModule: "k8s.io/kubernetes/vendor/k8s.io/client-go", 1216 ldflags: ` build -asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes 1217 build -buildmode=exe 1218 build -compiler=gc 1219 build -gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes " 1220 build -ldflags="all=-X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/client-go/pkg/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/component-base/version.buildDate=2023-04-14T13:14:42Z' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/client-go/pkg/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/component-base/version.gitCommit=4c9411232e10168d7b050c49a1b59f6df9d7ea4b' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/client-go/pkg/version.gitTreeState=clean' -X 'k8s.io/component-base/version.gitTreeState=clean' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitVersion=v1.27.1' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitVersion=v1.27.1' -X 'k8s.io/client-go/pkg/version.gitVersion=v1.27.1' -X 'k8s.io/component-base/version.gitVersion=v1.27.1' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/client-go/pkg/version.gitMajor=1' -X 'k8s.io/component-base/version.gitMajor=1' -X 'k8s.io/kubernetes/vendor/k8s.io/client-go/pkg/version.gitMinor=27' -X 'k8s.io/kubernetes/vendor/k8s.io/component-base/version.gitMinor=27' -X 'k8s.io/client-go/pkg/version.gitMinor=27' -X 'k8s.io/component-base/version.gitMinor=27' -s -w"`, 1221 wantMajorVersion: "1", 1222 wantFullVersion: "v1.27.1", 1223 }, 1224 { 1225 name: "prometheus ldflags", 1226 mainModule: "github.com/prometheus/common", 1227 ldflags: ` build -ldflags="-X github.com/prometheus/common/version.Version=2.44.0 -X github.com/prometheus/common/version.Revision=1ac5131f698ebc60f13fe2727f89b115a41f6558 -X github.com/prometheus/common/version.Branch=HEAD -X github.com/prometheus/common/version.BuildUser=root@739e8181c5db -X github.com/prometheus/common/version.BuildDate=20230514-06:18:11 -extldflags '-static'" 1228 build -tags=netgo,builtinassets,stringlabels`, 1229 wantMajorVersion: "2", 1230 wantFullVersion: "v2.44.0", 1231 }, 1232 { 1233 name: "influxdb ldflags", 1234 mainModule: "github.com/influxdata/influxdb-client-go/v2", 1235 ldflags: ` build -ldflags="-s -w -X main.version=v2.7.1 -X main.commit=407fa622e9 -X main.date=2023-04-28T13:24:27Z -linkmode=external -extld=/musl/x86_64/bin/musl-gcc -extldflags '-fno-PIC -static-pie -Wl,-z,stack-size=8388608'" 1236 build -tags=assets,sqlite_foreign_keys,sqlite_json,static_build,noasm`, 1237 wantMajorVersion: "2", 1238 wantFullVersion: "v2.7.1", 1239 }, 1240 { 1241 name: "gitea ldflags", 1242 mainModule: "code.gitea.io/gitea", 1243 ldflags: ` build -ldflags=" -X \"main.MakeVersion=GNU Make 4.1\" -X \"main.Version=1.19.3\" -X \"main.Tags=bindata sqlite sqlite_unlock_notify\" "`, 1244 wantMajorVersion: "1", 1245 wantFullVersion: "v1.19.3", 1246 }, 1247 { 1248 name: "docker sbom cli ldflags", 1249 mainModule: "github.com/docker/sbom-cli-plugin", 1250 ldflags: ` build -ldflags="-w -s -extldflags '-static' -X github.com/docker/sbom-cli-plugin/internal/version.version=0.6.1-SNAPSHOT-02cf1c8 -X github.com/docker/sbom-cli-plugin/internal/version.gitCommit=02cf1c888ad6662109ac6e3be618392514a56316 -X github.com/docker/sbom-cli-plugin/internal/version.gitDescription=v0.6.1-dirty "`, 1251 wantMajorVersion: "0", 1252 wantFullVersion: "v0.6.1-SNAPSHOT-02cf1c8", 1253 }, 1254 { 1255 name: "docker scout ldflags", 1256 mainModule: "github.com/docker/scout-cli-plugin", 1257 ldflags: ` build -ldflags="-w -s -extldflags '-static' -X github.com/docker/scout-cli-plugin/internal.version=0.10.0 "`, 1258 wantMajorVersion: "0", 1259 wantFullVersion: "v0.10.0", 1260 }, 1261 { 1262 name: "influx telegraf ldflags", 1263 mainModule: "github.com/influxdata/telegraf", 1264 ldflags: ` build -ldflags="-w -s -X github.com/influxdata/telegraf/internal.Commit=a3a884a1 -X github.com/influxdata/telegraf/internal.Branch=HEAD -X github.com/influxdata/telegraf/internal.Version=1.26.2"`, 1265 wantMajorVersion: "1", 1266 wantFullVersion: "v1.26.2", 1267 }, 1268 { 1269 name: "argocd ldflags", 1270 mainModule: "github.com/argoproj/argo-cd/v2", 1271 ldflags: ` build -ldflags="-X github.com/argoproj/argo-cd/v2/common.version=2.7.2 -X github.com/argoproj/argo-cd/v2/common.buildDate=2023-05-12T14:06:49Z -X github.com/argoproj/argo-cd/v2/common.gitCommit=cbee7e6011407ed2d1066c482db74e97e0cc6bdb -X github.com/argoproj/argo-cd/v2/common.gitTreeState=clean -X github.com/argoproj/argo-cd/v2/common.kubectlVersion=v0.24.2 -extldflags=\"-static\""`, 1272 wantMajorVersion: "2", 1273 wantFullVersion: "v2.7.2", 1274 }, 1275 { 1276 name: "kustomize ldflags", 1277 mainModule: "sigs.k8s.io/kustomize/api", 1278 ldflags: ` build -ldflags="-s -X sigs.k8s.io/kustomize/api/provenance.version=kustomize/v4.5.7 -X sigs.k8s.io/kustomize/api/provenance.gitCommit=56d82a8378dfc8dc3b3b1085e5a6e67b82966bd7 -X sigs.k8s.io/kustomize/api/provenance.buildDate=2022-08-02T16:35:54Z "`, 1279 wantMajorVersion: "4", 1280 wantFullVersion: "v4.5.7", 1281 }, 1282 { 1283 name: "TiDB 7.5.0 ldflags", 1284 mainModule: "github.com/pingcap/tidb", 1285 ldflags: `build -ldflags="-X \"github.com/pingcap/tidb/pkg/parser/mysql.TiDBReleaseVersion=v7.5.0\" -X \"github.com/pingcap/tidb/pkg/util/versioninfo.TiDBBuildTS=2023-11-24 08:51:04\" -X \"github.com/pingcap/tidb/pkg/util/versioninfo.TiDBGitHash=069631e2ecfedc000ffb92c67207bea81380f020\" -X \"github.com/pingcap/tidb/pkg/util/versioninfo.TiDBGitBranch=heads/refs/tags/v7.5.0\" -X \"github.com/pingcap/tidb/pkg/util/versioninfo.TiDBEdition=Community\" "`, 1286 wantMajorVersion: "7", 1287 wantFullVersion: "v7.5.0", 1288 }, 1289 { 1290 name: "TiDB 6.1.7 ldflags", 1291 mainModule: "github.com/pingcap/tidb", 1292 ldflags: `build -ldflags="-X \"github.com/pingcap/tidb/parser/mysql.TiDBReleaseVersion=v6.1.7\" -X \"github.com/pingcap/tidb/util/versioninfo.TiDBBuildTS=2023-07-04 12:06:03\" -X \"github.com/pingcap/tidb/util/versioninfo.TiDBGitHash=613ecc5f731b2843e1d53a43915e2cd8da795936\" -X \"github.com/pingcap/tidb/util/versioninfo.TiDBGitBranch=heads/refs/tags/v6.1.7\" -X \"github.com/pingcap/tidb/util/versioninfo.TiDBEdition=Community\" "`, 1293 wantMajorVersion: "6", 1294 wantFullVersion: "v6.1.7", 1295 }, 1296 { 1297 name: "logstash-exporter", 1298 ldflags: `build -ldflags="-w -s -X github.com/kuskoman/logstash-exporter/config.Version=v1.7.0 -X github.com/kuskoman/logstash-exporter/config.GitCommit=db696dbcfe5a91d288d5ad44ce8ccbea97e65978 -X github.com/kuskoman/logstash-exporter/config.BuildDate=2024-07-17T08:12:17Z"`, 1299 wantMajorVersion: "1", 1300 wantFullVersion: "v1.7.0", 1301 }, 1302 ////////////////////////////////////////////////////////////////// 1303 // negative cases 1304 { 1305 name: "hugo ldflags", 1306 mainModule: "github.com/gohugoio/hugo", 1307 ldflags: ` build -ldflags="-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"`, 1308 }, 1309 { 1310 name: "ghostunnel ldflags", 1311 mainModule: "github.com/ghostunnel/ghostunnel", 1312 ldflags: ` build -ldflags="-X main.version=77d9aaa"`, 1313 }, 1314 { 1315 name: "opa ldflags", 1316 mainModule: "github.com/open-policy-agent/opa", 1317 ldflags: `build -ldflags=" -X github.com/open-policy-agent/opa/version.Hostname=9549178459bc"`, 1318 }, 1319 /////////////////////////////////////////////////////////////////// 1320 // trickier cases 1321 { 1322 name: "macvlan plugin for cri-o ldflags", 1323 mainModule: "github.com/containernetworking/plugins", 1324 ldflags: ` build -ldflags="-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=v1.2.0"`, 1325 wantMajorVersion: "1", 1326 wantFullVersion: "v1.2.0", 1327 }, 1328 { 1329 name: "coder ldflags", 1330 mainModule: "github.com/coder/coder", 1331 ldflags: ` build -ldflags="-s -w -X 'github.com/coder/coder/buildinfo.tag=0.23.4'"`, 1332 wantMajorVersion: "0", 1333 wantFullVersion: "v0.23.4", 1334 }, 1335 { 1336 name: "hypothetical multiple versions in ldflags", 1337 mainModule: "github.com/foo/baz", 1338 ldflags: ` build -ldflags="-extldflags -static -X github.com/foo/bar/buildversion.BuildVersion=v1.2.0 -X github.com/foo/baz/buildversion.BuildVersion=v2.4.5"`, 1339 wantMajorVersion: "2", 1340 wantFullVersion: "v2.4.5", 1341 }, 1342 /////////////////////////////////////////////////////////////////// 1343 // don't know how to handle these... yet 1344 //{ 1345 // // package name: pkgName: "github.com/krakendio/krakend-ce/v2", 1346 // name: "krakenD ldflags", 1347 // ldflags: ` build -ldflags="-X github.com/luraproject/lura/v2/core.KrakendVersion=2.3.2 -X github.com/luraproject/lura/v2/core.GoVersion=1.20.4 -X github.com/luraproject/lura/v2/core.GlibcVersion=GLIBC-2.31_(debian-11) "`, 1348 // wantMajorVersion: "2.3.2", 1349 // wantFullVersion: "v2.3.2", 1350 //}, 1351 //{ 1352 // // package name: pkgName: "github.com/krakendio/krakend-ce/v2", 1353 // name: "krakenD ldflags -- answer embedded in the middle", 1354 // ldflags: ` build -ldflags=" -X github.com/luraproject/lura/v2/core.GoVersion=1.20.4 -X github.com/luraproject/lura/v2/core.KrakendVersion=2.3.2 -X github.com/luraproject/lura/v2/core.GlibcVersion=GLIBC-2.31_(debian-11) "`, 1355 // wantMajorVersion: "2.3.2", 1356 // wantFullVersion: "v2.3.2", 1357 //}, 1358 } 1359 for _, tt := range tests { 1360 t.Run(tt.name, func(t *testing.T) { 1361 gotMajorVersion, gotFullVersion := extractVersionFromLDFlags(tt.ldflags, tt.mainModule) 1362 assert.Equal(t, tt.wantMajorVersion, gotMajorVersion, "unexpected major version") 1363 assert.Equal(t, tt.wantFullVersion, gotFullVersion, "unexpected full version") 1364 }) 1365 } 1366 } 1367 1368 func Test_extractVersionFromContents(t *testing.T) { 1369 tests := []struct { 1370 name string 1371 contents io.Reader 1372 want string 1373 }{ 1374 { 1375 name: "empty string on error", 1376 contents: &alwaysErrorReader{}, 1377 want: "", 1378 }, 1379 { 1380 name: "empty string on empty reader", 1381 contents: bytes.NewReader([]byte{}), 1382 want: "", 1383 }, 1384 { 1385 name: "null-byte delimited semver", 1386 contents: strings.NewReader("\x001.2.3\x00"), 1387 want: "1.2.3", 1388 }, 1389 { 1390 name: "null-byte delimited semver with v prefix", 1391 contents: strings.NewReader("\x00v1.2.3\x00"), 1392 want: "v1.2.3", 1393 }, 1394 { 1395 // 01a0bfc8: 0e74 5a3b 0000 a04c 7631 2e39 2e35 0000 .tZ;...Lv1.9.5.. from nginx-ingress-controller 1396 // at /nginx-ingress-controller in registry.k8s.io/ingress-nginx/controller:v1.9.5 1397 // digest: sha256:b3aba22b1da80e7acfc52b115cae1d4c687172cbf2b742d5b502419c25ff340e 1398 // TODO: eventually use something for managing snippets, similar to what's used with binary classifier tests 1399 name: "null byte, then random byte, then L then semver", 1400 contents: strings.NewReader("\x0e\x74\x5a\x3b\x00\x00\xa0\x4cv1.9.5\x00\x00"), 1401 want: "v1.9.5", 1402 }, 1403 { 1404 // 06168a34: f98f b0be 332e 312e 3200 0000 636f 6d74 ....3.1.2...comt from /usr/local/bin/traefik 1405 // in traefik:v3.1.2@sha256:3f92eba47bd4bfda91d47b72d16fef2d7ae15db61a92b2057cf0cb389f8938f6 1406 // TODO: eventually use something for managing snippets, similar to what's used with binary classifier tests 1407 name: "parse traefik version", 1408 contents: strings.NewReader("\xf9\x8f\xb0\xbe\x33\x2e\x31\x2e\x32\x00\x00\x00\x63\x6f\x6d\x74"), 1409 want: "3.1.2", 1410 }, 1411 } 1412 for _, tt := range tests { 1413 t.Run(tt.name, func(t *testing.T) { 1414 got := extractVersionFromContents(tt.contents) 1415 assert.Equal(t, tt.want, got) 1416 }) 1417 } 1418 } 1419 1420 type alwaysErrorReader struct{} 1421 1422 func (alwaysErrorReader) Read(_ []byte) (int, error) { 1423 return 0, errors.New("read from always error reader") 1424 }