mirror of
https://github.com/anchore/syft.git
synced 2026-08-19 08:38:25 +02:00
Fix missing nested packages in package-lock.json v1 (#5108)
* fix(javascript): catalog nested package-lock v1 dependencies Signed-off-by: Eljees <57435526+Eljees@users.noreply.github.com> * test(javascript): cover nested package-lock v1 dependencies Signed-off-by: Eljees <57435526+Eljees@users.noreply.github.com> --------- Signed-off-by: Eljees <57435526+Eljees@users.noreply.github.com>
This commit is contained in:
parent
295454945c
commit
1286689419
@ -27,10 +27,11 @@ type packageLock struct {
|
||||
|
||||
// lockDependency represents a single package dependency listed in the package.lock json file
|
||||
type lockDependency struct {
|
||||
Version string `json:"version"`
|
||||
Resolved string `json:"resolved"`
|
||||
Integrity string `json:"integrity"`
|
||||
Dev bool `json:"dev"`
|
||||
Version string `json:"version"`
|
||||
Resolved string `json:"resolved"`
|
||||
Integrity string `json:"integrity"`
|
||||
Dev bool `json:"dev"`
|
||||
Dependencies map[string]lockDependency `json:"dependencies"`
|
||||
}
|
||||
|
||||
type lockPackage struct {
|
||||
@ -77,14 +78,7 @@ func (a genericPackageLockAdapter) parsePackageLock(ctx context.Context, resolve
|
||||
}
|
||||
|
||||
if lock.LockfileVersion == 1 {
|
||||
for name, pkgMeta := range lock.Dependencies {
|
||||
// skip packages that are only present as a dev dependency
|
||||
if !a.cfg.IncludeDevDependencies && pkgMeta.Dev {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPackageLockV1Package(ctx, a.cfg, resolver, reader.Location, name, pkgMeta))
|
||||
}
|
||||
pkgs = append(pkgs, a.packageLockV1Packages(ctx, resolver, reader.Location, lock.Dependencies)...)
|
||||
}
|
||||
|
||||
if lock.LockfileVersion == 2 || lock.LockfileVersion == 3 {
|
||||
@ -116,6 +110,32 @@ func (a genericPackageLockAdapter) parsePackageLock(ctx context.Context, resolve
|
||||
return pkgs, dependency.Resolve(packageLockDependencySpecifier, pkgs), unknown.IfEmptyf(pkgs, "unable to determine packages")
|
||||
}
|
||||
|
||||
func (a genericPackageLockAdapter) packageLockV1Packages(ctx context.Context, resolver file.Resolver, location file.Location, dependencies map[string]lockDependency) []pkg.Package {
|
||||
var pkgs []pkg.Package
|
||||
seen := make(map[string]struct{})
|
||||
|
||||
var walk func(map[string]lockDependency)
|
||||
walk = func(dependencies map[string]lockDependency) {
|
||||
for name, pkgMeta := range dependencies {
|
||||
// Skipping a dev-only dependency also skips its dev-only subtree.
|
||||
if !a.cfg.IncludeDevDependencies && pkgMeta.Dev {
|
||||
continue
|
||||
}
|
||||
|
||||
p := newPackageLockV1Package(ctx, a.cfg, resolver, location, name, pkgMeta)
|
||||
if _, exists := seen[p.PURL]; !exists {
|
||||
pkgs = append(pkgs, p)
|
||||
seen[p.PURL] = struct{}{}
|
||||
}
|
||||
|
||||
walk(pkgMeta.Dependencies)
|
||||
}
|
||||
}
|
||||
|
||||
walk(dependencies)
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func (licenses *packageLockLicense) UnmarshalJSON(data []byte) (err error) {
|
||||
// The license field could be either a string or an array.
|
||||
|
||||
|
||||
@ -111,6 +111,66 @@ func TestParsePackageLock(t *testing.T) {
|
||||
pkgtest.TestFileParser(t, fixture, adapter.parsePackageLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func TestParsePackageLockV1NestedDependencies(t *testing.T) {
|
||||
fixture := "testdata/pkg-lock/nested-package-lock-1.json"
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
Name: "duplicate",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:npm/duplicate@1.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/duplicate/-/duplicate-1.0.0.tgz", Integrity: "sha512-duplicate"},
|
||||
},
|
||||
{
|
||||
Name: "middle",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:npm/middle@1.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/middle/-/middle-1.0.0.tgz", Integrity: "sha512-middle"},
|
||||
},
|
||||
{
|
||||
Name: "parent",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:npm/parent@1.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/parent/-/parent-1.0.0.tgz", Integrity: "sha512-parent"},
|
||||
},
|
||||
{
|
||||
Name: "shared",
|
||||
Version: "0.5.0",
|
||||
PURL: "pkg:npm/shared@0.5.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/shared/-/shared-0.5.0.tgz", Integrity: "sha512-shared-0.5"},
|
||||
},
|
||||
{
|
||||
Name: "shared",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:npm/shared@1.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/shared/-/shared-1.0.0.tgz", Integrity: "sha512-shared-1"},
|
||||
},
|
||||
{
|
||||
Name: "shared",
|
||||
Version: "2.0.0",
|
||||
PURL: "pkg:npm/shared@2.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Metadata: pkg.NpmPackageLockEntry{Resolved: "https://registry.npmjs.org/shared/-/shared-2.0.0.tgz", Integrity: "sha512-shared-2"},
|
||||
},
|
||||
}
|
||||
for i := range expectedPkgs {
|
||||
expectedPkgs[i].Locations.Add(file.NewLocation(fixture))
|
||||
}
|
||||
|
||||
adapter := newGenericPackageLockAdapter(CatalogerConfig{})
|
||||
pkgtest.TestFileParser(t, fixture, adapter.parsePackageLock, expectedPkgs, nil)
|
||||
}
|
||||
|
||||
func TestParsePackageLockV2(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
fixture := "testdata/pkg-lock/package-lock-2.json"
|
||||
|
||||
54
syft/pkg/cataloger/javascript/testdata/pkg-lock/nested-package-lock-1.json
vendored
Normal file
54
syft/pkg/cataloger/javascript/testdata/pkg-lock/nested-package-lock-1.json
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"parent": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parent/-/parent-1.0.0.tgz",
|
||||
"integrity": "sha512-parent",
|
||||
"dependencies": {
|
||||
"shared": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shared/-/shared-1.0.0.tgz",
|
||||
"integrity": "sha512-shared-1"
|
||||
},
|
||||
"middle": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/middle/-/middle-1.0.0.tgz",
|
||||
"integrity": "sha512-middle",
|
||||
"dependencies": {
|
||||
"shared": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/shared/-/shared-0.5.0.tgz",
|
||||
"integrity": "sha512-shared-0.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"duplicate": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/duplicate/-/duplicate-1.0.0.tgz",
|
||||
"integrity": "sha512-duplicate"
|
||||
}
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shared/-/shared-2.0.0.tgz",
|
||||
"integrity": "sha512-shared-2"
|
||||
},
|
||||
"duplicate": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/duplicate/-/duplicate-1.0.0.tgz",
|
||||
"integrity": "sha512-duplicate"
|
||||
},
|
||||
"dev-parent": {
|
||||
"version": "1.0.0",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dev-nested": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user