github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/purl/purl_test.go (about)

     1  package purl_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	v1 "github.com/google/go-containerregistry/pkg/v1"
     7  	"github.com/package-url/packageurl-go"
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    12  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    13  	"github.com/devseccon/trivy/pkg/purl"
    14  	"github.com/devseccon/trivy/pkg/types"
    15  )
    16  
    17  func TestNewPackageURL(t *testing.T) {
    18  	testCases := []struct {
    19  		name     string
    20  		typ      ftypes.TargetType
    21  		pkg      ftypes.Package
    22  		metadata types.Metadata
    23  		want     *purl.PackageURL
    24  		wantErr  string
    25  	}{
    26  		{
    27  			name: "maven package",
    28  			typ:  ftypes.Jar,
    29  			pkg: ftypes.Package{
    30  				Name:    "org.springframework:spring-core",
    31  				Version: "5.3.14",
    32  			},
    33  			want: &purl.PackageURL{
    34  				PackageURL: packageurl.PackageURL{
    35  					Type:      packageurl.TypeMaven,
    36  					Namespace: "org.springframework",
    37  					Name:      "spring-core",
    38  					Version:   "5.3.14",
    39  				},
    40  			},
    41  		},
    42  		{
    43  			name: "gradle package",
    44  			typ:  ftypes.Gradle,
    45  			pkg: ftypes.Package{
    46  				Name:    "org.springframework:spring-core",
    47  				Version: "5.3.14",
    48  			},
    49  			want: &purl.PackageURL{
    50  				PackageURL: packageurl.PackageURL{
    51  					Type:      packageurl.TypeMaven,
    52  					Namespace: "org.springframework",
    53  					Name:      "spring-core",
    54  					Version:   "5.3.14",
    55  				},
    56  			},
    57  		},
    58  		{
    59  			name: "yarn package",
    60  			typ:  ftypes.Yarn,
    61  			pkg: ftypes.Package{
    62  				Name:    "@xtuc/ieee754",
    63  				Version: "1.2.0",
    64  			},
    65  			want: &purl.PackageURL{
    66  				PackageURL: packageurl.PackageURL{
    67  					Type:      packageurl.TypeNPM,
    68  					Namespace: "@xtuc",
    69  					Name:      "ieee754",
    70  					Version:   "1.2.0",
    71  				},
    72  			},
    73  		},
    74  		{
    75  			name: "yarn package with non-namespace",
    76  			typ:  ftypes.Yarn,
    77  			pkg: ftypes.Package{
    78  				Name:    "lodash",
    79  				Version: "4.17.21",
    80  			},
    81  			want: &purl.PackageURL{
    82  				PackageURL: packageurl.PackageURL{
    83  					Type:    packageurl.TypeNPM,
    84  					Name:    "lodash",
    85  					Version: "4.17.21",
    86  				},
    87  			},
    88  		},
    89  		{
    90  			name: "pnpm package",
    91  			typ:  ftypes.Pnpm,
    92  			pkg: ftypes.Package{
    93  				Name:    "@xtuc/ieee754",
    94  				Version: "1.2.0",
    95  			},
    96  			want: &purl.PackageURL{
    97  				PackageURL: packageurl.PackageURL{
    98  					Type:      packageurl.TypeNPM,
    99  					Namespace: "@xtuc",
   100  					Name:      "ieee754",
   101  					Version:   "1.2.0",
   102  				},
   103  			},
   104  		},
   105  		{
   106  			name: "pnpm package with non-namespace",
   107  			typ:  ftypes.Pnpm,
   108  			pkg: ftypes.Package{
   109  				Name:    "lodash",
   110  				Version: "4.17.21",
   111  			},
   112  			want: &purl.PackageURL{
   113  				PackageURL: packageurl.PackageURL{
   114  					Type:    packageurl.TypeNPM,
   115  					Name:    "lodash",
   116  					Version: "4.17.21",
   117  				},
   118  			},
   119  		},
   120  		{
   121  			name: "pypi package",
   122  			typ:  ftypes.PythonPkg,
   123  			pkg: ftypes.Package{
   124  				Name:    "Django_test",
   125  				Version: "1.2.0",
   126  			},
   127  			want: &purl.PackageURL{
   128  				PackageURL: packageurl.PackageURL{
   129  					Type:    packageurl.TypePyPi,
   130  					Name:    "django-test",
   131  					Version: "1.2.0",
   132  				},
   133  			},
   134  		},
   135  		{
   136  			name: "conda package",
   137  			typ:  ftypes.CondaPkg,
   138  			pkg: ftypes.Package{
   139  				Name:    "absl-py",
   140  				Version: "0.4.1",
   141  			},
   142  			want: &purl.PackageURL{
   143  				PackageURL: packageurl.PackageURL{
   144  					Type:    packageurl.TypeConda,
   145  					Name:    "absl-py",
   146  					Version: "0.4.1",
   147  				},
   148  			},
   149  		},
   150  		{
   151  			name: "composer package",
   152  			typ:  ftypes.Composer,
   153  			pkg: ftypes.Package{
   154  				Name:    "symfony/contracts",
   155  				Version: "v1.0.2",
   156  			},
   157  			want: &purl.PackageURL{
   158  				PackageURL: packageurl.PackageURL{
   159  					Type:      packageurl.TypeComposer,
   160  					Namespace: "symfony",
   161  					Name:      "contracts",
   162  					Version:   "v1.0.2",
   163  				},
   164  			},
   165  		},
   166  		{
   167  			name: "golang package",
   168  			typ:  ftypes.GoModule,
   169  			pkg: ftypes.Package{
   170  				Name:    "github.com/go-sql-driver/Mysql",
   171  				Version: "v1.5.0",
   172  			},
   173  			want: &purl.PackageURL{
   174  				PackageURL: packageurl.PackageURL{
   175  					Type:      packageurl.TypeGolang,
   176  					Namespace: "github.com/go-sql-driver",
   177  					Name:      "mysql",
   178  					Version:   "v1.5.0",
   179  				},
   180  			},
   181  		},
   182  		{
   183  			name: "golang package with a local path",
   184  			typ:  ftypes.GoModule,
   185  			pkg: ftypes.Package{
   186  				Name:    "./private_repos/cnrm.googlesource.com/cnrm/",
   187  				Version: "(devel)",
   188  			},
   189  			want: nil,
   190  		},
   191  		{
   192  			name: "hex package",
   193  			typ:  ftypes.Hex,
   194  			pkg: ftypes.Package{
   195  				ID:      "bunt@0.2.0",
   196  				Name:    "bunt",
   197  				Version: "0.2.0",
   198  				Locations: []ftypes.Location{
   199  					{
   200  						StartLine: 2,
   201  						EndLine:   2,
   202  					},
   203  				},
   204  			},
   205  			want: &purl.PackageURL{
   206  				PackageURL: packageurl.PackageURL{
   207  					Type:    packageurl.TypeHex,
   208  					Name:    "bunt",
   209  					Version: "0.2.0",
   210  				},
   211  			},
   212  		},
   213  		{
   214  			name: "dart package",
   215  			typ:  ftypes.Pub,
   216  			pkg: ftypes.Package{
   217  				Name:    "http",
   218  				Version: "0.13.2",
   219  			},
   220  			want: &purl.PackageURL{
   221  				PackageURL: packageurl.PackageURL{
   222  					Type:    purl.TypeDart,
   223  					Name:    "http",
   224  					Version: "0.13.2",
   225  				},
   226  			},
   227  		},
   228  		{
   229  			name: "swift package",
   230  			typ:  ftypes.Swift,
   231  			pkg: ftypes.Package{
   232  				ID:      "github.com/apple/swift-atomics@1.1.0",
   233  				Name:    "github.com/apple/swift-atomics",
   234  				Version: "1.1.0",
   235  			},
   236  			want: &purl.PackageURL{
   237  				PackageURL: packageurl.PackageURL{
   238  					Type:      packageurl.TypeSwift,
   239  					Namespace: "github.com/apple",
   240  					Name:      "swift-atomics",
   241  					Version:   "1.1.0",
   242  				},
   243  			},
   244  		},
   245  		{
   246  			name: "cocoapods package",
   247  			typ:  ftypes.Cocoapods,
   248  			pkg: ftypes.Package{
   249  				ID:      "GoogleUtilities/NSData+zlib@7.5.2",
   250  				Name:    "GoogleUtilities/NSData+zlib",
   251  				Version: "7.5.2",
   252  			},
   253  			want: &purl.PackageURL{
   254  				PackageURL: packageurl.PackageURL{
   255  					Type:    packageurl.TypeCocoapods,
   256  					Name:    "GoogleUtilities",
   257  					Version: "7.5.2",
   258  					Subpath: "NSData+zlib",
   259  				},
   260  			},
   261  		},
   262  		{
   263  			name: "rust binary",
   264  			typ:  ftypes.RustBinary,
   265  			pkg: ftypes.Package{
   266  				ID:      "abomination@0.7.3",
   267  				Name:    "abomination",
   268  				Version: "0.7.3",
   269  			},
   270  			want: &purl.PackageURL{
   271  				PackageURL: packageurl.PackageURL{
   272  					Type:    packageurl.TypeCargo,
   273  					Name:    "abomination",
   274  					Version: "0.7.3",
   275  				},
   276  			},
   277  		},
   278  		{
   279  			name: "os package",
   280  			typ:  ftypes.RedHat,
   281  			pkg: ftypes.Package{
   282  				Name:            "acl",
   283  				Version:         "2.2.53",
   284  				Release:         "1.el8",
   285  				Epoch:           1,
   286  				Arch:            "aarch64",
   287  				SrcName:         "acl",
   288  				SrcVersion:      "2.2.53",
   289  				SrcRelease:      "1.el8",
   290  				SrcEpoch:        1,
   291  				Modularitylabel: "",
   292  			},
   293  
   294  			metadata: types.Metadata{
   295  				OS: &ftypes.OS{
   296  					Family: ftypes.RedHat,
   297  					Name:   "8",
   298  				},
   299  			},
   300  			want: &purl.PackageURL{
   301  				PackageURL: packageurl.PackageURL{
   302  					Type:      packageurl.TypeRPM,
   303  					Namespace: "redhat",
   304  					Name:      "acl",
   305  					Version:   "2.2.53-1.el8",
   306  					Qualifiers: packageurl.Qualifiers{
   307  						{
   308  							Key:   "arch",
   309  							Value: "aarch64",
   310  						},
   311  						{
   312  							Key:   "epoch",
   313  							Value: "1",
   314  						},
   315  						{
   316  							Key:   "distro",
   317  							Value: "redhat-8",
   318  						},
   319  					},
   320  				},
   321  			},
   322  		},
   323  		{
   324  			name: "container",
   325  			typ:  purl.TypeOCI,
   326  			metadata: types.Metadata{
   327  				RepoTags: []string{
   328  					"cblmariner2preview.azurecr.io/base/core:2.0.20220124-amd64",
   329  				},
   330  				RepoDigests: []string{
   331  					"cblmariner2preview.azurecr.io/base/core@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
   332  					"cblmariner2preview.azurecr.io/base/core@sha256:016bb1f5735e43b2738cd3fd1979b62608fe1727132b2506c17ba0e1f6a6ed8a",
   333  				},
   334  				ImageConfig: v1.ConfigFile{
   335  					Architecture: "amd64",
   336  				},
   337  			},
   338  			want: &purl.PackageURL{
   339  				PackageURL: packageurl.PackageURL{
   340  					Type:      packageurl.TypeOCI,
   341  					Namespace: "",
   342  					Name:      "core",
   343  					Version:   "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
   344  					Qualifiers: packageurl.Qualifiers{
   345  						{
   346  							Key:   "repository_url",
   347  							Value: "cblmariner2preview.azurecr.io/base/core",
   348  						},
   349  						{
   350  							Key:   "arch",
   351  							Value: "amd64",
   352  						},
   353  					},
   354  				},
   355  			},
   356  		},
   357  		{
   358  			name: "container local",
   359  			typ:  purl.TypeOCI,
   360  			metadata: types.Metadata{
   361  				RepoTags:    []string{},
   362  				RepoDigests: []string{},
   363  				ImageConfig: v1.ConfigFile{
   364  					Architecture: "amd64",
   365  				},
   366  				ImageID: "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
   367  			},
   368  			want: nil,
   369  		},
   370  		{
   371  			name: "container with implicit registry",
   372  			typ:  purl.TypeOCI,
   373  			metadata: types.Metadata{
   374  				RepoTags: []string{
   375  					"alpine:3.14",
   376  					"alpine:latest",
   377  				},
   378  				RepoDigests: []string{
   379  					"alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
   380  					"alpine:latest@sha256:016bb1f5735e43b2738cd3fd1979b62608fe1727132b2506c17ba0e1f6a6ed8a",
   381  				},
   382  				ImageConfig: v1.ConfigFile{
   383  					Architecture: "amd64",
   384  				},
   385  			},
   386  			want: &purl.PackageURL{
   387  				PackageURL: packageurl.PackageURL{
   388  					Type:      packageurl.TypeOCI,
   389  					Namespace: "",
   390  					Name:      "alpine",
   391  					Version:   "sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
   392  					Qualifiers: packageurl.Qualifiers{
   393  						{
   394  							Key:   "repository_url",
   395  							Value: "index.docker.io/library/alpine",
   396  						},
   397  						{
   398  							Key:   "arch",
   399  							Value: "amd64",
   400  						},
   401  					},
   402  				},
   403  			},
   404  		},
   405  		{
   406  			name: "sad path",
   407  			typ:  purl.TypeOCI,
   408  			metadata: types.Metadata{
   409  				RepoTags: []string{
   410  					"cblmariner2preview.azurecr.io/base/core:2.0.20220124-amd64",
   411  				},
   412  				RepoDigests: []string{
   413  					"sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260",
   414  				},
   415  			},
   416  			wantErr: "failed to parse digest",
   417  		},
   418  	}
   419  
   420  	for _, tc := range testCases {
   421  		t.Run(tc.name, func(t *testing.T) {
   422  			packageURL, err := purl.NewPackageURL(tc.typ, tc.metadata, tc.pkg)
   423  			if tc.wantErr != "" {
   424  				require.Error(t, err)
   425  				assert.Contains(t, err.Error(), tc.wantErr)
   426  				return
   427  			}
   428  			assert.NoError(t, err)
   429  			assert.Equal(t, tc.want, packageURL, tc.name)
   430  		})
   431  	}
   432  }
   433  
   434  func TestFromString(t *testing.T) {
   435  	testCases := []struct {
   436  		name    string
   437  		purl    string
   438  		want    purl.PackageURL
   439  		wantErr string
   440  	}{
   441  		{
   442  			name: "happy path for maven",
   443  			purl: "pkg:maven/org.springframework/spring-core@5.0.4.RELEASE",
   444  			want: purl.PackageURL{
   445  				PackageURL: packageurl.PackageURL{
   446  					Type:       packageurl.TypeMaven,
   447  					Namespace:  "org.springframework",
   448  					Version:    "5.0.4.RELEASE",
   449  					Name:       "spring-core",
   450  					Qualifiers: packageurl.Qualifiers{},
   451  				},
   452  				FilePath: "",
   453  			},
   454  		},
   455  		{
   456  			name: "happy path for npm",
   457  			purl: "pkg:npm/bootstrap@5.0.2?file_path=app%2Fapp%2Fpackage.json",
   458  			want: purl.PackageURL{
   459  				PackageURL: packageurl.PackageURL{
   460  					Type:    packageurl.TypeNPM,
   461  					Name:    "bootstrap",
   462  					Version: "5.0.2",
   463  					Qualifiers: packageurl.Qualifiers{
   464  						{
   465  							Key:   "file_path",
   466  							Value: "app/app/package.json",
   467  						},
   468  					},
   469  				},
   470  			},
   471  		},
   472  		{
   473  			name: "happy path for coocapods",
   474  			purl: "pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib",
   475  			want: purl.PackageURL{
   476  				PackageURL: packageurl.PackageURL{
   477  					Type:       packageurl.TypeCocoapods,
   478  					Name:       "GoogleUtilities",
   479  					Version:    "7.5.2",
   480  					Subpath:    "NSData+zlib",
   481  					Qualifiers: packageurl.Qualifiers{},
   482  				},
   483  			},
   484  		},
   485  		{
   486  			name: "happy path for hex",
   487  			purl: "pkg:hex/plug@1.14.0",
   488  			want: purl.PackageURL{
   489  				PackageURL: packageurl.PackageURL{
   490  					Type:       packageurl.TypeHex,
   491  					Name:       "plug",
   492  					Version:    "1.14.0",
   493  					Qualifiers: packageurl.Qualifiers{},
   494  				},
   495  			},
   496  		},
   497  		{
   498  			name: "happy path for dart",
   499  			purl: "pkg:dart/http@0.13.2",
   500  			want: purl.PackageURL{
   501  				PackageURL: packageurl.PackageURL{
   502  					Type:       purl.TypeDart,
   503  					Name:       "http",
   504  					Version:    "0.13.2",
   505  					Qualifiers: packageurl.Qualifiers{},
   506  				},
   507  			},
   508  		},
   509  		{
   510  			name: "happy path for apk",
   511  			purl: "pkg:apk/alpine/alpine-baselayout@3.2.0-r16?distro=3.14.2&epoch=1",
   512  			want: purl.PackageURL{
   513  				PackageURL: packageurl.PackageURL{
   514  					Type:      string(analyzer.TypeApk),
   515  					Namespace: "alpine",
   516  					Name:      "alpine-baselayout",
   517  					Version:   "3.2.0-r16",
   518  					Qualifiers: packageurl.Qualifiers{
   519  						{
   520  							Key:   "distro",
   521  							Value: "3.14.2",
   522  						},
   523  						{
   524  							Key:   "epoch",
   525  							Value: "1",
   526  						},
   527  					},
   528  				},
   529  			},
   530  		},
   531  		{
   532  			name: "happy path for rpm",
   533  			purl: "pkg:rpm/redhat/containers-common@0.1.14",
   534  			want: purl.PackageURL{
   535  				PackageURL: packageurl.PackageURL{
   536  					Type:       packageurl.TypeRPM,
   537  					Namespace:  "redhat",
   538  					Name:       "containers-common",
   539  					Version:    "0.1.14",
   540  					Qualifiers: packageurl.Qualifiers{},
   541  				},
   542  			},
   543  		},
   544  		{
   545  			name: "happy path for conda",
   546  			purl: "pkg:conda/absl-py@0.4.1",
   547  			want: purl.PackageURL{
   548  				PackageURL: packageurl.PackageURL{
   549  					Type:       packageurl.TypeConda,
   550  					Name:       "absl-py",
   551  					Version:    "0.4.1",
   552  					Qualifiers: packageurl.Qualifiers{},
   553  				},
   554  			},
   555  		},
   556  		{
   557  			name: "bad rpm",
   558  			purl: "pkg:rpm/redhat/a--@1.0.0",
   559  			want: purl.PackageURL{
   560  				PackageURL: packageurl.PackageURL{
   561  					Type:       packageurl.TypeRPM,
   562  					Namespace:  "redhat",
   563  					Name:       "a--",
   564  					Version:    "1.0.0",
   565  					Qualifiers: packageurl.Qualifiers{},
   566  				},
   567  			},
   568  		},
   569  	}
   570  
   571  	for _, tc := range testCases {
   572  		t.Run(tc.name, func(t *testing.T) {
   573  			pkg, err := purl.FromString(tc.purl)
   574  			if tc.wantErr != "" {
   575  				require.Error(t, err)
   576  				assert.Contains(t, err.Error(), tc.wantErr)
   577  				return
   578  			}
   579  			assert.NoError(t, err)
   580  			assert.Equal(t, tc.want, *pkg, tc.name)
   581  		})
   582  	}
   583  }
   584  
   585  func TestPackage(t *testing.T) {
   586  	tests := []struct {
   587  		name    string
   588  		pkgURL  *purl.PackageURL
   589  		wantPkg *ftypes.Package
   590  	}{
   591  		{
   592  			name: "rpm + Qualifiers",
   593  			pkgURL: &purl.PackageURL{
   594  				PackageURL: packageurl.PackageURL{
   595  					Type:      packageurl.TypeRPM,
   596  					Namespace: "redhat",
   597  					Name:      "nodejs-full-i18n",
   598  					Version:   "10.21.0-3.module_el8.2.0+391+8da3adc6",
   599  					Qualifiers: packageurl.Qualifiers{
   600  						{
   601  							Key:   "arch",
   602  							Value: "x86_64",
   603  						},
   604  						{
   605  							Key:   "epoch",
   606  							Value: "1",
   607  						},
   608  						{
   609  							Key:   "modularitylabel",
   610  							Value: "nodejs:10:8020020200707141642:6a468ee4",
   611  						},
   612  						{
   613  							Key:   "distro",
   614  							Value: "redhat-8",
   615  						},
   616  					},
   617  				},
   618  			},
   619  			wantPkg: &ftypes.Package{
   620  				Name:            "nodejs-full-i18n",
   621  				Version:         "10.21.0",
   622  				Release:         "3.module_el8.2.0+391+8da3adc6",
   623  				Arch:            "x86_64",
   624  				Epoch:           1,
   625  				Modularitylabel: "nodejs:10:8020020200707141642:6a468ee4",
   626  			},
   627  		},
   628  		{
   629  			name: "composer with namespace",
   630  			pkgURL: &purl.PackageURL{
   631  				PackageURL: packageurl.PackageURL{
   632  					Type:      packageurl.TypeComposer,
   633  					Namespace: "symfony",
   634  					Name:      "contracts",
   635  					Version:   "v1.0.2",
   636  				},
   637  			},
   638  			wantPkg: &ftypes.Package{
   639  				Name:    "symfony/contracts",
   640  				Version: "v1.0.2",
   641  			},
   642  		},
   643  		{
   644  			name: "maven with namespace",
   645  			pkgURL: &purl.PackageURL{
   646  				PackageURL: packageurl.PackageURL{
   647  					Type:       packageurl.TypeMaven,
   648  					Namespace:  "org.springframework",
   649  					Name:       "spring-core",
   650  					Version:    "5.0.4.RELEASE",
   651  					Qualifiers: packageurl.Qualifiers{},
   652  				},
   653  			},
   654  			wantPkg: &ftypes.Package{
   655  				Name:    "org.springframework:spring-core",
   656  				Version: "5.0.4.RELEASE",
   657  			},
   658  		},
   659  		{
   660  			name: "cocoapods with subpath",
   661  			pkgURL: &purl.PackageURL{
   662  				PackageURL: packageurl.PackageURL{
   663  					Type:       packageurl.TypeCocoapods,
   664  					Version:    "4.2.0",
   665  					Name:       "AppCenter",
   666  					Subpath:    "Analytics",
   667  					Qualifiers: packageurl.Qualifiers{},
   668  				},
   669  			},
   670  			wantPkg: &ftypes.Package{
   671  				Name:    "AppCenter/Analytics",
   672  				Version: "4.2.0",
   673  			},
   674  		},
   675  		{
   676  			name: "wrong epoch",
   677  			pkgURL: &purl.PackageURL{
   678  				PackageURL: packageurl.PackageURL{
   679  					Type:      packageurl.TypeRPM,
   680  					Namespace: "redhat",
   681  					Name:      "acl",
   682  					Version:   "2.2.53-1.el8",
   683  					Qualifiers: packageurl.Qualifiers{
   684  						{
   685  							Key:   "epoch",
   686  							Value: "wrong",
   687  						},
   688  					},
   689  				},
   690  			},
   691  			wantPkg: &ftypes.Package{
   692  				Name:    "acl",
   693  				Version: "2.2.53",
   694  				Release: "1.el8",
   695  			},
   696  		},
   697  	}
   698  
   699  	for _, tt := range tests {
   700  		t.Run(tt.name, func(t *testing.T) {
   701  			got := tt.pkgURL.Package()
   702  			assert.Equal(t, tt.wantPkg, got)
   703  		})
   704  	}
   705  }
   706  
   707  func TestPackageURL_LangType(t *testing.T) {
   708  	tests := []struct {
   709  		name string
   710  		purl packageurl.PackageURL
   711  		want ftypes.LangType
   712  	}{
   713  		{
   714  			name: "maven",
   715  			purl: packageurl.PackageURL{
   716  				Type:      packageurl.TypeMaven,
   717  				Namespace: "org.springframework",
   718  				Name:      "spring-core",
   719  				Version:   "5.0.4.RELEASE",
   720  			},
   721  			want: ftypes.Jar,
   722  		},
   723  		{
   724  			name: "k8s",
   725  			purl: packageurl.PackageURL{
   726  				Type:    purl.TypeK8s,
   727  				Name:    "kubelet",
   728  				Version: "1.21.1",
   729  			},
   730  			want: ftypes.K8sUpstream,
   731  		},
   732  		{
   733  			name: "eks",
   734  			purl: packageurl.PackageURL{
   735  				Type:      purl.TypeK8s,
   736  				Namespace: purl.NamespaceEKS,
   737  				Name:      "kubelet",
   738  				Version:   "1.21.1",
   739  			},
   740  			want: ftypes.EKS,
   741  		},
   742  	}
   743  	for _, tt := range tests {
   744  		t.Run(tt.name, func(t *testing.T) {
   745  			p := &purl.PackageURL{PackageURL: tt.purl}
   746  			assert.Equalf(t, tt.want, p.LangType(), "LangType()")
   747  		})
   748  	}
   749  }