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  }