github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/golang/parse_go_binary_test.go (about)

     1  package golang
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"io"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"runtime/debug"
    12  	"strconv"
    13  	"strings"
    14  	"syscall"
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/anchore/syft/syft/file"
    21  	"github.com/anchore/syft/syft/internal/fileresolver"
    22  	"github.com/anchore/syft/syft/internal/unionreader"
    23  	"github.com/anchore/syft/syft/pkg"
    24  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    25  )
    26  
    27  // make will run the default make target for the given test fixture path
    28  func runMakeTarget(t *testing.T, fixtureName string) {
    29  	cwd, err := os.Getwd()
    30  	require.NoError(t, err)
    31  	fixtureDir := filepath.Join(cwd, "test-fixtures/", fixtureName)
    32  
    33  	t.Logf("Generating Fixture in %q", fixtureDir)
    34  
    35  	cmd := exec.Command("make")
    36  	cmd.Dir = fixtureDir
    37  
    38  	stderr, err := cmd.StderrPipe()
    39  	require.NoError(t, err)
    40  
    41  	stdout, err := cmd.StdoutPipe()
    42  	require.NoError(t, err)
    43  
    44  	err = cmd.Start()
    45  	require.NoError(t, err)
    46  
    47  	show := func(label string, reader io.ReadCloser) {
    48  		scanner := bufio.NewScanner(reader)
    49  		scanner.Split(bufio.ScanLines)
    50  		for scanner.Scan() {
    51  			t.Logf("%s: %s", label, scanner.Text())
    52  		}
    53  	}
    54  	go show("out", stdout)
    55  	go show("err", stderr)
    56  
    57  	if err := cmd.Wait(); err != nil {
    58  		if exiterr, ok := err.(*exec.ExitError); ok {
    59  			// The program has exited with an exit code != 0
    60  
    61  			// This works on both Unix and Windows. Although package
    62  			// syscall is generally platform dependent, WaitStatus is
    63  			// defined for both Unix and Windows and in both cases has
    64  			// an ExitStatus() method with the same signature.
    65  			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
    66  				if status.ExitStatus() != 0 {
    67  					t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus())
    68  				}
    69  			}
    70  		} else {
    71  			t.Fatalf("unable to get generate fixture result: %+v", err)
    72  		}
    73  	}
    74  }
    75  
    76  func Test_getGOARCHFromBin(t *testing.T) {
    77  	runMakeTarget(t, "archs")
    78  
    79  	tests := []struct {
    80  		name     string
    81  		filepath string
    82  		expected string
    83  	}{
    84  		{
    85  			name:     "pe",
    86  			filepath: "test-fixtures/archs/binaries/hello-win-amd64",
    87  			// see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
    88  			expected: strconv.Itoa(0x8664),
    89  		},
    90  		{
    91  			name:     "elf-ppc64",
    92  			filepath: "test-fixtures/archs/binaries/hello-linux-ppc64le",
    93  			expected: "ppc64",
    94  		},
    95  		{
    96  			name:     "mach-o-arm64",
    97  			filepath: "test-fixtures/archs/binaries/hello-mach-o-arm64",
    98  			expected: "arm64",
    99  		},
   100  		{
   101  			name:     "linux-arm",
   102  			filepath: "test-fixtures/archs/binaries/hello-linux-arm",
   103  			expected: "arm",
   104  		},
   105  		{
   106  			name:     "xcoff-32bit",
   107  			filepath: "internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec",
   108  			expected: strconv.Itoa(0x1DF),
   109  		},
   110  		{
   111  			name:     "xcoff-64bit",
   112  			filepath: "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec",
   113  			expected: strconv.Itoa(0x1F7),
   114  		},
   115  	}
   116  
   117  	for _, tt := range tests {
   118  		f, err := os.Open(tt.filepath)
   119  		require.NoError(t, err)
   120  		arch, err := getGOARCHFromBin(f)
   121  		require.NoError(t, err, "test name: %s", tt.name)
   122  		assert.Equal(t, tt.expected, arch)
   123  	}
   124  
   125  }
   126  
   127  func TestBuildGoPkgInfo(t *testing.T) {
   128  	const (
   129  		goCompiledVersion = "1.18"
   130  		archDetails       = "amd64"
   131  	)
   132  
   133  	defaultBuildSettings := []pkg.KeyValue{
   134  		{
   135  			Key:   "GOARCH",
   136  			Value: "amd64",
   137  		},
   138  		{
   139  			Key:   "GOOS",
   140  			Value: "darwin",
   141  		},
   142  		{
   143  			Key:   "GOAMD64",
   144  			Value: "v1",
   145  		},
   146  	}
   147  
   148  	unmodifiedMain := pkg.Package{
   149  		Name:     "github.com/anchore/syft",
   150  		Language: pkg.Go,
   151  		Type:     pkg.GoModulePkg,
   152  		Version:  "(devel)",
   153  		PURL:     "pkg:golang/github.com/anchore/syft@(devel)",
   154  		Locations: file.NewLocationSet(
   155  			file.NewLocationFromCoordinates(
   156  				file.Coordinates{
   157  					RealPath:     "/a-path",
   158  					FileSystemID: "layer-id",
   159  				},
   160  			).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   161  		),
   162  		Metadata: pkg.GolangBinaryBuildinfoEntry{
   163  			GoCompiledVersion: goCompiledVersion,
   164  			Architecture:      archDetails,
   165  			BuildSettings:     defaultBuildSettings,
   166  			MainModule:        "github.com/anchore/syft",
   167  		},
   168  	}
   169  
   170  	tests := []struct {
   171  		name          string
   172  		mod           *extendedBuildInfo
   173  		expected      []pkg.Package
   174  		binaryContent string
   175  	}{
   176  		{
   177  			name: "package without name",
   178  			mod: &extendedBuildInfo{
   179  				BuildInfo: &debug.BuildInfo{
   180  					Deps: []*debug.Module{
   181  						{
   182  							Path: "github.com/adrg/xdg",
   183  						},
   184  						{
   185  							Path:    "",
   186  							Version: "v0.2.1",
   187  						},
   188  					},
   189  				},
   190  				cryptoSettings: nil,
   191  				arch:           "",
   192  			},
   193  			expected: []pkg.Package{
   194  				{
   195  					Name:     "github.com/adrg/xdg",
   196  					PURL:     "pkg:golang/github.com/adrg/xdg",
   197  					Language: pkg.Go,
   198  					Type:     pkg.GoModulePkg,
   199  					Locations: file.NewLocationSet(
   200  						file.NewLocationFromCoordinates(
   201  							file.Coordinates{
   202  								RealPath:     "/a-path",
   203  								FileSystemID: "layer-id",
   204  							},
   205  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   206  					),
   207  					Metadata: pkg.GolangBinaryBuildinfoEntry{},
   208  				},
   209  			},
   210  		},
   211  		{
   212  			name:     "buildGoPkgInfo parses a blank mod and returns no packages",
   213  			mod:      &extendedBuildInfo{&debug.BuildInfo{}, nil, ""},
   214  			expected: []pkg.Package(nil),
   215  		},
   216  		{
   217  			name: "parse a mod without main module",
   218  			mod: &extendedBuildInfo{
   219  				BuildInfo: &debug.BuildInfo{
   220  					GoVersion: goCompiledVersion,
   221  					Settings: []debug.BuildSetting{
   222  						{Key: "GOARCH", Value: archDetails},
   223  						{Key: "GOOS", Value: "darwin"},
   224  						{Key: "GOAMD64", Value: "v1"},
   225  					},
   226  					Deps: []*debug.Module{
   227  						{
   228  							Path:    "github.com/adrg/xdg",
   229  							Version: "v0.2.1",
   230  							Sum:     "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   231  						},
   232  					},
   233  				},
   234  				cryptoSettings: nil,
   235  				arch:           archDetails,
   236  			},
   237  			expected: []pkg.Package{
   238  				{
   239  					Name:     "github.com/adrg/xdg",
   240  					Version:  "v0.2.1",
   241  					PURL:     "pkg:golang/github.com/adrg/xdg@v0.2.1",
   242  					Language: pkg.Go,
   243  					Type:     pkg.GoModulePkg,
   244  					Locations: file.NewLocationSet(
   245  						file.NewLocationFromCoordinates(
   246  							file.Coordinates{
   247  								RealPath:     "/a-path",
   248  								FileSystemID: "layer-id",
   249  							},
   250  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   251  					),
   252  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   253  						GoCompiledVersion: goCompiledVersion,
   254  						Architecture:      archDetails,
   255  						H1Digest:          "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   256  					},
   257  				},
   258  			},
   259  		},
   260  		{
   261  			name: "parse a mod with path but no main module",
   262  			mod: &extendedBuildInfo{
   263  				BuildInfo: &debug.BuildInfo{
   264  					GoVersion: goCompiledVersion,
   265  					Settings: []debug.BuildSetting{
   266  						{Key: "GOARCH", Value: archDetails},
   267  						{Key: "GOOS", Value: "darwin"},
   268  						{Key: "GOAMD64", Value: "v1"},
   269  					},
   270  					Path: "github.com/a/b/c",
   271  				},
   272  				cryptoSettings: []string{"boringcrypto + fips"},
   273  				arch:           archDetails,
   274  			},
   275  			expected: []pkg.Package{
   276  				{
   277  					Name:     "github.com/a/b/c",
   278  					Version:  "(devel)",
   279  					PURL:     "pkg:golang/github.com/a/b@(devel)#c",
   280  					Language: pkg.Go,
   281  					Type:     pkg.GoModulePkg,
   282  					Locations: file.NewLocationSet(
   283  						file.NewLocationFromCoordinates(
   284  							file.Coordinates{
   285  								RealPath:     "/a-path",
   286  								FileSystemID: "layer-id",
   287  							},
   288  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   289  					),
   290  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   291  						GoCompiledVersion: goCompiledVersion,
   292  						Architecture:      archDetails,
   293  						H1Digest:          "",
   294  						BuildSettings: []pkg.KeyValue{
   295  							{
   296  								Key:   "GOARCH",
   297  								Value: archDetails,
   298  							},
   299  							{
   300  								Key:   "GOOS",
   301  								Value: "darwin",
   302  							},
   303  							{
   304  								Key:   "GOAMD64",
   305  								Value: "v1",
   306  							},
   307  						},
   308  						MainModule:       "github.com/a/b/c",
   309  						GoCryptoSettings: []string{"boringcrypto + fips"},
   310  					},
   311  				},
   312  			},
   313  		},
   314  		{
   315  			name: "parse a mod without packages",
   316  			mod: &extendedBuildInfo{
   317  				BuildInfo: &debug.BuildInfo{
   318  					GoVersion: goCompiledVersion,
   319  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   320  					Settings: []debug.BuildSetting{
   321  						{Key: "GOARCH", Value: archDetails},
   322  						{Key: "GOOS", Value: "darwin"},
   323  						{Key: "GOAMD64", Value: "v1"},
   324  					},
   325  				},
   326  				cryptoSettings: nil,
   327  				arch:           archDetails,
   328  			},
   329  			expected: []pkg.Package{unmodifiedMain},
   330  		},
   331  		{
   332  			name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)",
   333  			mod: &extendedBuildInfo{
   334  				BuildInfo: &debug.BuildInfo{
   335  					GoVersion: goCompiledVersion,
   336  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   337  					Settings: []debug.BuildSetting{
   338  						{Key: "GOARCH", Value: archDetails},
   339  						{Key: "GOOS", Value: "darwin"},
   340  						{Key: "GOAMD64", Value: "v1"},
   341  						{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
   342  						{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
   343  						{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X blah=foobar`},
   344  					},
   345  				},
   346  				cryptoSettings: nil,
   347  				arch:           archDetails,
   348  			},
   349  			expected: []pkg.Package{
   350  				{
   351  					Name:     "github.com/anchore/syft",
   352  					Language: pkg.Go,
   353  					Type:     pkg.GoModulePkg,
   354  					Version:  "v0.0.0-20221014195457-41bc6bb41035",
   355  					PURL:     "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035",
   356  					Locations: file.NewLocationSet(
   357  						file.NewLocationFromCoordinates(
   358  							file.Coordinates{
   359  								RealPath:     "/a-path",
   360  								FileSystemID: "layer-id",
   361  							},
   362  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   363  					),
   364  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   365  						GoCompiledVersion: goCompiledVersion,
   366  						Architecture:      archDetails,
   367  						BuildSettings: []pkg.KeyValue{
   368  							{
   369  								Key:   "GOARCH",
   370  								Value: archDetails,
   371  							},
   372  							{
   373  								Key:   "GOOS",
   374  								Value: "darwin",
   375  							},
   376  							{
   377  								Key:   "GOAMD64",
   378  								Value: "v1",
   379  							},
   380  							{
   381  								Key:   "vcs.revision",
   382  								Value: "41bc6bb410352845f22766e27dd48ba93aa825a4",
   383  							},
   384  							{
   385  								Key:   "vcs.time",
   386  								Value: "2022-10-14T19:54:57Z",
   387  							},
   388  							{
   389  								Key:   "-ldflags",
   390  								Value: `build	-ldflags="-w -s -extldflags '-static' -X blah=foobar`,
   391  							},
   392  						},
   393  						MainModule: "github.com/anchore/syft",
   394  					},
   395  				},
   396  			},
   397  		},
   398  		{
   399  			name: "parse main mod and replace devel version with one from ldflags with vcs. build settings",
   400  			mod: &extendedBuildInfo{
   401  				BuildInfo: &debug.BuildInfo{
   402  					GoVersion: goCompiledVersion,
   403  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   404  					Settings: []debug.BuildSetting{
   405  						{Key: "GOARCH", Value: archDetails},
   406  						{Key: "GOOS", Value: "darwin"},
   407  						{Key: "GOAMD64", Value: "v1"},
   408  						{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
   409  						{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
   410  						{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`},
   411  					},
   412  				},
   413  				cryptoSettings: nil,
   414  				arch:           archDetails,
   415  			},
   416  			expected: []pkg.Package{
   417  				{
   418  					Name:     "github.com/anchore/syft",
   419  					Language: pkg.Go,
   420  					Type:     pkg.GoModulePkg,
   421  					Version:  "v0.79.0",
   422  					PURL:     "pkg:golang/github.com/anchore/syft@v0.79.0",
   423  					Locations: file.NewLocationSet(
   424  						file.NewLocationFromCoordinates(
   425  							file.Coordinates{
   426  								RealPath:     "/a-path",
   427  								FileSystemID: "layer-id",
   428  							},
   429  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   430  					),
   431  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   432  						GoCompiledVersion: goCompiledVersion,
   433  						Architecture:      archDetails,
   434  						BuildSettings: []pkg.KeyValue{
   435  							{
   436  								Key:   "GOARCH",
   437  								Value: archDetails,
   438  							},
   439  							{
   440  								Key:   "GOOS",
   441  								Value: "darwin",
   442  							},
   443  							{
   444  								Key:   "GOAMD64",
   445  								Value: "v1",
   446  							},
   447  							{
   448  								Key:   "vcs.revision",
   449  								Value: "41bc6bb410352845f22766e27dd48ba93aa825a4",
   450  							},
   451  							{
   452  								Key:   "vcs.time",
   453  								Value: "2022-10-14T19:54:57Z",
   454  							},
   455  							{
   456  								Key:   "-ldflags",
   457  								Value: `build	-ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`,
   458  							},
   459  						},
   460  						MainModule: "github.com/anchore/syft",
   461  					},
   462  				},
   463  			},
   464  		},
   465  		{
   466  			name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings",
   467  			mod: &extendedBuildInfo{
   468  				BuildInfo: &debug.BuildInfo{
   469  					GoVersion: goCompiledVersion,
   470  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   471  					Settings: []debug.BuildSetting{
   472  						{Key: "GOARCH", Value: archDetails},
   473  						{Key: "GOOS", Value: "darwin"},
   474  						{Key: "GOAMD64", Value: "v1"},
   475  						{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`},
   476  					},
   477  				},
   478  				cryptoSettings: nil,
   479  				arch:           archDetails,
   480  			},
   481  			expected: []pkg.Package{
   482  				{
   483  					Name:     "github.com/anchore/syft",
   484  					Language: pkg.Go,
   485  					Type:     pkg.GoModulePkg,
   486  					Version:  "v0.79.0",
   487  					PURL:     "pkg:golang/github.com/anchore/syft@v0.79.0",
   488  					Locations: file.NewLocationSet(
   489  						file.NewLocationFromCoordinates(
   490  							file.Coordinates{
   491  								RealPath:     "/a-path",
   492  								FileSystemID: "layer-id",
   493  							},
   494  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   495  					),
   496  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   497  						GoCompiledVersion: goCompiledVersion,
   498  						Architecture:      archDetails,
   499  						BuildSettings: []pkg.KeyValue{
   500  							{
   501  								Key:   "GOARCH",
   502  								Value: archDetails,
   503  							},
   504  							{
   505  								Key:   "GOOS",
   506  								Value: "darwin",
   507  							},
   508  							{
   509  								Key:   "GOAMD64",
   510  								Value: "v1",
   511  							},
   512  							{
   513  								Key:   "-ldflags",
   514  								Value: `build	-ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`,
   515  							},
   516  						},
   517  						MainModule: "github.com/anchore/syft",
   518  					},
   519  				},
   520  			},
   521  		},
   522  		{
   523  			name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings",
   524  			mod: &extendedBuildInfo{
   525  				BuildInfo: &debug.BuildInfo{
   526  					GoVersion: goCompiledVersion,
   527  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   528  					Settings: []debug.BuildSetting{
   529  						{Key: "GOARCH", Value: archDetails},
   530  						{Key: "GOOS", Value: "darwin"},
   531  						{Key: "GOAMD64", Value: "v1"},
   532  						{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`},
   533  					},
   534  				},
   535  				cryptoSettings: nil,
   536  				arch:           archDetails,
   537  			},
   538  			expected: []pkg.Package{
   539  				{
   540  					Name:     "github.com/anchore/syft",
   541  					Language: pkg.Go,
   542  					Type:     pkg.GoModulePkg,
   543  					Version:  "v0.79.0",
   544  					PURL:     "pkg:golang/github.com/anchore/syft@v0.79.0",
   545  					Locations: file.NewLocationSet(
   546  						file.NewLocationFromCoordinates(
   547  							file.Coordinates{
   548  								RealPath:     "/a-path",
   549  								FileSystemID: "layer-id",
   550  							},
   551  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   552  					),
   553  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   554  						GoCompiledVersion: goCompiledVersion,
   555  						Architecture:      archDetails,
   556  						BuildSettings: []pkg.KeyValue{
   557  							{
   558  								Key:   "GOARCH",
   559  								Value: archDetails,
   560  							},
   561  							{
   562  								Key:   "GOOS",
   563  								Value: "darwin",
   564  							},
   565  							{
   566  								Key:   "GOAMD64",
   567  								Value: "v1",
   568  							},
   569  							{
   570  								Key:   "-ldflags",
   571  								Value: `build	-ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`,
   572  							},
   573  						},
   574  						MainModule: "github.com/anchore/syft",
   575  					},
   576  				},
   577  			},
   578  		},
   579  		{
   580  			name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings",
   581  			mod: &extendedBuildInfo{
   582  				BuildInfo: &debug.BuildInfo{
   583  					GoVersion: goCompiledVersion,
   584  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   585  					Settings: []debug.BuildSetting{
   586  						{Key: "GOARCH", Value: archDetails},
   587  						{Key: "GOOS", Value: "darwin"},
   588  						{Key: "GOAMD64", Value: "v1"},
   589  						{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`},
   590  					},
   591  				},
   592  				cryptoSettings: nil,
   593  				arch:           archDetails,
   594  			},
   595  			expected: []pkg.Package{
   596  				{
   597  					Name:     "github.com/anchore/syft",
   598  					Language: pkg.Go,
   599  					Type:     pkg.GoModulePkg,
   600  					Version:  "v0.79.0",
   601  					PURL:     "pkg:golang/github.com/anchore/syft@v0.79.0",
   602  					Locations: file.NewLocationSet(
   603  						file.NewLocationFromCoordinates(
   604  							file.Coordinates{
   605  								RealPath:     "/a-path",
   606  								FileSystemID: "layer-id",
   607  							},
   608  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   609  					),
   610  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   611  						GoCompiledVersion: goCompiledVersion,
   612  						Architecture:      archDetails,
   613  						BuildSettings: []pkg.KeyValue{
   614  							{
   615  								Key:   "GOARCH",
   616  								Value: archDetails,
   617  							},
   618  							{
   619  								Key:   "GOOS",
   620  								Value: "darwin",
   621  							},
   622  							{
   623  								Key:   "GOAMD64",
   624  								Value: "v1",
   625  							},
   626  							{
   627  								Key:   "-ldflags",
   628  								Value: `build	-ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`,
   629  							},
   630  						},
   631  						MainModule: "github.com/anchore/syft",
   632  					},
   633  				},
   634  			},
   635  		},
   636  		{
   637  			name: "parse main mod and replace devel version with a pseudo version",
   638  			mod: &extendedBuildInfo{
   639  				BuildInfo: &debug.BuildInfo{
   640  					GoVersion: goCompiledVersion,
   641  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   642  					Settings: []debug.BuildSetting{
   643  						{Key: "GOARCH", Value: archDetails},
   644  						{Key: "GOOS", Value: "darwin"},
   645  						{Key: "GOAMD64", Value: "v1"},
   646  						{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
   647  						{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
   648  					},
   649  				},
   650  				cryptoSettings: nil,
   651  				arch:           archDetails,
   652  			},
   653  			expected: []pkg.Package{
   654  				{
   655  					Name:     "github.com/anchore/syft",
   656  					Language: pkg.Go,
   657  					Type:     pkg.GoModulePkg,
   658  					Version:  "v0.0.0-20221014195457-41bc6bb41035",
   659  					PURL:     "pkg:golang/github.com/anchore/syft@v0.0.0-20221014195457-41bc6bb41035",
   660  					Locations: file.NewLocationSet(
   661  						file.NewLocationFromCoordinates(
   662  							file.Coordinates{
   663  								RealPath:     "/a-path",
   664  								FileSystemID: "layer-id",
   665  							},
   666  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   667  					),
   668  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   669  						GoCompiledVersion: goCompiledVersion,
   670  						Architecture:      archDetails,
   671  						BuildSettings: []pkg.KeyValue{
   672  							{
   673  								Key:   "GOARCH",
   674  								Value: archDetails,
   675  							},
   676  							{
   677  								Key:   "GOOS",
   678  								Value: "darwin",
   679  							},
   680  							{
   681  								Key:   "GOAMD64",
   682  								Value: "v1",
   683  							},
   684  							{
   685  								Key:   "vcs.revision",
   686  								Value: "41bc6bb410352845f22766e27dd48ba93aa825a4",
   687  							},
   688  							{
   689  								Key:   "vcs.time",
   690  								Value: "2022-10-14T19:54:57Z",
   691  							},
   692  						},
   693  						MainModule: "github.com/anchore/syft",
   694  					},
   695  				},
   696  			},
   697  		},
   698  		{
   699  			name: "parse a populated mod string and returns packages but no source info",
   700  			mod: &extendedBuildInfo{
   701  				BuildInfo: &debug.BuildInfo{
   702  					GoVersion: goCompiledVersion,
   703  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   704  					Settings: []debug.BuildSetting{
   705  						{Key: "GOARCH", Value: archDetails},
   706  						{Key: "GOOS", Value: "darwin"},
   707  						{Key: "GOAMD64", Value: "v1"},
   708  					},
   709  					Deps: []*debug.Module{
   710  						{
   711  							Path:    "github.com/adrg/xdg",
   712  							Version: "v0.2.1",
   713  							Sum:     "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   714  						},
   715  						{
   716  							Path:    "github.com/anchore/client-go",
   717  							Version: "v0.0.0-20210222170800-9c70f9b80bcf",
   718  							Sum:     "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
   719  						},
   720  					},
   721  				},
   722  				cryptoSettings: nil,
   723  				arch:           archDetails,
   724  			},
   725  			expected: []pkg.Package{
   726  				{
   727  					Name:     "github.com/adrg/xdg",
   728  					Version:  "v0.2.1",
   729  					PURL:     "pkg:golang/github.com/adrg/xdg@v0.2.1",
   730  					Language: pkg.Go,
   731  					Type:     pkg.GoModulePkg,
   732  					Locations: file.NewLocationSet(
   733  						file.NewLocationFromCoordinates(
   734  							file.Coordinates{
   735  								RealPath:     "/a-path",
   736  								FileSystemID: "layer-id",
   737  							},
   738  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   739  					),
   740  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   741  						GoCompiledVersion: goCompiledVersion,
   742  						Architecture:      archDetails,
   743  						H1Digest:          "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   744  						MainModule:        "github.com/anchore/syft",
   745  					},
   746  				},
   747  				{
   748  					Name:     "github.com/anchore/client-go",
   749  					Version:  "v0.0.0-20210222170800-9c70f9b80bcf",
   750  					PURL:     "pkg:golang/github.com/anchore/client-go@v0.0.0-20210222170800-9c70f9b80bcf",
   751  					Language: pkg.Go,
   752  					Type:     pkg.GoModulePkg,
   753  					Locations: file.NewLocationSet(
   754  						file.NewLocationFromCoordinates(
   755  							file.Coordinates{
   756  								RealPath:     "/a-path",
   757  								FileSystemID: "layer-id",
   758  							},
   759  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   760  					),
   761  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   762  						GoCompiledVersion: goCompiledVersion,
   763  						Architecture:      archDetails,
   764  						H1Digest:          "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
   765  						MainModule:        "github.com/anchore/syft",
   766  					},
   767  				},
   768  				unmodifiedMain,
   769  			},
   770  		},
   771  		{
   772  			name: "parse a populated mod string and returns packages when a replace directive exists",
   773  			mod: &extendedBuildInfo{
   774  				BuildInfo: &debug.BuildInfo{
   775  					GoVersion: goCompiledVersion,
   776  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   777  					Settings: []debug.BuildSetting{
   778  						{Key: "GOARCH", Value: archDetails},
   779  						{Key: "GOOS", Value: "darwin"},
   780  						{Key: "GOAMD64", Value: "v1"},
   781  					},
   782  					Deps: []*debug.Module{
   783  						{
   784  							Path:    "golang.org/x/sys",
   785  							Version: "v0.0.0-20211006194710-c8a6f5223071",
   786  							Sum:     "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
   787  						},
   788  						{
   789  							Path:    "golang.org/x/term",
   790  							Version: "v0.0.0-20210927222741-03fcf44c2211",
   791  							Sum:     "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
   792  							Replace: &debug.Module{
   793  								Path:    "golang.org/x/term",
   794  								Version: "v0.0.0-20210916214954-140adaaadfaf",
   795  								Sum:     "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
   796  							},
   797  						},
   798  					},
   799  				},
   800  				cryptoSettings: nil,
   801  				arch:           archDetails,
   802  			},
   803  			expected: []pkg.Package{
   804  				{
   805  					Name:     "golang.org/x/sys",
   806  					Version:  "v0.0.0-20211006194710-c8a6f5223071",
   807  					PURL:     "pkg:golang/golang.org/x/sys@v0.0.0-20211006194710-c8a6f5223071",
   808  					Language: pkg.Go,
   809  					Type:     pkg.GoModulePkg,
   810  					Locations: file.NewLocationSet(
   811  						file.NewLocationFromCoordinates(
   812  							file.Coordinates{
   813  								RealPath:     "/a-path",
   814  								FileSystemID: "layer-id",
   815  							},
   816  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   817  					),
   818  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   819  						GoCompiledVersion: goCompiledVersion,
   820  						Architecture:      archDetails,
   821  						H1Digest:          "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
   822  						MainModule:        "github.com/anchore/syft",
   823  					}},
   824  				{
   825  					Name:     "golang.org/x/term",
   826  					Version:  "v0.0.0-20210916214954-140adaaadfaf",
   827  					PURL:     "pkg:golang/golang.org/x/term@v0.0.0-20210916214954-140adaaadfaf",
   828  					Language: pkg.Go,
   829  					Type:     pkg.GoModulePkg,
   830  					Locations: file.NewLocationSet(
   831  						file.NewLocationFromCoordinates(
   832  							file.Coordinates{
   833  								RealPath:     "/a-path",
   834  								FileSystemID: "layer-id",
   835  							},
   836  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   837  					),
   838  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   839  						GoCompiledVersion: goCompiledVersion,
   840  						Architecture:      archDetails,
   841  						H1Digest:          "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
   842  						MainModule:        "github.com/anchore/syft",
   843  					},
   844  				},
   845  				unmodifiedMain,
   846  			},
   847  		},
   848  		{
   849  			name: "parse main mod and replace devel with pattern from binary contents",
   850  			mod: &extendedBuildInfo{
   851  				BuildInfo: &debug.BuildInfo{
   852  					GoVersion: goCompiledVersion,
   853  					Main:      debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
   854  					Settings: []debug.BuildSetting{
   855  						{Key: "GOARCH", Value: archDetails},
   856  						{Key: "GOOS", Value: "darwin"},
   857  						{Key: "GOAMD64", Value: "v1"},
   858  						{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, // important! missing revision
   859  						{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X blah=foobar`},
   860  					},
   861  				},
   862  				cryptoSettings: nil,
   863  				arch:           archDetails,
   864  			},
   865  			binaryContent: "\x00v1.0.0-somethingelse+incompatible\x00",
   866  			expected: []pkg.Package{
   867  				{
   868  					Name:     "github.com/anchore/syft",
   869  					Language: pkg.Go,
   870  					Type:     pkg.GoModulePkg,
   871  					Version:  "v1.0.0-somethingelse+incompatible",
   872  					PURL:     "pkg:golang/github.com/anchore/syft@v1.0.0-somethingelse%2Bincompatible",
   873  					Locations: file.NewLocationSet(
   874  						file.NewLocationFromCoordinates(
   875  							file.Coordinates{
   876  								RealPath:     "/a-path",
   877  								FileSystemID: "layer-id",
   878  							},
   879  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   880  					),
   881  					Metadata: pkg.GolangBinaryBuildinfoEntry{
   882  						GoCompiledVersion: goCompiledVersion,
   883  						Architecture:      archDetails,
   884  						BuildSettings: []pkg.KeyValue{
   885  							{
   886  								Key:   "GOARCH",
   887  								Value: archDetails,
   888  							},
   889  							{
   890  								Key:   "GOOS",
   891  								Value: "darwin",
   892  							},
   893  							{
   894  								Key:   "GOAMD64",
   895  								Value: "v1",
   896  							},
   897  							{
   898  								Key:   "vcs.time",
   899  								Value: "2022-10-14T19:54:57Z",
   900  							},
   901  							{
   902  								Key:   "-ldflags",
   903  								Value: `build	-ldflags="-w -s -extldflags '-static' -X blah=foobar`,
   904  							},
   905  						},
   906  						MainModule: "github.com/anchore/syft",
   907  					},
   908  				},
   909  			},
   910  		},
   911  	}
   912  
   913  	for _, test := range tests {
   914  		t.Run(test.name, func(t *testing.T) {
   915  			for i := range test.expected {
   916  				p := &test.expected[i]
   917  				p.SetID()
   918  			}
   919  			location := file.NewLocationFromCoordinates(
   920  				file.Coordinates{
   921  					RealPath:     "/a-path",
   922  					FileSystemID: "layer-id",
   923  				},
   924  			)
   925  
   926  			c := newGoBinaryCataloger(DefaultCatalogerConfig())
   927  			reader, err := unionreader.GetUnionReader(io.NopCloser(strings.NewReader(test.binaryContent)))
   928  			require.NoError(t, err)
   929  			pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.mod.arch, reader)
   930  			require.Len(t, pkgs, len(test.expected))
   931  			for i, p := range pkgs {
   932  				pkgtest.AssertPackagesEqual(t, test.expected[i], p)
   933  			}
   934  		})
   935  	}
   936  }
   937  
   938  func Test_extractVersionFromLDFlags(t *testing.T) {
   939  	tests := []struct {
   940  		name             string
   941  		ldflags          string
   942  		wantMajorVersion string
   943  		wantFullVersion  string
   944  	}{
   945  		{
   946  			name:    "empty ldflags",
   947  			ldflags: "",
   948  		},
   949  		{
   950  			name:             "syft ldflags",
   951  			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 "`,
   952  			wantMajorVersion: "0",
   953  			wantFullVersion:  "v0.79.0",
   954  		},
   955  		{
   956  			name: "kubectl ldflags",
   957  			ldflags: `	build	-asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes
   958  	build	-compiler=gc
   959  	build	-gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes "
   960  	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"`,
   961  			wantMajorVersion: "1",
   962  			wantFullVersion:  "v1.25.9",
   963  		},
   964  		{
   965  			name:             "nerdctl ldflags",
   966  			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"`,
   967  			wantMajorVersion: "1",
   968  			wantFullVersion:  "v1.3.1",
   969  		},
   970  		{
   971  			name:             "limactl ldflags",
   972  			ldflags:          `	build	-ldflags="-s -w -X github.com/lima-vm/lima/pkg/version.Version=v0.15.1"`,
   973  			wantMajorVersion: "0",
   974  			wantFullVersion:  "v0.15.1",
   975  		},
   976  		{
   977  			name:             "terraform ldflags",
   978  			ldflags:          `	build	-ldflags="-w -s -X 'github.com/hashicorp/terraform/version.Version=1.4.6' -X 'github.com/hashicorp/terraform/version.Prerelease='"`,
   979  			wantMajorVersion: "1",
   980  			wantFullVersion:  "v1.4.6",
   981  		},
   982  		{
   983  			name: "kube-apiserver ldflags",
   984  			ldflags: `	build	-asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes
   985  	build	-buildmode=exe
   986  	build	-compiler=gc
   987  	build	-gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes "
   988  	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"`,
   989  			wantMajorVersion: "1",
   990  			wantFullVersion:  "v1.27.1",
   991  		},
   992  		{
   993  			name: "prometheus ldflags",
   994  			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'"
   995  	build	-tags=netgo,builtinassets,stringlabels`,
   996  			wantMajorVersion: "2",
   997  			wantFullVersion:  "v2.44.0",
   998  		},
   999  		{
  1000  			name: "influxdb ldflags",
  1001  			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'"
  1002  	build	-tags=assets,sqlite_foreign_keys,sqlite_json,static_build,noasm`,
  1003  			wantMajorVersion: "2",
  1004  			wantFullVersion:  "v2.7.1",
  1005  		},
  1006  		{
  1007  			name:             "gitea ldflags",
  1008  			ldflags:          `	build	-ldflags=" -X \"main.MakeVersion=GNU Make 4.1\" -X \"main.Version=1.19.3\" -X \"main.Tags=bindata sqlite sqlite_unlock_notify\" "`,
  1009  			wantMajorVersion: "1",
  1010  			wantFullVersion:  "v1.19.3",
  1011  		},
  1012  		{
  1013  			name:             "docker sbom cli ldflags",
  1014  			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 "`,
  1015  			wantMajorVersion: "0",
  1016  			wantFullVersion:  "v0.6.1-SNAPSHOT-02cf1c8",
  1017  		},
  1018  		{
  1019  			name:             "docker scout ldflags",
  1020  			ldflags:          `	build	-ldflags="-w -s -extldflags '-static' -X github.com/docker/scout-cli-plugin/internal.version=0.10.0 "`,
  1021  			wantMajorVersion: "0",
  1022  			wantFullVersion:  "v0.10.0",
  1023  		},
  1024  		{
  1025  			name:             "influx telegraf ldflags",
  1026  			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"`,
  1027  			wantMajorVersion: "1",
  1028  			wantFullVersion:  "v1.26.2",
  1029  		},
  1030  		{
  1031  			name:             "argocd ldflags",
  1032  			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\""`,
  1033  			wantMajorVersion: "2",
  1034  			wantFullVersion:  "v2.7.2",
  1035  		},
  1036  		{
  1037  			name:             "kustomize ldflags",
  1038  			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 "`,
  1039  			wantMajorVersion: "4",
  1040  			wantFullVersion:  "v4.5.7",
  1041  		},
  1042  		//////////////////////////////////////////////////////////////////
  1043  		// negative cases
  1044  		{
  1045  			name:    "hugo ldflags",
  1046  			ldflags: `	build	-ldflags="-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"`,
  1047  		},
  1048  		{
  1049  			name:    "ghostunnel ldflags",
  1050  			ldflags: `	build	-ldflags="-X main.version=77d9aaa"`,
  1051  		},
  1052  		{
  1053  			name:    "opa ldflags",
  1054  			ldflags: `build	-ldflags=" -X github.com/open-policy-agent/opa/version.Hostname=9549178459bc"`,
  1055  		},
  1056  		///////////////////////////////////////////////////////////////////
  1057  		// trickier cases
  1058  		{
  1059  			name:             "macvlan plugin for cri-o ldflags",
  1060  			ldflags:          `	build	-ldflags="-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=v1.2.0"`,
  1061  			wantMajorVersion: "1",
  1062  			wantFullVersion:  "v1.2.0",
  1063  		},
  1064  		{
  1065  			name:             "coder ldflags",
  1066  			ldflags:          `	build	-ldflags="-s -w -X 'github.com/coder/coder/buildinfo.tag=0.23.4'"`,
  1067  			wantMajorVersion: "0",
  1068  			wantFullVersion:  "v0.23.4",
  1069  		},
  1070  		///////////////////////////////////////////////////////////////////
  1071  		// don't know how to handle these... yet
  1072  		//{
  1073  		//	// package name: pkgName: "github.com/krakendio/krakend-ce/v2",
  1074  		//	name:             "krakenD ldflags",
  1075  		//	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) "`,
  1076  		//	wantMajorVersion: "2.3.2",
  1077  		//	wantFullVersion:  "v2.3.2",
  1078  		//},
  1079  		//{
  1080  		//	// package name: pkgName: "github.com/krakendio/krakend-ce/v2",
  1081  		//	name:             "krakenD ldflags -- answer embedded in the middle",
  1082  		//	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) "`,
  1083  		//	wantMajorVersion: "2.3.2",
  1084  		//	wantFullVersion:  "v2.3.2",
  1085  		//},
  1086  	}
  1087  	for _, tt := range tests {
  1088  		t.Run(tt.name, func(t *testing.T) {
  1089  			gotMajorVersion, gotFullVersion := extractVersionFromLDFlags(tt.ldflags)
  1090  			assert.Equal(t, tt.wantMajorVersion, gotMajorVersion, "unexpected major version")
  1091  			assert.Equal(t, tt.wantFullVersion, gotFullVersion, "unexpected full version")
  1092  		})
  1093  	}
  1094  }
  1095  
  1096  func Test_extractVersionFromContents(t *testing.T) {
  1097  	tests := []struct {
  1098  		name     string
  1099  		contents io.Reader
  1100  		want     string
  1101  	}{
  1102  		{
  1103  			name:     "empty string on error",
  1104  			contents: &alwaysErrorReader{},
  1105  			want:     "",
  1106  		},
  1107  		{
  1108  			name:     "empty string on empty reader",
  1109  			contents: bytes.NewReader([]byte{}),
  1110  			want:     "",
  1111  		},
  1112  		{
  1113  			name:     "null-byte delimited semver",
  1114  			contents: strings.NewReader("\x001.2.3\x00"),
  1115  			want:     "1.2.3",
  1116  		},
  1117  		{
  1118  			name:     "null-byte delimited semver with v prefix",
  1119  			contents: strings.NewReader("\x00v1.2.3\x00"),
  1120  			want:     "v1.2.3",
  1121  		},
  1122  		{
  1123  			// 01a0bfc8: 0e74 5a3b 0000 a04c 7631 2e39 2e35 0000  .tZ;...Lv1.9.5.. from nginx-ingress-controller
  1124  			// at /nginx-ingress-controller in registry.k8s.io/ingress-nginx/controller:v1.9.5
  1125  			// digest: sha256:b3aba22b1da80e7acfc52b115cae1d4c687172cbf2b742d5b502419c25ff340e
  1126  			// TODO: eventually use something for managing snippets, similar to what's used with binary classifier tests
  1127  			name:     "null byte, then random byte, then L then semver",
  1128  			contents: strings.NewReader("\x0e\x74\x5a\x3b\x00\x00\xa0\x4cv1.9.5\x00\x00"),
  1129  			want:     "v1.9.5",
  1130  		},
  1131  	}
  1132  	for _, tt := range tests {
  1133  		t.Run(tt.name, func(t *testing.T) {
  1134  			got := extractVersionFromContents(tt.contents)
  1135  			assert.Equal(t, tt.want, got)
  1136  		})
  1137  	}
  1138  }
  1139  
  1140  type alwaysErrorReader struct{}
  1141  
  1142  func (alwaysErrorReader) Read(_ []byte) (int, error) {
  1143  	return 0, errors.New("read from always error reader")
  1144  }