github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/golang/parse_go_mod_test.go (about)

     1  package golang
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/anchore/syft/syft/artifact"
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/internal/fileresolver"
    13  	"github.com/anchore/syft/syft/pkg"
    14  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    15  )
    16  
    17  func TestParseGoMod(t *testing.T) {
    18  	tests := []struct {
    19  		fixture  string
    20  		expected []pkg.Package
    21  	}{
    22  		{
    23  			fixture: "test-fixtures/go-mod-fixtures/one-package/go.mod",
    24  			expected: []pkg.Package{
    25  				{
    26  					Name:      "github.com/bmatcuk/doublestar",
    27  					Version:   "v1.3.1",
    28  					PURL:      "pkg:golang/github.com/bmatcuk/doublestar@v1.3.1",
    29  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/one-package/go.mod")),
    30  					Language:  pkg.Go,
    31  					Type:      pkg.GoModulePkg,
    32  					Metadata:  pkg.GolangModuleEntry{},
    33  				},
    34  			},
    35  		},
    36  		{
    37  			fixture: "test-fixtures/go-mod-fixtures/relative-replace/go.mod",
    38  			expected: []pkg.Package{
    39  				{
    40  					Name:      "github.com/aws/aws-sdk-go-v2",
    41  					Version:   "",
    42  					PURL:      "pkg:golang/github.com/aws/aws-sdk-go-v2",
    43  					Language:  pkg.Go,
    44  					Type:      pkg.GoModulePkg,
    45  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/relative-replace/go.mod")),
    46  					Metadata:  pkg.GolangModuleEntry{},
    47  				},
    48  			},
    49  		},
    50  		{
    51  
    52  			fixture: "test-fixtures/go-mod-fixtures/many-packages/go.mod",
    53  			expected: []pkg.Package{
    54  				{
    55  					Name:      "github.com/anchore/archiver/v3",
    56  					Version:   "v3.5.2",
    57  					PURL:      "pkg:golang/github.com/anchore/archiver@v3.5.2#v3",
    58  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
    59  					Language:  pkg.Go,
    60  					Type:      pkg.GoModulePkg,
    61  					Metadata:  pkg.GolangModuleEntry{},
    62  				},
    63  				{
    64  					Name:      "github.com/anchore/go-testutils",
    65  					Version:   "v0.0.0-20200624184116-66aa578126db",
    66  					PURL:      "pkg:golang/github.com/anchore/go-testutils@v0.0.0-20200624184116-66aa578126db",
    67  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
    68  					Language:  pkg.Go,
    69  					Type:      pkg.GoModulePkg,
    70  					Metadata:  pkg.GolangModuleEntry{},
    71  				},
    72  				{
    73  					Name:      "github.com/anchore/go-version",
    74  					Version:   "v1.2.2-0.20200701162849-18adb9c92b9b",
    75  					PURL:      "pkg:golang/github.com/anchore/go-version@v1.2.2-0.20200701162849-18adb9c92b9b",
    76  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
    77  					Language:  pkg.Go,
    78  					Type:      pkg.GoModulePkg,
    79  					Metadata:  pkg.GolangModuleEntry{},
    80  				},
    81  				{
    82  					Name:      "github.com/anchore/stereoscope",
    83  					Version:   "v0.0.0-20200706164556-7cf39d7f4639",
    84  					PURL:      "pkg:golang/github.com/anchore/stereoscope@v0.0.0-20200706164556-7cf39d7f4639",
    85  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
    86  					Language:  pkg.Go,
    87  					Type:      pkg.GoModulePkg,
    88  					Metadata:  pkg.GolangModuleEntry{},
    89  				},
    90  				{
    91  					Name:      "github.com/bmatcuk/doublestar",
    92  					Version:   "v8.8.8",
    93  					PURL:      "pkg:golang/github.com/bmatcuk/doublestar@v8.8.8",
    94  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
    95  					Language:  pkg.Go,
    96  					Type:      pkg.GoModulePkg,
    97  					Metadata:  pkg.GolangModuleEntry{},
    98  				},
    99  				{
   100  					Name:      "github.com/go-test/deep",
   101  					Version:   "v1.0.6",
   102  					PURL:      "pkg:golang/github.com/go-test/deep@v1.0.6",
   103  					Locations: file.NewLocationSet(file.NewLocation("test-fixtures/go-mod-fixtures/many-packages/go.mod")),
   104  					Language:  pkg.Go,
   105  					Type:      pkg.GoModulePkg,
   106  					Metadata:  pkg.GolangModuleEntry{},
   107  				},
   108  			},
   109  		},
   110  	}
   111  
   112  	for _, test := range tests {
   113  		t.Run(test.fixture, func(t *testing.T) {
   114  			c := newGoModCataloger(DefaultCatalogerConfig())
   115  			pkgtest.NewCatalogTester().
   116  				FromFile(t, test.fixture).
   117  				Expects(test.expected, nil).
   118  				WithResolver(fileresolver.Empty{}).
   119  				TestParser(t, c.parseGoModFile)
   120  		})
   121  	}
   122  }
   123  
   124  func Test_GoSumHashes(t *testing.T) {
   125  	tests := []struct {
   126  		fixture  string
   127  		expected []pkg.Package
   128  	}{
   129  		{
   130  			fixture: "test-fixtures/go-sum-hashes",
   131  			expected: []pkg.Package{
   132  				{
   133  					Name:      "github.com/CycloneDX/cyclonedx-go",
   134  					Version:   "v0.6.0",
   135  					PURL:      "pkg:golang/github.com/CycloneDX/cyclonedx-go@v0.6.0",
   136  					Locations: file.NewLocationSet(file.NewLocation("go.mod")),
   137  					FoundBy:   "go-module-file-cataloger",
   138  					Language:  pkg.Go,
   139  					Type:      pkg.GoModulePkg,
   140  					Metadata:  pkg.GolangModuleEntry{},
   141  				},
   142  				{
   143  					Name:      "github.com/acarl005/stripansi",
   144  					Version:   "v0.0.0-20180116102854-5a71ef0e047d",
   145  					PURL:      "pkg:golang/github.com/acarl005/stripansi@v0.0.0-20180116102854-5a71ef0e047d",
   146  					Locations: file.NewLocationSet(file.NewLocation("go.mod")),
   147  					FoundBy:   "go-module-file-cataloger",
   148  					Language:  pkg.Go,
   149  					Type:      pkg.GoModulePkg,
   150  					Metadata: pkg.GolangModuleEntry{
   151  						H1Digest: "h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=",
   152  					},
   153  				},
   154  				{
   155  					Name:      "github.com/mgutz/ansi",
   156  					Version:   "v0.0.0-20200706080929-d51e80ef957d",
   157  					PURL:      "pkg:golang/github.com/mgutz/ansi@v0.0.0-20200706080929-d51e80ef957d",
   158  					Locations: file.NewLocationSet(file.NewLocation("go.mod")),
   159  					FoundBy:   "go-module-file-cataloger",
   160  					Language:  pkg.Go,
   161  					Type:      pkg.GoModulePkg,
   162  					Metadata: pkg.GolangModuleEntry{
   163  						H1Digest: "h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=",
   164  					},
   165  				},
   166  			},
   167  		},
   168  	}
   169  
   170  	for _, test := range tests {
   171  		t.Run(test.fixture, func(t *testing.T) {
   172  			pkgtest.NewCatalogTester().
   173  				FromDirectory(t, test.fixture).
   174  				Expects(test.expected, nil).
   175  				TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
   176  		})
   177  	}
   178  }
   179  
   180  func Test_corruptGoMod(t *testing.T) {
   181  	c := NewGoModuleFileCataloger(DefaultCatalogerConfig().WithSearchRemoteLicenses(false))
   182  	pkgtest.NewCatalogTester().
   183  		FromDirectory(t, "test-fixtures/corrupt").
   184  		WithError().
   185  		TestCataloger(t, c)
   186  }
   187  
   188  func Test_parseGoSource_packageResolution(t *testing.T) {
   189  	tests := []struct {
   190  		name             string
   191  		fixturePath      string
   192  		config           CatalogerConfig
   193  		expectedPkgs     []string
   194  		expectedRels     []string
   195  		expectedLicenses map[string][]string
   196  	}{
   197  		{
   198  			name:        "go-source with direct, transitive, and deps of transitive",
   199  			fixturePath: filepath.Join("test-fixtures", "go-source"),
   200  			expectedPkgs: []string{
   201  				"anchore.io/not/real @  (go.mod)",
   202  				"github.com/davecgh/go-spew @ v1.1.1 (go.mod)",
   203  				"github.com/go-viper/mapstructure/v2 @ v2.2.1 (go.mod)",
   204  				"github.com/google/uuid @ v1.6.0 (go.mod)",
   205  				"github.com/pmezard/go-difflib @ v1.0.0 (go.mod)",
   206  				"github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
   207  				"github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
   208  				"github.com/sourcegraph/conc @ v0.3.0 (go.mod)",
   209  				"github.com/spf13/afero @ v1.12.0 (go.mod)",
   210  				"github.com/spf13/cast @ v1.7.1 (go.mod)",
   211  				"github.com/spf13/pflag @ v1.0.6 (go.mod)",
   212  				"github.com/spf13/viper @ v1.20.1 (go.mod)",
   213  				"github.com/stretchr/testify @ v1.10.0 (go.mod)",
   214  				"github.com/subosito/gotenv @ v1.6.0 (go.mod)",
   215  				"go.uber.org/multierr @ v1.10.0 (go.mod)",
   216  				"go.uber.org/zap @ v1.27.0 (go.mod)",
   217  				"golang.org/x/sys @ v0.33.0 (go.mod)",
   218  				"golang.org/x/text @ v0.21.0 (go.mod)",
   219  				"gopkg.in/yaml.v3 @ v3.0.1 (go.mod)",
   220  				"github.com/fsnotify/fsnotify @ v1.8.0 (go.mod)",
   221  				"github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod)",
   222  				"github.com/frankban/quicktest @ v1.14.6 (go.mod)",
   223  				"github.com/google/go-cmp @ v0.6.0 (go.mod)",
   224  				"github.com/kr/pretty @ v0.3.1 (go.mod)",
   225  				"github.com/kr/text @ v0.2.0 (go.mod)",
   226  				"github.com/rogpeppe/go-internal @ v1.9.0 (go.mod)",
   227  				"go.uber.org/goleak @ v1.3.0 (go.mod)",
   228  				"gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod)",
   229  			},
   230  			expectedRels: []string{
   231  				"github.com/davecgh/go-spew @ v1.1.1 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
   232  				"github.com/frankban/quicktest @ v1.14.6 (go.mod) [dependency-of] github.com/spf13/cast @ v1.7.1 (go.mod)",
   233  				"github.com/fsnotify/fsnotify @ v1.8.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   234  				"github.com/go-viper/mapstructure/v2 @ v2.2.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   235  				"github.com/google/go-cmp @ v0.6.0 (go.mod) [dependency-of] github.com/frankban/quicktest @ v1.14.6 (go.mod)",
   236  				"github.com/google/uuid @ v1.6.0 (go.mod) [dependency-of] anchore.io/not/real @  (go.mod)",
   237  				"github.com/kr/pretty @ v0.3.1 (go.mod) [dependency-of] github.com/frankban/quicktest @ v1.14.6 (go.mod)",
   238  				"github.com/kr/pretty @ v0.3.1 (go.mod) [dependency-of] gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod)",
   239  				"github.com/kr/text @ v0.2.0 (go.mod) [dependency-of] github.com/kr/pretty @ v0.3.1 (go.mod)",
   240  				"github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   241  				"github.com/pmezard/go-difflib @ v1.0.0 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
   242  				"github.com/rogpeppe/go-internal @ v1.9.0 (go.mod) [dependency-of] github.com/kr/pretty @ v0.3.1 (go.mod)",
   243  				"github.com/sagikazarmark/locafero @ v0.7.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   244  				"github.com/sirupsen/logrus @ v1.9.3 (go.mod) [dependency-of] anchore.io/not/real @  (go.mod)",
   245  				"github.com/sourcegraph/conc @ v0.3.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
   246  				"github.com/spf13/afero @ v1.12.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
   247  				"github.com/spf13/afero @ v1.12.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   248  				"github.com/spf13/cast @ v1.7.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   249  				"github.com/spf13/pflag @ v1.0.6 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   250  				"github.com/spf13/viper @ v1.20.1 (go.mod) [dependency-of] anchore.io/not/real @  (go.mod)",
   251  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] anchore.io/not/real @  (go.mod)",
   252  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/pelletier/go-toml/v2 @ v2.2.3 (go.mod)",
   253  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sagikazarmark/locafero @ v0.7.0 (go.mod)",
   254  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
   255  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/sourcegraph/conc @ v0.3.0 (go.mod)",
   256  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   257  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] github.com/subosito/gotenv @ v1.6.0 (go.mod)",
   258  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] go.uber.org/multierr @ v1.10.0 (go.mod)",
   259  				"github.com/stretchr/testify @ v1.10.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
   260  				"github.com/subosito/gotenv @ v1.6.0 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   261  				"go.uber.org/goleak @ v1.3.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
   262  				"go.uber.org/multierr @ v1.10.0 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
   263  				"go.uber.org/zap @ v1.27.0 (go.mod) [dependency-of] anchore.io/not/real @  (go.mod)",
   264  				"golang.org/x/sys @ v0.33.0 (go.mod) [dependency-of] github.com/fsnotify/fsnotify @ v1.8.0 (go.mod)",
   265  				"golang.org/x/sys @ v0.33.0 (go.mod) [dependency-of] github.com/sirupsen/logrus @ v1.9.3 (go.mod)",
   266  				"golang.org/x/text @ v0.21.0 (go.mod) [dependency-of] github.com/spf13/afero @ v1.12.0 (go.mod)",
   267  				"golang.org/x/text @ v0.21.0 (go.mod) [dependency-of] github.com/subosito/gotenv @ v1.6.0 (go.mod)",
   268  				"gopkg.in/check.v1 @ v1.0.0-20190902080502-41f04d3bba15 (go.mod) [dependency-of] gopkg.in/yaml.v3 @ v3.0.1 (go.mod)",
   269  				"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] github.com/spf13/viper @ v1.20.1 (go.mod)",
   270  				"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] github.com/stretchr/testify @ v1.10.0 (go.mod)",
   271  				"gopkg.in/yaml.v3 @ v3.0.1 (go.mod) [dependency-of] go.uber.org/zap @ v1.27.0 (go.mod)",
   272  			},
   273  			expectedLicenses: map[string][]string{
   274  				"github.com/fsnotify/fsnotify":        {"BSD-3-Clause"},
   275  				"github.com/go-viper/mapstructure/v2": {"MIT"},
   276  				"github.com/google/uuid":              {"BSD-3-Clause"},
   277  				"github.com/pelletier/go-toml/v2":     {"MIT"},
   278  				"github.com/sagikazarmark/locafero":   {"MIT"},
   279  				"github.com/sirupsen/logrus":          {"MIT"},
   280  				"github.com/sourcegraph/conc":         {"MIT"},
   281  				"github.com/spf13/afero":              {"Apache-2.0"},
   282  				"github.com/spf13/cast":               {"MIT"},
   283  				"github.com/spf13/pflag":              {"BSD-3-Clause"},
   284  				"github.com/spf13/viper":              {"MIT"},
   285  				"github.com/subosito/gotenv":          {"MIT"},
   286  				"go.uber.org/multierr":                {"MIT"},
   287  				"go.uber.org/zap":                     {"MIT"},
   288  				"golang.org/x/sys":                    {"BSD-3-Clause"},
   289  				"golang.org/x/text":                   {"BSD-3-Clause"},
   290  				"gopkg.in/yaml.v3":                    {"Apache-2.0", "MIT"},
   291  				"github.com/davecgh/go-spew":          {"ISC"},
   292  				"github.com/pmezard/go-difflib":       {"BSD-3-Clause"},
   293  				"github.com/stretchr/testify":         {"MIT"},
   294  				"github.com/frankban/quicktest":       {"MIT"},
   295  				"github.com/google/go-cmp":            {"BSD-3-Clause"},
   296  				"github.com/kr/text":                  {"MIT"},
   297  				"github.com/kr/pretty":                {"MIT"},
   298  				"github.com/rogpeppe/go-internal":     {"BSD-3-Clause"},
   299  				"go.uber.org/goleak":                  {"MIT"},
   300  				"gopkg.in/check.v1":                   {"BSD-2-Clause"},
   301  			},
   302  		},
   303  	}
   304  
   305  	for _, tt := range tests {
   306  		t.Run(tt.name, func(t *testing.T) {
   307  			pkgtest.NewCatalogTester().
   308  				FromDirectory(t, tt.fixturePath).
   309  				ExpectsPackageStrings(tt.expectedPkgs).
   310  				ExpectsRelationshipStrings(tt.expectedRels).
   311  				ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
   312  					for _, p := range pkgs {
   313  						if metadata, ok := p.Metadata.(pkg.GolangSourceEntry); ok {
   314  							// Validate that GolangSourceEntry metadata is present but don't assert on specific field values
   315  							// since these might vary across development machines
   316  							require.IsType(t, pkg.GolangSourceEntry{}, metadata, "expected GolangSourceEntry metadata for package %s", p.Name)
   317  							// Verify that the metadata struct is populated (non-zero values indicate go source method was used)
   318  							require.NotEmpty(t, metadata, "GolangSourceEntry metadata should not be empty for package %s", p.Name)
   319  						}
   320  					}
   321  				}).
   322  				ExpectsAssertion(func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
   323  					actualLicenses := make(map[string][]string)
   324  					for _, p := range pkgs {
   325  						for _, l := range p.Licenses.ToSlice() {
   326  							if actualLicenses[p.Name] == nil {
   327  								actualLicenses[p.Name] = make([]string, 0)
   328  							}
   329  							actualLicenses[p.Name] = append(actualLicenses[p.Name], l.Value)
   330  						}
   331  					}
   332  					if diff := cmp.Diff(tt.expectedLicenses, actualLicenses); diff != "" {
   333  						t.Errorf("mismatch in licenses (-want +got):\n%s", diff)
   334  					}
   335  				}).
   336  				TestCataloger(t, NewGoModuleFileCataloger(CatalogerConfig{}))
   337  		})
   338  	}
   339  }