github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/javascript/parse_package_lock_test.go (about)

     1  package javascript
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/anchore/syft/syft/artifact"
     8  	"github.com/anchore/syft/syft/file"
     9  	"github.com/anchore/syft/syft/pkg"
    10  	"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
    11  )
    12  
    13  func TestParsePackageLock(t *testing.T) {
    14  	var expectedRelationships []artifact.Relationship
    15  	expectedPkgs := []pkg.Package{
    16  		{
    17  			Name:     "@actions/core",
    18  			Version:  "1.6.0",
    19  			PURL:     "pkg:npm/%40actions/core@1.6.0",
    20  			Language: pkg.JavaScript,
    21  			Type:     pkg.NpmPkg,
    22  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz", Integrity: "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw=="},
    23  		},
    24  		{
    25  			Name:     "ansi-regex",
    26  			Version:  "3.0.0",
    27  			PURL:     "pkg:npm/ansi-regex@3.0.0",
    28  			Language: pkg.JavaScript,
    29  			Type:     pkg.NpmPkg,
    30  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", Integrity: "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},
    31  		},
    32  		{
    33  			Name:     "cowsay",
    34  			Version:  "1.4.0",
    35  			PURL:     "pkg:npm/cowsay@1.4.0",
    36  			Language: pkg.JavaScript,
    37  			Type:     pkg.NpmPkg,
    38  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/cowsay/-/cowsay-1.4.0.tgz", Integrity: "sha512-rdg5k5PsHFVJheO/pmE3aDg2rUDDTfPJau6yYkZYlHFktUz+UxbE+IgnUAEyyCyv4noL5ltxXD0gZzmHPCy/9g=="},
    39  		},
    40  		{
    41  			Name:     "get-stdin",
    42  			Version:  "5.0.1",
    43  			PURL:     "pkg:npm/get-stdin@5.0.1",
    44  			Language: pkg.JavaScript,
    45  			Type:     pkg.NpmPkg,
    46  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", Integrity: "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g="},
    47  		},
    48  		{
    49  			Name:     "is-fullwidth-code-point",
    50  			Version:  "2.0.0",
    51  			PURL:     "pkg:npm/is-fullwidth-code-point@2.0.0",
    52  			Language: pkg.JavaScript,
    53  			Type:     pkg.NpmPkg,
    54  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", Integrity: "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},
    55  		},
    56  		{
    57  			Name:     "minimist",
    58  			Version:  "0.0.10",
    59  			PURL:     "pkg:npm/minimist@0.0.10",
    60  			Language: pkg.JavaScript,
    61  			Type:     pkg.NpmPkg,
    62  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", Integrity: "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="},
    63  		},
    64  		{
    65  			Name:     "optimist",
    66  			Version:  "0.6.1",
    67  			PURL:     "pkg:npm/optimist@0.6.1",
    68  			Language: pkg.JavaScript,
    69  			Type:     pkg.NpmPkg,
    70  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", Integrity: "sha1-2j6nRob6IaGaERwybpDrFaAZZoY="},
    71  		},
    72  		{
    73  			Name:     "string-width",
    74  			Version:  "2.1.1",
    75  			PURL:     "pkg:npm/string-width@2.1.1",
    76  			Language: pkg.JavaScript,
    77  			Type:     pkg.NpmPkg,
    78  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", Integrity: "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw=="},
    79  		},
    80  		{
    81  			Name:     "strip-ansi",
    82  			Version:  "4.0.0",
    83  			PURL:     "pkg:npm/strip-ansi@4.0.0",
    84  			Language: pkg.JavaScript,
    85  			Type:     pkg.NpmPkg,
    86  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", Integrity: "sha1-qEeQIusaw2iocTibY1JixQXuNo8="},
    87  		},
    88  		{
    89  			Name:     "strip-eof",
    90  			Version:  "1.0.0",
    91  			PURL:     "pkg:npm/strip-eof@1.0.0",
    92  			Language: pkg.JavaScript,
    93  			Type:     pkg.NpmPkg,
    94  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", Integrity: "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="},
    95  		},
    96  		{
    97  			Name:     "wordwrap",
    98  			Version:  "0.0.3",
    99  			PURL:     "pkg:npm/wordwrap@0.0.3",
   100  			Language: pkg.JavaScript,
   101  			Type:     pkg.NpmPkg,
   102  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", Integrity: "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="},
   103  		},
   104  	}
   105  	fixture := "test-fixtures/pkg-lock/package-lock.json"
   106  	for i := range expectedPkgs {
   107  		expectedPkgs[i].Locations.Add(file.NewLocation(fixture))
   108  	}
   109  
   110  	adapter := newGenericPackageLockAdapter(CatalogerConfig{})
   111  	pkgtest.TestFileParser(t, fixture, adapter.parsePackageLock, expectedPkgs, expectedRelationships)
   112  }
   113  
   114  func TestParsePackageLockV2(t *testing.T) {
   115  	ctx := context.TODO()
   116  	fixture := "test-fixtures/pkg-lock/package-lock-2.json"
   117  	var expectedRelationships []artifact.Relationship
   118  	expectedPkgs := []pkg.Package{
   119  		{
   120  			Name:     "npm",
   121  			Version:  "6.14.6",
   122  			Language: pkg.JavaScript,
   123  			Type:     pkg.NpmPkg,
   124  			PURL:     "pkg:npm/npm@6.14.6",
   125  			Metadata: pkg.NpmPackageLockEntry{Dependencies: map[string]string{"@types/react": "^18.0.9"}},
   126  		},
   127  		{
   128  			Name:     "@types/prop-types",
   129  			Version:  "15.7.5",
   130  			PURL:     "pkg:npm/%40types/prop-types@15.7.5",
   131  			Language: pkg.JavaScript,
   132  			Type:     pkg.NpmPkg,
   133  			Licenses: pkg.NewLicenseSet(
   134  				pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
   135  			),
   136  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha1-XxnSuFqY6VWANvajysyIGUIPBc8="},
   137  		},
   138  		{
   139  			Name:     "@types/react",
   140  			Version:  "18.0.17",
   141  			PURL:     "pkg:npm/%40types/react@18.0.17",
   142  			Language: pkg.JavaScript,
   143  			Type:     pkg.NpmPkg,
   144  			Licenses: pkg.NewLicenseSet(
   145  				pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
   146  			),
   147  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.17.tgz", Integrity: "sha1-RYPZwyLWfv5LOak10iPtzHBQzPQ=", Dependencies: map[string]string{"@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2"}},
   148  		},
   149  		{
   150  			Name:     "@types/scheduler",
   151  			Version:  "0.16.2",
   152  			PURL:     "pkg:npm/%40types/scheduler@0.16.2",
   153  			Language: pkg.JavaScript,
   154  			Type:     pkg.NpmPkg,
   155  			Licenses: pkg.NewLicenseSet(
   156  				pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
   157  			),
   158  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha1-GmL4lSVyPd4kuhsBsJK/XfitTTk="},
   159  		},
   160  		{
   161  			Name:     "csstype",
   162  			Version:  "3.1.0",
   163  			PURL:     "pkg:npm/csstype@3.1.0",
   164  			Language: pkg.JavaScript,
   165  			Type:     pkg.NpmPkg,
   166  			Licenses: pkg.NewLicenseSet(
   167  				pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
   168  			),
   169  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", Integrity: "sha1-TdysNxjXh8+d8NG30VAzklyPKfI="},
   170  		},
   171  	}
   172  	for i := range expectedPkgs {
   173  		expectedPkgs[i].Locations.Add(file.NewLocation(fixture))
   174  	}
   175  	expectedRelationships = []artifact.Relationship{
   176  		{
   177  			From: expectedPkgs[1],
   178  			To:   expectedPkgs[2],
   179  			Type: artifact.DependencyOfRelationship,
   180  		},
   181  		{
   182  			From: expectedPkgs[3],
   183  			To:   expectedPkgs[2],
   184  			Type: artifact.DependencyOfRelationship,
   185  		},
   186  		{
   187  			From: expectedPkgs[4],
   188  			To:   expectedPkgs[2],
   189  			Type: artifact.DependencyOfRelationship,
   190  		},
   191  		{
   192  			From: expectedPkgs[2],
   193  			To:   expectedPkgs[0],
   194  			Type: artifact.DependencyOfRelationship,
   195  		},
   196  	}
   197  	adapter := newGenericPackageLockAdapter(CatalogerConfig{})
   198  	pkgtest.TestFileParser(t, fixture, adapter.parsePackageLock, expectedPkgs, expectedRelationships)
   199  }
   200  
   201  func TestParsePackageLockV3(t *testing.T) {
   202  	fixture := "test-fixtures/pkg-lock/package-lock-3.json"
   203  	var expectedRelationships []artifact.Relationship
   204  	expectedPkgs := []pkg.Package{
   205  		{
   206  			Name:     "lock-v3-fixture",
   207  			Version:  "1.0.0",
   208  			Language: pkg.JavaScript,
   209  			Type:     pkg.NpmPkg,
   210  			PURL:     "pkg:npm/lock-v3-fixture@1.0.0",
   211  			Metadata: pkg.NpmPackageLockEntry{Dependencies: map[string]string{"@types/react": "^18.0.9"}},
   212  		},
   213  		{
   214  			Name:     "@types/prop-types",
   215  			Version:  "15.7.5",
   216  			Language: pkg.JavaScript,
   217  			Type:     pkg.NpmPkg,
   218  			PURL:     "pkg:npm/%40types/prop-types@15.7.5",
   219  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", Integrity: "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="},
   220  		},
   221  		{
   222  			Name:     "@types/react",
   223  			Version:  "18.0.20",
   224  			Language: pkg.JavaScript,
   225  			Type:     pkg.NpmPkg,
   226  			PURL:     "pkg:npm/%40types/react@18.0.20",
   227  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/react/-/react-18.0.20.tgz", Integrity: "sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==", Dependencies: map[string]string{"@types/prop-types": "*", "@types/scheduler": "*", "csstype": "^3.0.2"}},
   228  		},
   229  		{
   230  			Name:     "@types/scheduler",
   231  			Version:  "0.16.2",
   232  			Language: pkg.JavaScript,
   233  			Type:     pkg.NpmPkg,
   234  			PURL:     "pkg:npm/%40types/scheduler@0.16.2",
   235  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", Integrity: "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew=="},
   236  		},
   237  		{
   238  			Name:     "csstype",
   239  			Version:  "3.1.1",
   240  			Language: pkg.JavaScript,
   241  			Type:     pkg.NpmPkg,
   242  			PURL:     "pkg:npm/csstype@3.1.1",
   243  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", Integrity: "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="},
   244  		},
   245  	}
   246  	for i := range expectedPkgs {
   247  		expectedPkgs[i].Locations.Add(file.NewLocation(fixture))
   248  	}
   249  	expectedRelationships = []artifact.Relationship{
   250  		{
   251  			From: expectedPkgs[1],
   252  			To:   expectedPkgs[2],
   253  			Type: artifact.DependencyOfRelationship,
   254  		},
   255  		{
   256  			From: expectedPkgs[3],
   257  			To:   expectedPkgs[2],
   258  			Type: artifact.DependencyOfRelationship,
   259  		},
   260  		{
   261  			From: expectedPkgs[4],
   262  			To:   expectedPkgs[2],
   263  			Type: artifact.DependencyOfRelationship,
   264  		},
   265  		{
   266  			From: expectedPkgs[2],
   267  			To:   expectedPkgs[0],
   268  			Type: artifact.DependencyOfRelationship,
   269  		},
   270  	}
   271  	adapter := newGenericPackageLockAdapter(CatalogerConfig{})
   272  	pkgtest.TestFileParser(t, fixture, adapter.parsePackageLock, expectedPkgs, expectedRelationships)
   273  }
   274  
   275  func TestParsePackageLockAlias(t *testing.T) {
   276  	ctx := context.TODO()
   277  	var expectedRelationships []artifact.Relationship
   278  	commonPkgs := []pkg.Package{
   279  		{
   280  			Name:     "case",
   281  			Version:  "1.6.2",
   282  			PURL:     "pkg:npm/case@1.6.2",
   283  			Language: pkg.JavaScript,
   284  			Type:     pkg.NpmPkg,
   285  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/case/-/case-1.6.2.tgz", Integrity: "sha512-ll380ZRoraT7mUK2G92UbH+FJVD5AwdVIAYk9xhV1tauh0carDgYByUD1HhjCWsWgxrfQvCeHvtfj7IYR6TKeg=="},
   286  		},
   287  		{
   288  			Name:     "case",
   289  			Version:  "1.6.3",
   290  			PURL:     "pkg:npm/case@1.6.3",
   291  			Language: pkg.JavaScript,
   292  			Type:     pkg.NpmPkg,
   293  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/case/-/case-1.6.3.tgz", Integrity: "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ=="},
   294  		},
   295  		{
   296  			Name:     "@bundled-es-modules/chai",
   297  			Version:  "4.2.2",
   298  			PURL:     "pkg:npm/%40bundled-es-modules/chai@4.2.2",
   299  			Language: pkg.JavaScript,
   300  			Type:     pkg.NpmPkg,
   301  			Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/@bundled-es-modules/chai/-/chai-4.2.2.tgz", Integrity: "sha512-iGmVYw2/zJCoqyKTtWEYCtFmMyi8WmACQKtky0lpNyEKWX0YIOpKWGD7saMXL+tPpllss0otilxV0SLwyi3Ytg=="},
   302  		},
   303  	}
   304  
   305  	packageLockV1 := "test-fixtures/pkg-lock/alias-package-lock-1.json"
   306  	packageLockV2 := "test-fixtures/pkg-lock/alias-package-lock-2.json"
   307  	packageLocks := []string{packageLockV1, packageLockV2}
   308  
   309  	v2Pkg := pkg.Package{
   310  		Name:     "alias-check",
   311  		Version:  "1.0.0",
   312  		PURL:     "pkg:npm/alias-check@1.0.0",
   313  		Language: pkg.JavaScript,
   314  		Type:     pkg.NpmPkg,
   315  		Licenses: pkg.NewLicenseSet(
   316  			pkg.NewLicenseFromLocationsWithContext(ctx, "ISC", file.NewLocation(packageLockV2)),
   317  		),
   318  		Metadata: pkg.NpmPackageLockEntry{Dependencies: map[string]string{"case": "1.6.2", "case-alias": "npm:case@^1.6.3", "chai": "npm:@bundled-es-modules/chai@^4.2.2"}},
   319  	}
   320  
   321  	for _, pl := range packageLocks {
   322  		expected := make([]pkg.Package, len(commonPkgs))
   323  		copy(expected, commonPkgs)
   324  
   325  		if pl == packageLockV2 {
   326  			expected = append(expected, v2Pkg)
   327  		}
   328  
   329  		for i := range expected {
   330  			expected[i].Locations.Add(file.NewLocation(pl))
   331  		}
   332  
   333  		if pl == packageLockV2 {
   334  			expectedRelationships = []artifact.Relationship{
   335  				{
   336  					From: expected[0],
   337  					To:   expected[3],
   338  					Type: artifact.DependencyOfRelationship,
   339  				},
   340  				{
   341  					From: expected[1],
   342  					To:   expected[3],
   343  					Type: artifact.DependencyOfRelationship,
   344  				},
   345  				{
   346  					From: expected[2],
   347  					To:   expected[3],
   348  					Type: artifact.DependencyOfRelationship,
   349  				},
   350  			}
   351  		}
   352  		adapter := newGenericPackageLockAdapter(CatalogerConfig{})
   353  		pkgtest.TestFileParser(t, pl, adapter.parsePackageLock, expected, expectedRelationships)
   354  	}
   355  }
   356  
   357  func TestParsePackageLockLicenseWithArray(t *testing.T) {
   358  	ctx := context.TODO()
   359  	fixture := "test-fixtures/pkg-lock/array-license-package-lock.json"
   360  	var expectedRelationships []artifact.Relationship
   361  	expectedPkgs := []pkg.Package{
   362  		{
   363  			Name:     "tmp",
   364  			Version:  "1.0.0",
   365  			Language: pkg.JavaScript,
   366  			Type:     pkg.NpmPkg,
   367  			Licenses: pkg.NewLicenseSet(
   368  				pkg.NewLicenseFromLocationsWithContext(ctx, "ISC", file.NewLocation(fixture)),
   369  			),
   370  			PURL:     "pkg:npm/tmp@1.0.0",
   371  			Metadata: pkg.NpmPackageLockEntry{Dependencies: map[string]string{"pause-stream": "0.0.11"}},
   372  		},
   373  		{
   374  			Name:     "pause-stream",
   375  			Version:  "0.0.11",
   376  			Language: pkg.JavaScript,
   377  			Type:     pkg.NpmPkg,
   378  
   379  			Licenses: pkg.NewLicenseSet(
   380  				pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
   381  				pkg.NewLicenseFromLocationsWithContext(ctx, "Apache2", file.NewLocation(fixture)),
   382  			),
   383  			PURL:     "pkg:npm/pause-stream@0.0.11",
   384  			Metadata: pkg.NpmPackageLockEntry{Dependencies: map[string]string{"through": "~2.3"}},
   385  		},
   386  		{
   387  			Name:     "through",
   388  			Version:  "2.3.8",
   389  			Language: pkg.JavaScript,
   390  			Type:     pkg.NpmPkg,
   391  			Licenses: pkg.NewLicenseSet(
   392  				pkg.NewLicenseFromLocationsWithContext(ctx, "MIT", file.NewLocation(fixture)),
   393  			),
   394  			PURL:     "pkg:npm/through@2.3.8",
   395  			Metadata: pkg.NpmPackageLockEntry{},
   396  		},
   397  	}
   398  	for i := range expectedPkgs {
   399  		expectedPkgs[i].Locations.Add(file.NewLocation(fixture))
   400  	}
   401  
   402  	expectedRelationships = []artifact.Relationship{
   403  		{
   404  			From: expectedPkgs[2],
   405  			To:   expectedPkgs[1],
   406  			Type: artifact.DependencyOfRelationship,
   407  		},
   408  		{
   409  			From: expectedPkgs[1],
   410  			To:   expectedPkgs[0],
   411  			Type: artifact.DependencyOfRelationship,
   412  		},
   413  	}
   414  	adapter := newGenericPackageLockAdapter(CatalogerConfig{})
   415  	pkgtest.TestFileParser(t, fixture, adapter.parsePackageLock, expectedPkgs, expectedRelationships)
   416  }
   417  
   418  func Test_corruptPackageLock(t *testing.T) {
   419  	gap := newGenericPackageLockAdapter(DefaultCatalogerConfig())
   420  	pkgtest.NewCatalogTester().
   421  		FromFile(t, "test-fixtures/corrupt/package-lock.json").
   422  		WithError().
   423  		TestParser(t, gap.parsePackageLock)
   424  }