github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/golang/parse_go_binary_test.go (about)

     1  package golang
     2  
     3  import (
     4  	"bufio"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"runtime/debug"
    10  	"strconv"
    11  	"syscall"
    12  	"testing"
    13  
    14  	"github.com/nextlinux/gosbom/gosbom/file"
    15  	"github.com/nextlinux/gosbom/gosbom/internal/fileresolver"
    16  	"github.com/nextlinux/gosbom/gosbom/pkg"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  // make will run the default make target for the given test fixture path
    22  func runMakeTarget(t *testing.T, fixtureName string) {
    23  	cwd, err := os.Getwd()
    24  	require.NoError(t, err)
    25  	fixtureDir := filepath.Join(cwd, "test-fixtures/", fixtureName)
    26  
    27  	t.Logf("Generating Fixture in %q", fixtureDir)
    28  
    29  	cmd := exec.Command("make")
    30  	cmd.Dir = fixtureDir
    31  
    32  	stderr, err := cmd.StderrPipe()
    33  	require.NoError(t, err)
    34  
    35  	stdout, err := cmd.StdoutPipe()
    36  	require.NoError(t, err)
    37  
    38  	err = cmd.Start()
    39  	require.NoError(t, err)
    40  
    41  	show := func(label string, reader io.ReadCloser) {
    42  		scanner := bufio.NewScanner(reader)
    43  		scanner.Split(bufio.ScanLines)
    44  		for scanner.Scan() {
    45  			t.Logf("%s: %s", label, scanner.Text())
    46  		}
    47  	}
    48  	go show("out", stdout)
    49  	go show("err", stderr)
    50  
    51  	if err := cmd.Wait(); err != nil {
    52  		if exiterr, ok := err.(*exec.ExitError); ok {
    53  			// The program has exited with an exit code != 0
    54  
    55  			// This works on both Unix and Windows. Although package
    56  			// syscall is generally platform dependent, WaitStatus is
    57  			// defined for both Unix and Windows and in both cases has
    58  			// an ExitStatus() method with the same signature.
    59  			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
    60  				if status.ExitStatus() != 0 {
    61  					t.Fatalf("failed to generate fixture: rc=%d", status.ExitStatus())
    62  				}
    63  			}
    64  		} else {
    65  			t.Fatalf("unable to get generate fixture result: %+v", err)
    66  		}
    67  	}
    68  }
    69  
    70  func Test_getGOARCHFromBin(t *testing.T) {
    71  	runMakeTarget(t, "archs")
    72  
    73  	tests := []struct {
    74  		name     string
    75  		filepath string
    76  		expected string
    77  	}{
    78  		{
    79  			name:     "pe",
    80  			filepath: "test-fixtures/archs/binaries/hello-win-amd64",
    81  			// see: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types
    82  			expected: strconv.Itoa(0x8664),
    83  		},
    84  		{
    85  			name:     "elf-ppc64",
    86  			filepath: "test-fixtures/archs/binaries/hello-linux-ppc64le",
    87  			expected: "ppc64",
    88  		},
    89  		{
    90  			name:     "mach-o-arm64",
    91  			filepath: "test-fixtures/archs/binaries/hello-mach-o-arm64",
    92  			expected: "arm64",
    93  		},
    94  		{
    95  			name:     "linux-arm",
    96  			filepath: "test-fixtures/archs/binaries/hello-linux-arm",
    97  			expected: "arm",
    98  		},
    99  		{
   100  			name:     "xcoff-32bit",
   101  			filepath: "internal/xcoff/testdata/gcc-ppc32-aix-dwarf2-exec",
   102  			expected: strconv.Itoa(0x1DF),
   103  		},
   104  		{
   105  			name:     "xcoff-64bit",
   106  			filepath: "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec",
   107  			expected: strconv.Itoa(0x1F7),
   108  		},
   109  	}
   110  
   111  	for _, tt := range tests {
   112  		f, err := os.Open(tt.filepath)
   113  		require.NoError(t, err)
   114  		arch, err := getGOARCHFromBin(f)
   115  		require.NoError(t, err, "test name: %s", tt.name)
   116  		assert.Equal(t, tt.expected, arch)
   117  	}
   118  
   119  }
   120  
   121  func TestBuildGoPkgInfo(t *testing.T) {
   122  	const (
   123  		goCompiledVersion = "1.18"
   124  		archDetails       = "amd64"
   125  	)
   126  	defaultBuildSettings := map[string]string{
   127  		"GOARCH":  "amd64",
   128  		"GOOS":    "darwin",
   129  		"GOAMD64": "v1",
   130  	}
   131  
   132  	unmodifiedMain := pkg.Package{
   133  		Name:     "github.com/nextlinux/gosbom",
   134  		Language: pkg.Go,
   135  		Type:     pkg.GoModulePkg,
   136  		Version:  "(devel)",
   137  		PURL:     "pkg:golang/github.com/nextlinux/gosbom@(devel)",
   138  		Locations: file.NewLocationSet(
   139  			file.NewLocationFromCoordinates(
   140  				file.Coordinates{
   141  					RealPath:     "/a-path",
   142  					FileSystemID: "layer-id",
   143  				},
   144  			).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   145  		),
   146  		MetadataType: pkg.GolangBinMetadataType,
   147  		Metadata: pkg.GolangBinMetadata{
   148  			GoCompiledVersion: goCompiledVersion,
   149  			Architecture:      archDetails,
   150  			BuildSettings:     defaultBuildSettings,
   151  			MainModule:        "github.com/nextlinux/gosbom",
   152  		},
   153  	}
   154  
   155  	tests := []struct {
   156  		name     string
   157  		mod      *debug.BuildInfo
   158  		arch     string
   159  		expected []pkg.Package
   160  	}{
   161  		{
   162  			name:     "parse an empty mod",
   163  			mod:      nil,
   164  			expected: []pkg.Package(nil),
   165  		},
   166  		{
   167  			name: "package without name",
   168  			mod: &debug.BuildInfo{
   169  				Deps: []*debug.Module{
   170  					{
   171  						Path: "github.com/adrg/xdg",
   172  					},
   173  					{
   174  						Path:    "",
   175  						Version: "v0.2.1",
   176  					},
   177  				},
   178  			},
   179  			expected: []pkg.Package{
   180  				{
   181  					Name:     "github.com/adrg/xdg",
   182  					PURL:     "pkg:golang/github.com/adrg/xdg",
   183  					Language: pkg.Go,
   184  					Type:     pkg.GoModulePkg,
   185  					Locations: file.NewLocationSet(
   186  						file.NewLocationFromCoordinates(
   187  							file.Coordinates{
   188  								RealPath:     "/a-path",
   189  								FileSystemID: "layer-id",
   190  							},
   191  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   192  					),
   193  					MetadataType: pkg.GolangBinMetadataType,
   194  					Metadata:     pkg.GolangBinMetadata{},
   195  				},
   196  			},
   197  		},
   198  		{
   199  			name:     "buildGoPkgInfo parses a blank mod and returns no packages",
   200  			mod:      &debug.BuildInfo{},
   201  			expected: []pkg.Package(nil),
   202  		},
   203  		{
   204  			name: "parse a mod without main module",
   205  			arch: archDetails,
   206  			mod: &debug.BuildInfo{
   207  				GoVersion: goCompiledVersion,
   208  				Settings: []debug.BuildSetting{
   209  					{Key: "GOARCH", Value: archDetails},
   210  					{Key: "GOOS", Value: "darwin"},
   211  					{Key: "GOAMD64", Value: "v1"},
   212  				},
   213  				Deps: []*debug.Module{
   214  					{
   215  						Path:    "github.com/adrg/xdg",
   216  						Version: "v0.2.1",
   217  						Sum:     "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   218  					},
   219  				},
   220  			},
   221  			expected: []pkg.Package{
   222  				{
   223  					Name:     "github.com/adrg/xdg",
   224  					Version:  "v0.2.1",
   225  					PURL:     "pkg:golang/github.com/adrg/xdg@v0.2.1",
   226  					Language: pkg.Go,
   227  					Type:     pkg.GoModulePkg,
   228  					Locations: file.NewLocationSet(
   229  						file.NewLocationFromCoordinates(
   230  							file.Coordinates{
   231  								RealPath:     "/a-path",
   232  								FileSystemID: "layer-id",
   233  							},
   234  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   235  					),
   236  					MetadataType: pkg.GolangBinMetadataType,
   237  					Metadata: pkg.GolangBinMetadata{
   238  						GoCompiledVersion: goCompiledVersion,
   239  						Architecture:      archDetails,
   240  						H1Digest:          "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   241  					},
   242  				},
   243  			},
   244  		},
   245  		{
   246  			name: "parse a mod with path but no main module",
   247  			arch: archDetails,
   248  			mod: &debug.BuildInfo{
   249  				GoVersion: goCompiledVersion,
   250  				Settings: []debug.BuildSetting{
   251  					{Key: "GOARCH", Value: archDetails},
   252  					{Key: "GOOS", Value: "darwin"},
   253  					{Key: "GOAMD64", Value: "v1"},
   254  				},
   255  				Path: "github.com/a/b/c",
   256  			},
   257  			expected: []pkg.Package{
   258  				{
   259  					Name:     "github.com/a/b/c",
   260  					Version:  "(devel)",
   261  					PURL:     "pkg:golang/github.com/a/b/c@(devel)",
   262  					Language: pkg.Go,
   263  					Type:     pkg.GoModulePkg,
   264  					Locations: file.NewLocationSet(
   265  						file.NewLocationFromCoordinates(
   266  							file.Coordinates{
   267  								RealPath:     "/a-path",
   268  								FileSystemID: "layer-id",
   269  							},
   270  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   271  					),
   272  					MetadataType: pkg.GolangBinMetadataType,
   273  					Metadata: pkg.GolangBinMetadata{
   274  						GoCompiledVersion: goCompiledVersion,
   275  						Architecture:      archDetails,
   276  						H1Digest:          "",
   277  						BuildSettings: map[string]string{
   278  							"GOAMD64": "v1",
   279  							"GOARCH":  "amd64",
   280  							"GOOS":    "darwin",
   281  						},
   282  						MainModule: "github.com/a/b/c",
   283  					},
   284  				},
   285  			},
   286  		},
   287  		{
   288  			name: "parse a mod without packages",
   289  			arch: archDetails,
   290  			mod: &debug.BuildInfo{
   291  				GoVersion: goCompiledVersion,
   292  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   293  				Settings: []debug.BuildSetting{
   294  					{Key: "GOARCH", Value: archDetails},
   295  					{Key: "GOOS", Value: "darwin"},
   296  					{Key: "GOAMD64", Value: "v1"},
   297  				},
   298  			},
   299  			expected: []pkg.Package{unmodifiedMain},
   300  		},
   301  		{
   302  			name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)",
   303  			arch: archDetails,
   304  			mod: &debug.BuildInfo{
   305  				GoVersion: goCompiledVersion,
   306  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   307  				Settings: []debug.BuildSetting{
   308  					{Key: "GOARCH", Value: archDetails},
   309  					{Key: "GOOS", Value: "darwin"},
   310  					{Key: "GOAMD64", Value: "v1"},
   311  					{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
   312  					{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
   313  					{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X blah=foobar`},
   314  				},
   315  			},
   316  			expected: []pkg.Package{
   317  				{
   318  					Name:     "github.com/nextlinux/gosbom",
   319  					Language: pkg.Go,
   320  					Type:     pkg.GoModulePkg,
   321  					Version:  "v0.0.0-20221014195457-41bc6bb41035",
   322  					PURL:     "pkg:golang/github.com/nextlinux/gosbom@v0.0.0-20221014195457-41bc6bb41035",
   323  					Locations: file.NewLocationSet(
   324  						file.NewLocationFromCoordinates(
   325  							file.Coordinates{
   326  								RealPath:     "/a-path",
   327  								FileSystemID: "layer-id",
   328  							},
   329  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   330  					),
   331  					MetadataType: pkg.GolangBinMetadataType,
   332  					Metadata: pkg.GolangBinMetadata{
   333  						GoCompiledVersion: goCompiledVersion,
   334  						Architecture:      archDetails,
   335  						BuildSettings: map[string]string{
   336  							"GOARCH":       archDetails,
   337  							"GOOS":         "darwin",
   338  							"GOAMD64":      "v1",
   339  							"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
   340  							"vcs.time":     "2022-10-14T19:54:57Z",
   341  							"-ldflags":     `build	-ldflags="-w -s -extldflags '-static' -X blah=foobar`,
   342  						},
   343  						MainModule: "github.com/nextlinux/gosbom",
   344  					},
   345  				},
   346  			},
   347  		},
   348  		{
   349  			name: "parse main mod and replace devel version with one from ldflags with vcs. build settings",
   350  			arch: archDetails,
   351  			mod: &debug.BuildInfo{
   352  				GoVersion: goCompiledVersion,
   353  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   354  				Settings: []debug.BuildSetting{
   355  					{Key: "GOARCH", Value: archDetails},
   356  					{Key: "GOOS", Value: "darwin"},
   357  					{Key: "GOAMD64", Value: "v1"},
   358  					{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
   359  					{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
   360  					{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X github.com/nextlinux/gosbom/internal/version.version=0.79.0`},
   361  				},
   362  			},
   363  			expected: []pkg.Package{
   364  				{
   365  					Name:     "github.com/nextlinux/gosbom",
   366  					Language: pkg.Go,
   367  					Type:     pkg.GoModulePkg,
   368  					Version:  "v0.79.0",
   369  					PURL:     "pkg:golang/github.com/nextlinux/gosbom@v0.79.0",
   370  					Locations: file.NewLocationSet(
   371  						file.NewLocationFromCoordinates(
   372  							file.Coordinates{
   373  								RealPath:     "/a-path",
   374  								FileSystemID: "layer-id",
   375  							},
   376  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   377  					),
   378  					MetadataType: pkg.GolangBinMetadataType,
   379  					Metadata: pkg.GolangBinMetadata{
   380  						GoCompiledVersion: goCompiledVersion,
   381  						Architecture:      archDetails,
   382  						BuildSettings: map[string]string{
   383  							"GOARCH":       archDetails,
   384  							"GOOS":         "darwin",
   385  							"GOAMD64":      "v1",
   386  							"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
   387  							"vcs.time":     "2022-10-14T19:54:57Z",
   388  							"-ldflags":     `build	-ldflags="-w -s -extldflags '-static' -X github.com/nextlinux/gosbom/internal/version.version=0.79.0`,
   389  						},
   390  						MainModule: "github.com/nextlinux/gosbom",
   391  					},
   392  				},
   393  			},
   394  		},
   395  		{
   396  			name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings",
   397  			arch: archDetails,
   398  			mod: &debug.BuildInfo{
   399  				GoVersion: goCompiledVersion,
   400  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   401  				Settings: []debug.BuildSetting{
   402  					{Key: "GOARCH", Value: archDetails},
   403  					{Key: "GOOS", Value: "darwin"},
   404  					{Key: "GOAMD64", Value: "v1"},
   405  					{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X github.com/nextlinux/gosbom/internal/version.version=0.79.0`},
   406  				},
   407  			},
   408  			expected: []pkg.Package{
   409  				{
   410  					Name:     "github.com/nextlinux/gosbom",
   411  					Language: pkg.Go,
   412  					Type:     pkg.GoModulePkg,
   413  					Version:  "v0.79.0",
   414  					PURL:     "pkg:golang/github.com/nextlinux/gosbom@v0.79.0",
   415  					Locations: file.NewLocationSet(
   416  						file.NewLocationFromCoordinates(
   417  							file.Coordinates{
   418  								RealPath:     "/a-path",
   419  								FileSystemID: "layer-id",
   420  							},
   421  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   422  					),
   423  					MetadataType: pkg.GolangBinMetadataType,
   424  					Metadata: pkg.GolangBinMetadata{
   425  						GoCompiledVersion: goCompiledVersion,
   426  						Architecture:      archDetails,
   427  						BuildSettings: map[string]string{
   428  							"GOARCH":   archDetails,
   429  							"GOOS":     "darwin",
   430  							"GOAMD64":  "v1",
   431  							"-ldflags": `build	-ldflags="-w -s -extldflags '-static' -X github.com/nextlinux/gosbom/internal/version.version=0.79.0`,
   432  						},
   433  						MainModule: "github.com/nextlinux/gosbom",
   434  					},
   435  				},
   436  			},
   437  		},
   438  		{
   439  			name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings",
   440  			arch: archDetails,
   441  			mod: &debug.BuildInfo{
   442  				GoVersion: goCompiledVersion,
   443  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   444  				Settings: []debug.BuildSetting{
   445  					{Key: "GOARCH", Value: archDetails},
   446  					{Key: "GOOS", Value: "darwin"},
   447  					{Key: "GOAMD64", Value: "v1"},
   448  					{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`},
   449  				},
   450  			},
   451  			expected: []pkg.Package{
   452  				{
   453  					Name:     "github.com/nextlinux/gosbom",
   454  					Language: pkg.Go,
   455  					Type:     pkg.GoModulePkg,
   456  					Version:  "v0.79.0",
   457  					PURL:     "pkg:golang/github.com/nextlinux/gosbom@v0.79.0",
   458  					Locations: file.NewLocationSet(
   459  						file.NewLocationFromCoordinates(
   460  							file.Coordinates{
   461  								RealPath:     "/a-path",
   462  								FileSystemID: "layer-id",
   463  							},
   464  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   465  					),
   466  					MetadataType: pkg.GolangBinMetadataType,
   467  					Metadata: pkg.GolangBinMetadata{
   468  						GoCompiledVersion: goCompiledVersion,
   469  						Architecture:      archDetails,
   470  						BuildSettings: map[string]string{
   471  							"GOARCH":   archDetails,
   472  							"GOOS":     "darwin",
   473  							"GOAMD64":  "v1",
   474  							"-ldflags": `build	-ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`,
   475  						},
   476  						MainModule: "github.com/nextlinux/gosbom",
   477  					},
   478  				},
   479  			},
   480  		},
   481  		{
   482  			name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings",
   483  			arch: archDetails,
   484  			mod: &debug.BuildInfo{
   485  				GoVersion: goCompiledVersion,
   486  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   487  				Settings: []debug.BuildSetting{
   488  					{Key: "GOARCH", Value: archDetails},
   489  					{Key: "GOOS", Value: "darwin"},
   490  					{Key: "GOAMD64", Value: "v1"},
   491  					{Key: "-ldflags", Value: `build	-ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`},
   492  				},
   493  			},
   494  			expected: []pkg.Package{
   495  				{
   496  					Name:     "github.com/nextlinux/gosbom",
   497  					Language: pkg.Go,
   498  					Type:     pkg.GoModulePkg,
   499  					Version:  "v0.79.0",
   500  					PURL:     "pkg:golang/github.com/nextlinux/gosbom@v0.79.0",
   501  					Locations: file.NewLocationSet(
   502  						file.NewLocationFromCoordinates(
   503  							file.Coordinates{
   504  								RealPath:     "/a-path",
   505  								FileSystemID: "layer-id",
   506  							},
   507  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   508  					),
   509  					MetadataType: pkg.GolangBinMetadataType,
   510  					Metadata: pkg.GolangBinMetadata{
   511  						GoCompiledVersion: goCompiledVersion,
   512  						Architecture:      archDetails,
   513  						BuildSettings: map[string]string{
   514  							"GOARCH":   archDetails,
   515  							"GOOS":     "darwin",
   516  							"GOAMD64":  "v1",
   517  							"-ldflags": `build	-ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`,
   518  						},
   519  						MainModule: "github.com/nextlinux/gosbom",
   520  					},
   521  				},
   522  			},
   523  		},
   524  		{
   525  			name: "parse main mod and replace devel version with a pseudo version",
   526  			arch: archDetails,
   527  			mod: &debug.BuildInfo{
   528  				GoVersion: goCompiledVersion,
   529  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   530  				Settings: []debug.BuildSetting{
   531  					{Key: "GOARCH", Value: archDetails},
   532  					{Key: "GOOS", Value: "darwin"},
   533  					{Key: "GOAMD64", Value: "v1"},
   534  					{Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"},
   535  					{Key: "vcs.time", Value: "2022-10-14T19:54:57Z"},
   536  				},
   537  			},
   538  			expected: []pkg.Package{
   539  				{
   540  					Name:     "github.com/nextlinux/gosbom",
   541  					Language: pkg.Go,
   542  					Type:     pkg.GoModulePkg,
   543  					Version:  "v0.0.0-20221014195457-41bc6bb41035",
   544  					PURL:     "pkg:golang/github.com/nextlinux/gosbom@v0.0.0-20221014195457-41bc6bb41035",
   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  					MetadataType: pkg.GolangBinMetadataType,
   554  					Metadata: pkg.GolangBinMetadata{
   555  						GoCompiledVersion: goCompiledVersion,
   556  						Architecture:      archDetails,
   557  						BuildSettings: map[string]string{
   558  							"GOARCH":       archDetails,
   559  							"GOOS":         "darwin",
   560  							"GOAMD64":      "v1",
   561  							"vcs.revision": "41bc6bb410352845f22766e27dd48ba93aa825a4",
   562  							"vcs.time":     "2022-10-14T19:54:57Z",
   563  						},
   564  						MainModule: "github.com/nextlinux/gosbom",
   565  					},
   566  				},
   567  			},
   568  		},
   569  		{
   570  			name: "parse a populated mod string and returns packages but no source info",
   571  			arch: archDetails,
   572  			mod: &debug.BuildInfo{
   573  				GoVersion: goCompiledVersion,
   574  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   575  				Settings: []debug.BuildSetting{
   576  					{Key: "GOARCH", Value: archDetails},
   577  					{Key: "GOOS", Value: "darwin"},
   578  					{Key: "GOAMD64", Value: "v1"},
   579  				},
   580  				Deps: []*debug.Module{
   581  					{
   582  						Path:    "github.com/adrg/xdg",
   583  						Version: "v0.2.1",
   584  						Sum:     "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   585  					},
   586  					{
   587  						Path:    "github.com/anchore/client-go",
   588  						Version: "v0.0.0-20210222170800-9c70f9b80bcf",
   589  						Sum:     "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
   590  					},
   591  				},
   592  			},
   593  			expected: []pkg.Package{
   594  				{
   595  					Name:     "github.com/adrg/xdg",
   596  					Version:  "v0.2.1",
   597  					PURL:     "pkg:golang/github.com/adrg/xdg@v0.2.1",
   598  					Language: pkg.Go,
   599  					Type:     pkg.GoModulePkg,
   600  					Locations: file.NewLocationSet(
   601  						file.NewLocationFromCoordinates(
   602  							file.Coordinates{
   603  								RealPath:     "/a-path",
   604  								FileSystemID: "layer-id",
   605  							},
   606  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   607  					),
   608  					MetadataType: pkg.GolangBinMetadataType,
   609  					Metadata: pkg.GolangBinMetadata{
   610  						GoCompiledVersion: goCompiledVersion,
   611  						Architecture:      archDetails,
   612  						H1Digest:          "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=",
   613  						MainModule:        "github.com/nextlinux/gosbom",
   614  					},
   615  				},
   616  				{
   617  					Name:     "github.com/anchore/client-go",
   618  					Version:  "v0.0.0-20210222170800-9c70f9b80bcf",
   619  					PURL:     "pkg:golang/github.com/anchore/client-go@v0.0.0-20210222170800-9c70f9b80bcf",
   620  					Language: pkg.Go,
   621  					Type:     pkg.GoModulePkg,
   622  					Locations: file.NewLocationSet(
   623  						file.NewLocationFromCoordinates(
   624  							file.Coordinates{
   625  								RealPath:     "/a-path",
   626  								FileSystemID: "layer-id",
   627  							},
   628  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   629  					),
   630  					MetadataType: pkg.GolangBinMetadataType,
   631  					Metadata: pkg.GolangBinMetadata{
   632  						GoCompiledVersion: goCompiledVersion,
   633  						Architecture:      archDetails,
   634  						H1Digest:          "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=",
   635  						MainModule:        "github.com/nextlinux/gosbom",
   636  					},
   637  				},
   638  				unmodifiedMain,
   639  			},
   640  		},
   641  		{
   642  			name: "parse a populated mod string and returns packages when a replace directive exists",
   643  			arch: archDetails,
   644  			mod: &debug.BuildInfo{
   645  				GoVersion: goCompiledVersion,
   646  				Main:      debug.Module{Path: "github.com/nextlinux/gosbom", Version: "(devel)"},
   647  				Settings: []debug.BuildSetting{
   648  					{Key: "GOARCH", Value: archDetails},
   649  					{Key: "GOOS", Value: "darwin"},
   650  					{Key: "GOAMD64", Value: "v1"},
   651  				},
   652  				Deps: []*debug.Module{
   653  					{
   654  						Path:    "golang.org/x/sys",
   655  						Version: "v0.0.0-20211006194710-c8a6f5223071",
   656  						Sum:     "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
   657  					},
   658  					{
   659  						Path:    "golang.org/x/term",
   660  						Version: "v0.0.0-20210927222741-03fcf44c2211",
   661  						Sum:     "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
   662  						Replace: &debug.Module{
   663  							Path:    "golang.org/x/term",
   664  							Version: "v0.0.0-20210916214954-140adaaadfaf",
   665  							Sum:     "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
   666  						},
   667  					},
   668  				},
   669  			},
   670  			expected: []pkg.Package{
   671  				{
   672  					Name:     "golang.org/x/sys",
   673  					Version:  "v0.0.0-20211006194710-c8a6f5223071",
   674  					PURL:     "pkg:golang/golang.org/x/sys@v0.0.0-20211006194710-c8a6f5223071",
   675  					Language: pkg.Go,
   676  					Type:     pkg.GoModulePkg,
   677  					Locations: file.NewLocationSet(
   678  						file.NewLocationFromCoordinates(
   679  							file.Coordinates{
   680  								RealPath:     "/a-path",
   681  								FileSystemID: "layer-id",
   682  							},
   683  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   684  					),
   685  					MetadataType: pkg.GolangBinMetadataType,
   686  					Metadata: pkg.GolangBinMetadata{
   687  						GoCompiledVersion: goCompiledVersion,
   688  						Architecture:      archDetails,
   689  						H1Digest:          "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=",
   690  						MainModule:        "github.com/nextlinux/gosbom",
   691  					}},
   692  				{
   693  					Name:     "golang.org/x/term",
   694  					Version:  "v0.0.0-20210916214954-140adaaadfaf",
   695  					PURL:     "pkg:golang/golang.org/x/term@v0.0.0-20210916214954-140adaaadfaf",
   696  					Language: pkg.Go,
   697  					Type:     pkg.GoModulePkg,
   698  					Locations: file.NewLocationSet(
   699  						file.NewLocationFromCoordinates(
   700  							file.Coordinates{
   701  								RealPath:     "/a-path",
   702  								FileSystemID: "layer-id",
   703  							},
   704  						).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
   705  					),
   706  					MetadataType: pkg.GolangBinMetadataType,
   707  					Metadata: pkg.GolangBinMetadata{
   708  						GoCompiledVersion: goCompiledVersion,
   709  						Architecture:      archDetails,
   710  						H1Digest:          "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=",
   711  						MainModule:        "github.com/nextlinux/gosbom",
   712  					},
   713  				},
   714  				unmodifiedMain,
   715  			},
   716  		},
   717  	}
   718  
   719  	for _, test := range tests {
   720  		t.Run(test.name, func(t *testing.T) {
   721  			for i := range test.expected {
   722  				p := &test.expected[i]
   723  				p.SetID()
   724  			}
   725  			location := file.NewLocationFromCoordinates(
   726  				file.Coordinates{
   727  					RealPath:     "/a-path",
   728  					FileSystemID: "layer-id",
   729  				},
   730  			)
   731  
   732  			c := goBinaryCataloger{}
   733  			pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.arch)
   734  			assert.Equal(t, test.expected, pkgs)
   735  		})
   736  	}
   737  }
   738  
   739  func Test_extractVersionFromLDFlags(t *testing.T) {
   740  	tests := []struct {
   741  		name             string
   742  		ldflags          string
   743  		wantMajorVersion string
   744  		wantFullVersion  string
   745  	}{
   746  		{
   747  			name:    "empty ldflags",
   748  			ldflags: "",
   749  		},
   750  		{
   751  			name:             "gosbom ldflags",
   752  			ldflags:          `	build	-ldflags="-w -s -extldflags '-static' -X github.com/nextlinux/gosbom/internal/version.version=0.79.0 -X github.com/nextlinux/gosbom/internal/version.gitCommit=b2b332e8b2b66af0905e98b54ebd713a922be1a8 -X github.com/nextlinux/gosbom/internal/version.buildDate=2023-04-21T16:20:25Z -X github.com/nextlinux/gosbom/internal/version.gitDescription=v0.79.0 "`,
   753  			wantMajorVersion: "0",
   754  			wantFullVersion:  "v0.79.0",
   755  		},
   756  		{
   757  			name: "kubectl ldflags",
   758  			ldflags: `	build	-asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes
   759  	build	-compiler=gc
   760  	build	-gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes "
   761  	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"`,
   762  			wantMajorVersion: "1",
   763  			wantFullVersion:  "v1.25.9",
   764  		},
   765  		{
   766  			name:             "nerdctl ldflags",
   767  			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"`,
   768  			wantMajorVersion: "1",
   769  			wantFullVersion:  "v1.3.1",
   770  		},
   771  		{
   772  			name:             "limactl ldflags",
   773  			ldflags:          `	build	-ldflags="-s -w -X github.com/lima-vm/lima/pkg/version.Version=v0.15.1"`,
   774  			wantMajorVersion: "0",
   775  			wantFullVersion:  "v0.15.1",
   776  		},
   777  		{
   778  			name:             "terraform ldflags",
   779  			ldflags:          `	build	-ldflags="-w -s -X 'github.com/hashicorp/terraform/version.Version=1.4.6' -X 'github.com/hashicorp/terraform/version.Prerelease='"`,
   780  			wantMajorVersion: "1",
   781  			wantFullVersion:  "v1.4.6",
   782  		},
   783  		{
   784  			name: "kube-apiserver ldflags",
   785  			ldflags: `	build	-asmflags=all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes
   786  	build	-buildmode=exe
   787  	build	-compiler=gc
   788  	build	-gcflags="all=-trimpath=/workspace/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes "
   789  	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"`,
   790  			wantMajorVersion: "1",
   791  			wantFullVersion:  "v1.27.1",
   792  		},
   793  		{
   794  			name: "prometheus ldflags",
   795  			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'"
   796  	build	-tags=netgo,builtinassets,stringlabels`,
   797  			wantMajorVersion: "2",
   798  			wantFullVersion:  "v2.44.0",
   799  		},
   800  		{
   801  			name: "influxdb ldflags",
   802  			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'"
   803  	build	-tags=assets,sqlite_foreign_keys,sqlite_json,static_build,noasm`,
   804  			wantMajorVersion: "2",
   805  			wantFullVersion:  "v2.7.1",
   806  		},
   807  		{
   808  			name:             "gitea ldflags",
   809  			ldflags:          `	build	-ldflags=" -X \"main.MakeVersion=GNU Make 4.1\" -X \"main.Version=1.19.3\" -X \"main.Tags=bindata sqlite sqlite_unlock_notify\" "`,
   810  			wantMajorVersion: "1",
   811  			wantFullVersion:  "v1.19.3",
   812  		},
   813  		{
   814  			name:             "docker sbom cli ldflags",
   815  			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 "`,
   816  			wantMajorVersion: "0",
   817  			wantFullVersion:  "v0.6.1-SNAPSHOT-02cf1c8",
   818  		},
   819  		{
   820  			name:             "docker scout ldflags",
   821  			ldflags:          `	build	-ldflags="-w -s -extldflags '-static' -X github.com/docker/scout-cli-plugin/internal.version=0.10.0 "`,
   822  			wantMajorVersion: "0",
   823  			wantFullVersion:  "v0.10.0",
   824  		},
   825  		{
   826  			name:             "influx telegraf ldflags",
   827  			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"`,
   828  			wantMajorVersion: "1",
   829  			wantFullVersion:  "v1.26.2",
   830  		},
   831  		{
   832  			name:             "argocd ldflags",
   833  			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\""`,
   834  			wantMajorVersion: "2",
   835  			wantFullVersion:  "v2.7.2",
   836  		},
   837  		{
   838  			name:             "kustomize ldflags",
   839  			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 "`,
   840  			wantMajorVersion: "4",
   841  			wantFullVersion:  "v4.5.7",
   842  		},
   843  		//////////////////////////////////////////////////////////////////
   844  		// negative cases
   845  		{
   846  			name:    "hugo ldflags",
   847  			ldflags: `	build	-ldflags="-s -w -X github.com/gohugoio/hugo/common/hugo.vendorInfo=gohugoio"`,
   848  		},
   849  		{
   850  			name:    "ghostunnel ldflags",
   851  			ldflags: `	build	-ldflags="-X main.version=77d9aaa"`,
   852  		},
   853  		{
   854  			name:    "opa ldflags",
   855  			ldflags: `build	-ldflags=" -X github.com/open-policy-agent/opa/version.Hostname=9549178459bc"`,
   856  		},
   857  		///////////////////////////////////////////////////////////////////
   858  		// trickier cases
   859  		{
   860  			name:             "macvlan plugin for cri-o ldflags",
   861  			ldflags:          `	build	-ldflags="-extldflags -static -X github.com/containernetworking/plugins/pkg/utils/buildversion.BuildVersion=v1.2.0"`,
   862  			wantMajorVersion: "1",
   863  			wantFullVersion:  "v1.2.0",
   864  		},
   865  		{
   866  			name:             "coder ldflags",
   867  			ldflags:          `	build	-ldflags="-s -w -X 'github.com/coder/coder/buildinfo.tag=0.23.4'"`,
   868  			wantMajorVersion: "0",
   869  			wantFullVersion:  "v0.23.4",
   870  		},
   871  		///////////////////////////////////////////////////////////////////
   872  		// don't know how to handle these... yet
   873  		//{
   874  		//	// package name: pkgName: "github.com/krakendio/krakend-ce/v2",
   875  		//	name:             "krakenD ldflags",
   876  		//	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) "`,
   877  		//	wantMajorVersion: "2.3.2",
   878  		//	wantFullVersion:  "v2.3.2",
   879  		//},
   880  		//{
   881  		//	// package name: pkgName: "github.com/krakendio/krakend-ce/v2",
   882  		//	name:             "krakenD ldflags -- answer embedded in the middle",
   883  		//	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) "`,
   884  		//	wantMajorVersion: "2.3.2",
   885  		//	wantFullVersion:  "v2.3.2",
   886  		//},
   887  	}
   888  	for _, tt := range tests {
   889  		t.Run(tt.name, func(t *testing.T) {
   890  			gotMajorVersion, gotFullVersion := extractVersionFromLDFlags(tt.ldflags)
   891  			assert.Equal(t, tt.wantMajorVersion, gotMajorVersion, "unexpected major version")
   892  			assert.Equal(t, tt.wantFullVersion, gotFullVersion, "unexpected full version")
   893  		})
   894  	}
   895  }