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 }