github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/language/golang/gobinary/gobinary_test.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gobinary_test
    16  
    17  import (
    18  	"errors"
    19  	"io/fs"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/google/go-cmp/cmp/cmpopts"
    27  	cpb "github.com/google/osv-scalibr/binary/proto/config_go_proto"
    28  	"github.com/google/osv-scalibr/extractor"
    29  	"github.com/google/osv-scalibr/extractor/filesystem"
    30  	"github.com/google/osv-scalibr/extractor/filesystem/language/golang/gobinary"
    31  	"github.com/google/osv-scalibr/extractor/filesystem/simplefileapi"
    32  	scalibrfs "github.com/google/osv-scalibr/fs"
    33  	"github.com/google/osv-scalibr/inventory"
    34  	"github.com/google/osv-scalibr/purl"
    35  	"github.com/google/osv-scalibr/stats"
    36  	"github.com/google/osv-scalibr/testing/fakefs"
    37  	"github.com/google/osv-scalibr/testing/testcollector"
    38  )
    39  
    40  func TestFileRequired(t *testing.T) {
    41  	tests := []struct {
    42  		name             string
    43  		path             string
    44  		mode             fs.FileMode
    45  		fileSizeBytes    int64
    46  		maxFileSizeBytes int64
    47  		wantRequired     bool
    48  		wantResultMetric stats.FileRequiredResult
    49  	}{
    50  		{
    51  			name:             "user executable",
    52  			path:             "some/path/a",
    53  			mode:             0766,
    54  			wantRequired:     true,
    55  			wantResultMetric: stats.FileRequiredResultOK,
    56  		},
    57  		{
    58  			name:             "group executable",
    59  			path:             "some/path/a",
    60  			mode:             0676,
    61  			wantRequired:     true,
    62  			wantResultMetric: stats.FileRequiredResultOK,
    63  		},
    64  		{
    65  			name:             "other executable",
    66  			path:             "some/path/a",
    67  			mode:             0667,
    68  			wantRequired:     true,
    69  			wantResultMetric: stats.FileRequiredResultOK,
    70  		},
    71  		{
    72  			name:             "windows exe",
    73  			path:             "some/path/a.exe",
    74  			mode:             0666,
    75  			wantRequired:     true,
    76  			wantResultMetric: stats.FileRequiredResultOK,
    77  		},
    78  		{
    79  			name:             "executable required if size less than maxFileSizeBytes",
    80  			path:             "some/path/a",
    81  			mode:             0766,
    82  			fileSizeBytes:    100,
    83  			maxFileSizeBytes: 1000,
    84  			wantRequired:     true,
    85  			wantResultMetric: stats.FileRequiredResultOK,
    86  		},
    87  		{
    88  			name:             "executable required if size equal to maxFileSizeBytes",
    89  			path:             "some/path/a",
    90  			mode:             0766,
    91  			fileSizeBytes:    1000,
    92  			maxFileSizeBytes: 1000,
    93  			wantRequired:     true,
    94  			wantResultMetric: stats.FileRequiredResultOK,
    95  		},
    96  		{
    97  			name:             "executable not required if size greater than maxFileSizeBytes",
    98  			path:             "some/path/a",
    99  			mode:             0766,
   100  			fileSizeBytes:    1000,
   101  			maxFileSizeBytes: 100,
   102  			wantRequired:     false,
   103  			wantResultMetric: stats.FileRequiredResultSizeLimitExceeded,
   104  		},
   105  		{
   106  			name:             "executable required if maxFileSizeBytes explicitly set to 0",
   107  			path:             "some/path/a",
   108  			mode:             0766,
   109  			fileSizeBytes:    1000,
   110  			maxFileSizeBytes: 0,
   111  			wantRequired:     true,
   112  			wantResultMetric: stats.FileRequiredResultOK,
   113  		},
   114  	}
   115  
   116  	for _, tt := range tests {
   117  		t.Run(tt.name, func(t *testing.T) {
   118  			collector := testcollector.New()
   119  			e := gobinary.New(&cpb.PluginConfig{
   120  				MaxFileSizeBytes: tt.maxFileSizeBytes,
   121  			})
   122  			e.(*gobinary.Extractor).Stats = collector
   123  
   124  			// Set a default file size if not specified.
   125  			fileSizeBytes := tt.fileSizeBytes
   126  			if fileSizeBytes == 0 {
   127  				fileSizeBytes = 1000
   128  			}
   129  
   130  			if got := e.FileRequired(simplefileapi.New(tt.path, fakefs.FakeFileInfo{
   131  				FileName: filepath.Base(tt.path),
   132  				FileMode: tt.mode,
   133  				FileSize: fileSizeBytes,
   134  			})); got != tt.wantRequired {
   135  				t.Fatalf("FileRequired(%s): got %v, want %v", tt.path, got, tt.wantRequired)
   136  			}
   137  
   138  			gotResultMetric := collector.FileRequiredResult(tt.path)
   139  			if gotResultMetric != tt.wantResultMetric {
   140  				t.Errorf("FileRequired(%s) recorded result metric %v, want result metric %v", tt.path, gotResultMetric, tt.wantResultMetric)
   141  			}
   142  		})
   143  	}
   144  }
   145  
   146  func TestExtract(t *testing.T) {
   147  	tests := []struct {
   148  		name             string
   149  		cfg              *cpb.PluginConfig
   150  		path             string
   151  		wantPackages     []*extractor.Package
   152  		wantErr          error
   153  		wantResultMetric stats.FileExtractedResult
   154  	}{
   155  		{
   156  			name:         "binary_with_module_replacement-darwin-amd64",
   157  			path:         "testdata/binary_with_module_replacement-darwin-amd64",
   158  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-darwin-amd64"),
   159  		},
   160  		{
   161  			name:         "binary_with_module_replacement-darwin-arm64",
   162  			path:         "testdata/binary_with_module_replacement-darwin-arm64",
   163  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-darwin-arm64"),
   164  		},
   165  		{
   166  			name:         "binary_with_module_replacement-linux-386",
   167  			path:         "testdata/binary_with_module_replacement-linux-386",
   168  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-linux-386"),
   169  		},
   170  		{
   171  			name:         "binary_with_module_replacement-linux-amd64",
   172  			path:         "testdata/binary_with_module_replacement-linux-amd64",
   173  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-linux-amd64"),
   174  		},
   175  		{
   176  			name:         "binary_with_module_replacement-linux-arm64",
   177  			path:         "testdata/binary_with_module_replacement-linux-arm64",
   178  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-linux-arm64"),
   179  		},
   180  		{
   181  			name:         "binary_with_module_replacement-windows-386",
   182  			path:         "testdata/binary_with_module_replacement-windows-386",
   183  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-windows-386"),
   184  		},
   185  		{
   186  			name:         "binary_with_module_replacement-windows-amd64",
   187  			path:         "testdata/binary_with_module_replacement-windows-amd64",
   188  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-windows-amd64"),
   189  		},
   190  		{
   191  			name:         "binary_with_module_replacement-windows-arm64",
   192  			path:         "testdata/binary_with_module_replacement-windows-arm64",
   193  			wantPackages: createPackagesWithMain(append(BinaryWithModuleReplacementPackages, Toolchain), "testdata/binary_with_module_replacement-windows-arm64"),
   194  		},
   195  		{
   196  			name:         "binary_with_modules-darwin-amd64",
   197  			path:         "testdata/binary_with_modules-darwin-amd64",
   198  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackages, Toolchain), "testdata/binary_with_modules-darwin-amd64"),
   199  		},
   200  		{
   201  			name:         "binary_with_modules-darwin-arm64",
   202  			path:         "testdata/binary_with_modules-darwin-arm64",
   203  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackages, Toolchain), "testdata/binary_with_modules-darwin-arm64"),
   204  		},
   205  		{
   206  			name:         "binary_with_modules-linux-386",
   207  			path:         "testdata/binary_with_modules-linux-386",
   208  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackages, Toolchain), "testdata/binary_with_modules-linux-386"),
   209  		},
   210  		{
   211  			name:         "binary_with_modules-linux-amd64",
   212  			path:         "testdata/binary_with_modules-linux-amd64",
   213  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackages, Toolchain), "testdata/binary_with_modules-linux-amd64"),
   214  		},
   215  		{
   216  			name:         "binary_with_modules-linux-arm64",
   217  			path:         "testdata/binary_with_modules-linux-arm64",
   218  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackages, Toolchain), "testdata/binary_with_modules-linux-arm64"),
   219  		},
   220  		{
   221  			name:         "binary_with_modules-windows-386",
   222  			path:         "testdata/binary_with_modules-windows-386",
   223  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackagesWindows, Toolchain), "testdata/binary_with_modules-windows-386"),
   224  		},
   225  		{
   226  			name:         "binary_with_modules-windows-amd64",
   227  			path:         "testdata/binary_with_modules-windows-amd64",
   228  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackagesWindows, Toolchain), "testdata/binary_with_modules-windows-amd64"),
   229  		},
   230  		{
   231  			name:         "binary_with_modules-windows-arm64",
   232  			path:         "testdata/binary_with_modules-windows-arm64",
   233  			wantPackages: createPackagesWithMain(append(BinaryWithModulesPackagesWindows, Toolchain), "testdata/binary_with_modules-windows-arm64"),
   234  		},
   235  		{
   236  			name:         "nginx-ingress-controller with version from content off",
   237  			path:         "testdata/nginx-ingress-controller",
   238  			wantPackages: createPackages(append(BinaryWithModulesPackagesNginx, goPackage("k8s.io/ingress-nginx", "(devel)")), "testdata/nginx-ingress-controller"),
   239  		},
   240  		{
   241  			name: "nginx-ingress-controller_with_version_from_content_on",
   242  			path: "testdata/nginx-ingress-controller",
   243  			cfg: &cpb.PluginConfig{
   244  				PluginSpecific: []*cpb.PluginSpecificConfig{
   245  					{Config: &cpb.PluginSpecificConfig_GoBinary{GoBinary: &cpb.GoBinaryConfig{VersionFromContent: true}}},
   246  				},
   247  			},
   248  			wantPackages: createPackages(append(BinaryWithModulesPackagesNginx, goPackage("k8s.io/ingress-nginx", "1.11.4")), "testdata/nginx-ingress-controller"),
   249  		},
   250  		{
   251  			name:             "dummy file that fails to parse will log an error, but won't fail extraction",
   252  			path:             "testdata/dummy",
   253  			wantPackages:     nil,
   254  			wantResultMetric: stats.FileExtractedResultErrorUnknown,
   255  		},
   256  	}
   257  
   258  	for _, tt := range tests {
   259  		t.Run(tt.name, func(t *testing.T) {
   260  			f, err := os.Open(tt.path)
   261  			if err != nil {
   262  				t.Fatalf("os.Open(%s) unexpected error: %v", tt.path, err)
   263  			}
   264  			defer f.Close()
   265  
   266  			info, err := f.Stat()
   267  			if err != nil {
   268  				t.Fatalf("f.Stat() for %q unexpected error: %v", tt.path, err)
   269  			}
   270  
   271  			collector := testcollector.New()
   272  
   273  			input := &filesystem.ScanInput{FS: scalibrfs.DirFS("."), Path: tt.path, Info: info, Reader: f}
   274  
   275  			if tt.cfg == nil {
   276  				tt.cfg = &cpb.PluginConfig{}
   277  			}
   278  
   279  			e := gobinary.New(tt.cfg)
   280  			e.(*gobinary.Extractor).Stats = collector
   281  			got, err := e.Extract(t.Context(), input)
   282  			if !errors.Is(err, tt.wantErr) {
   283  				t.Fatalf("Extract(%s) got error: %v, want error: %v", tt.path, err, tt.wantErr)
   284  			}
   285  			sort := func(a, b *extractor.Package) bool { return a.Name < b.Name }
   286  			wantInv := inventory.Inventory{Packages: tt.wantPackages}
   287  			if diff := cmp.Diff(wantInv, got, cmpopts.SortSlices(sort)); diff != "" {
   288  				t.Fatalf("Extract(%s) (-want +got):\n%s", tt.path, diff)
   289  			}
   290  
   291  			wantResultMetric := tt.wantResultMetric
   292  			if wantResultMetric == "" && tt.wantErr == nil {
   293  				wantResultMetric = stats.FileExtractedResultSuccess
   294  			}
   295  			gotResultMetric := collector.FileExtractedResult(tt.path)
   296  			if gotResultMetric != wantResultMetric {
   297  				t.Errorf("Extract(%s) recorded result metric %v, want result metric %v", tt.path, gotResultMetric, wantResultMetric)
   298  			}
   299  
   300  			gotFileSizeMetric := collector.FileExtractedFileSize(tt.path)
   301  			if gotFileSizeMetric != info.Size() {
   302  				t.Errorf("Extract(%s) recorded file size %v, want file size %v", tt.path, gotFileSizeMetric, info.Size())
   303  			}
   304  		})
   305  	}
   306  }
   307  
   308  var (
   309  	// BinaryWithModulesPackagesWindows is a list of packages built into the
   310  	// binary_with_modules-* testdata binaries, but only on Windows, where there
   311  	// is an indirect dependency that is not built-in.
   312  	BinaryWithModulesPackagesWindows = []*extractor.Package{
   313  		// direct dependencies
   314  		goPackage("github.com/ulikunitz/xz", "0.5.11"),
   315  		goPackage("github.com/gin-gonic/gin", "1.8.1"),
   316  
   317  		// indirect dependencies
   318  		goPackage("github.com/gin-contrib/sse", "0.1.0"),
   319  		goPackage("github.com/go-playground/locales", "0.14.0"),
   320  		goPackage("github.com/go-playground/universal-translator", "0.18.0"),
   321  		goPackage("github.com/go-playground/validator/v10", "10.11.1"),
   322  		goPackage("github.com/leodido/go-urn", "1.2.1"),
   323  		goPackage("github.com/mattn/go-isatty", "0.0.16"),
   324  		goPackage("github.com/pelletier/go-toml/v2", "2.0.6"),
   325  		goPackage("github.com/ugorji/go/codec", "1.2.7"),
   326  		goPackage("golang.org/x/crypto", "0.4.0"),
   327  		goPackage("golang.org/x/net", "0.4.0"),
   328  		goPackage("golang.org/x/text", "0.5.0"),
   329  		goPackage("google.golang.org/protobuf", "1.28.1"),
   330  		goPackage("gopkg.in/yaml.v2", "2.4.0"),
   331  	}
   332  
   333  	// BinaryWithModulesPackages is a list of packages built into the
   334  	// binary_with_modules-* testdata binaries.
   335  	BinaryWithModulesPackages = append(
   336  		BinaryWithModulesPackagesWindows,
   337  		goPackage("golang.org/x/sys", "0.3.0"),
   338  	)
   339  
   340  	// BinaryWithModuleReplacementPackages is a list of packages built into the
   341  	// binary_with_module_replacement-* testdata binaries.
   342  	BinaryWithModuleReplacementPackages = []*extractor.Package{
   343  		// this binary replaces golang.org/x/xerrors => github.com/golang/xerrors
   344  		goPackage("github.com/golang/xerrors", "0.0.0-20220907171357-04be3eba64a2"),
   345  	}
   346  
   347  	BinaryWithModulesPackagesNginx = []*extractor.Package{
   348  		goPackage("dario.cat/mergo", "1.0.1"),
   349  		goPackage("github.com/armon/go-proxyproto", "0.1.0"),
   350  		goPackage("github.com/beorn7/perks", "1.0.1"),
   351  		goPackage("github.com/blang/semver/v4", "4.0.0"),
   352  		goPackage("github.com/cespare/xxhash/v2", "2.3.0"),
   353  		goPackage("github.com/coreos/go-systemd/v22", "22.5.0"),
   354  		goPackage("github.com/cyphar/filepath-securejoin", "0.3.5"),
   355  		goPackage("github.com/davecgh/go-spew", "1.1.2-0.20180830191138-d8f796af33cc"),
   356  		goPackage("github.com/eapache/channels", "1.1.0"),
   357  		goPackage("github.com/eapache/queue", "1.1.0"),
   358  		goPackage("github.com/emicklei/go-restful/v3", "3.12.0"),
   359  		goPackage("github.com/fsnotify/fsnotify", "1.8.0"),
   360  		goPackage("github.com/fullsailor/pkcs7", "0.0.0-20190404230743-d7302db945fa"),
   361  		goPackage("github.com/fxamacker/cbor/v2", "2.7.0"),
   362  		goPackage("github.com/go-logr/logr", "1.4.2"),
   363  		goPackage("github.com/go-openapi/jsonpointer", "0.21.0"),
   364  		goPackage("github.com/go-openapi/jsonreference", "0.21.0"),
   365  		goPackage("github.com/go-openapi/swag", "0.23.0"),
   366  		goPackage("github.com/godbus/dbus/v5", "5.1.0"),
   367  		goPackage("github.com/gogo/protobuf", "1.3.2"),
   368  		goPackage("github.com/golang/protobuf", "1.5.4"),
   369  		goPackage("github.com/google/gnostic-models", "0.6.8"),
   370  		goPackage("github.com/google/go-cmp", "0.6.0"),
   371  		goPackage("github.com/google/gofuzz", "1.2.0"),
   372  		goPackage("github.com/google/uuid", "1.6.0"),
   373  		goPackage("github.com/josharian/intern", "1.0.0"),
   374  		goPackage("github.com/json-iterator/go", "1.1.12"),
   375  		goPackage("github.com/klauspost/compress", "1.17.9"),
   376  		goPackage("github.com/mailru/easyjson", "0.7.7"),
   377  		goPackage("github.com/mitchellh/go-ps", "1.0.0"),
   378  		goPackage("github.com/mitchellh/hashstructure/v2", "2.0.2"),
   379  		goPackage("github.com/mitchellh/mapstructure", "1.5.0"),
   380  		goPackage("github.com/moby/sys/mountinfo", "0.7.1"),
   381  		goPackage("github.com/moby/sys/userns", "0.1.0"),
   382  		goPackage("github.com/modern-go/concurrent", "0.0.0-20180306012644-bacd9c7ef1dd"),
   383  		goPackage("github.com/modern-go/reflect2", "1.0.2"),
   384  		goPackage("github.com/munnerz/goautoneg", "0.0.0-20191010083416-a7dc8b61c822"),
   385  		goPackage("github.com/ncabatoff/go-seq", "0.0.0-20180805175032-b08ef85ed833"),
   386  		goPackage("github.com/ncabatoff/process-exporter", "0.8.4"),
   387  		goPackage("github.com/opencontainers/runc", "1.2.3"),
   388  		goPackage("github.com/opencontainers/runtime-spec", "1.2.0"),
   389  		goPackage("github.com/pkg/errors", "0.9.1"),
   390  		goPackage("github.com/prometheus/client_golang", "1.20.5"),
   391  		goPackage("github.com/prometheus/client_model", "0.6.1"),
   392  		goPackage("github.com/prometheus/common", "0.61.0"),
   393  		goPackage("github.com/prometheus/procfs", "0.15.1"),
   394  		goPackage("github.com/sirupsen/logrus", "1.9.3"),
   395  		goPackage("github.com/spf13/cobra", "1.8.1"),
   396  		goPackage("github.com/spf13/pflag", "1.0.5"),
   397  		goPackage("github.com/x448/float16", "0.8.4"),
   398  		goPackage("github.com/zakjan/cert-chain-resolver", "0.0.0-20221221105603-fcedb00c5b30"),
   399  		goPackage("go", "1.23.4"),
   400  		goPackage("go.opentelemetry.io/otel", "1.31.0"),
   401  		goPackage("go.opentelemetry.io/otel/trace", "1.31.0"),
   402  		goPackage("golang.org/x/exp", "0.0.0-20240719175910-8a7402abbf56"),
   403  		goPackage("golang.org/x/net", "0.33.0"),
   404  		goPackage("golang.org/x/oauth2", "0.24.0"),
   405  		goPackage("golang.org/x/sys", "0.28.0"),
   406  		goPackage("golang.org/x/term", "0.27.0"),
   407  		goPackage("golang.org/x/text", "0.21.0"),
   408  		goPackage("golang.org/x/time", "0.7.0"),
   409  		goPackage("google.golang.org/protobuf", "1.35.2"),
   410  		goPackage("gopkg.in/evanphx/json-patch.v4", "4.12.0"),
   411  		goPackage("gopkg.in/go-playground/pool.v3", "3.1.1"),
   412  		goPackage("gopkg.in/inf.v0", "0.9.1"),
   413  		goPackage("gopkg.in/mcuadros/go-syslog.v2", "2.3.0"),
   414  		goPackage("gopkg.in/yaml.v3", "3.0.1"),
   415  		goPackage("k8s.io/api", "0.32.0"),
   416  		goPackage("k8s.io/apimachinery", "0.32.0"),
   417  		goPackage("k8s.io/apiserver", "0.32.0"),
   418  		goPackage("k8s.io/client-go", "0.32.0"),
   419  		goPackage("k8s.io/component-base", "0.32.0"),
   420  		goPackage("k8s.io/klog/v2", "2.130.1"),
   421  		goPackage("k8s.io/kube-openapi", "0.0.0-20241105132330-32ad38e42d3f"),
   422  		goPackage("k8s.io/utils", "0.0.0-20241104100929-3ea5e8cea738"),
   423  		goPackage("pault.ag/go/sniff", "0.0.0-20200207005214-cf7e4d167732"),
   424  		goPackage("sigs.k8s.io/json", "0.0.0-20241010143419-9aa6b5e7a4b3"),
   425  		goPackage("sigs.k8s.io/structured-merge-diff/v4", "4.4.2"),
   426  		goPackage("sigs.k8s.io/yaml", "1.4.0"),
   427  	}
   428  
   429  	Toolchain = goPackage("go", "1.22.0")
   430  )
   431  
   432  func goPackage(name, version string) *extractor.Package {
   433  	return &extractor.Package{Name: name, Version: version, PURLType: purl.TypeGolang}
   434  }
   435  
   436  func createPackagesWithMain(pkgs []*extractor.Package, location string) []*extractor.Package {
   437  	res := createPackages(pkgs, location)
   438  	// Main package
   439  	mainName := strings.Split(strings.TrimPrefix(location, "testdata/"), "-")[0]
   440  	res = append(res, &extractor.Package{
   441  		Name: mainName, Version: "(devel)", Locations: []string{location},
   442  		PURLType: purl.TypeGolang,
   443  	})
   444  	return res
   445  }
   446  
   447  func createPackages(pkgs []*extractor.Package, location string) []*extractor.Package {
   448  	res := []*extractor.Package{}
   449  	for _, p := range pkgs {
   450  		res = append(res, &extractor.Package{
   451  			Name:      p.Name,
   452  			Version:   p.Version,
   453  			Locations: []string{location},
   454  			PURLType:  purl.TypeGolang,
   455  		})
   456  	}
   457  	return res
   458  }