diff --git a/cmd/syft/internal/options/catalog.go b/cmd/syft/internal/options/catalog.go index 787859af0cc..22cb286a445 100644 --- a/cmd/syft/internal/options/catalog.go +++ b/cmd/syft/internal/options/catalog.go @@ -166,6 +166,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config { WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags), ), JavaScript: javascript.DefaultCatalogerConfig(). + WithIncludeDevDependencies(*multiLevelOption(false, cfg.JavaScript.IncludeDevDependencies)). WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.JavaScript, task.Node, task.NPM), cfg.JavaScript.SearchRemoteLicenses)). WithNpmBaseURL(cfg.JavaScript.NpmBaseURL), LinuxKernel: kernel.LinuxKernelCatalogerConfig{ diff --git a/cmd/syft/internal/options/javascript.go b/cmd/syft/internal/options/javascript.go index e618037ccaa..982bffa29a3 100644 --- a/cmd/syft/internal/options/javascript.go +++ b/cmd/syft/internal/options/javascript.go @@ -3,8 +3,9 @@ package options import "github.com/anchore/clio" type javaScriptConfig struct { - SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` - NpmBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"` + SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` + NpmBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"` + IncludeDevDependencies *bool `json:"include-dev-dependencies" yaml:"include-dev-dependencies" mapstructure:"include-dev-dependencies"` } var _ interface { @@ -14,4 +15,5 @@ var _ interface { func (o *javaScriptConfig) DescribeFields(descriptions clio.FieldDescriptionSet) { descriptions.Add(&o.SearchRemoteLicenses, `enables Syft to use the network to fill in more detailed license information`) descriptions.Add(&o.NpmBaseURL, `base NPM url to use`) + descriptions.Add(&o.IncludeDevDependencies, `include development-scoped dependencies`) } diff --git a/cmd/syft/internal/test/integration/node_packages_test.go b/cmd/syft/internal/test/integration/node_packages_test.go index dcf1c4a7381..8995a913a2d 100644 --- a/cmd/syft/internal/test/integration/node_packages_test.go +++ b/cmd/syft/internal/test/integration/node_packages_test.go @@ -25,7 +25,7 @@ func TestNpmPackageLockDirectory(t *testing.T) { } // ensure that integration test commonTestCases stay in sync with the available catalogers - const expectedPackageCount = 6 + const expectedPackageCount = 2 if foundPackages.Size() != expectedPackageCount { t.Errorf("found the wrong set of npm package-lock.json packages (expected: %d, actual: %d)", expectedPackageCount, foundPackages.Size()) } diff --git a/syft/pkg/cataloger/javascript/config.go b/syft/pkg/cataloger/javascript/config.go index f73e1899b53..e3b837e2637 100644 --- a/syft/pkg/cataloger/javascript/config.go +++ b/syft/pkg/cataloger/javascript/config.go @@ -3,8 +3,9 @@ package javascript const npmBaseURL = "https://registry.npmjs.org" type CatalogerConfig struct { - SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` - NPMBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"` + SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"` + NPMBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"` + IncludeDevDependencies bool `json:"include-dev-dependencies" yaml:"include-dev-dependencies" mapstructure:"include-dev-dependencies"` } func DefaultCatalogerConfig() CatalogerConfig { @@ -25,3 +26,8 @@ func (j CatalogerConfig) WithNpmBaseURL(input string) CatalogerConfig { } return j } + +func (j CatalogerConfig) WithIncludeDevDependencies(input bool) CatalogerConfig { + j.IncludeDevDependencies = input + return j +} diff --git a/syft/pkg/cataloger/javascript/parse_package_lock.go b/syft/pkg/cataloger/javascript/parse_package_lock.go index ec8a2b60029..003683b76ce 100644 --- a/syft/pkg/cataloger/javascript/parse_package_lock.go +++ b/syft/pkg/cataloger/javascript/parse_package_lock.go @@ -29,6 +29,7 @@ type lockDependency struct { Version string `json:"version"` Resolved string `json:"resolved"` Integrity string `json:"integrity"` + Dev bool `json:"dev"` } type lockPackage struct { @@ -37,6 +38,7 @@ type lockPackage struct { Resolved string `json:"resolved"` Integrity string `json:"integrity"` License packageLockLicense `json:"license"` + Dev bool `json:"dev"` } // packageLockLicense @@ -74,6 +76,11 @@ func (a genericPackageLockAdapter) parsePackageLock(_ context.Context, resolver 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(a.cfg, resolver, reader.Location, name, pkgMeta)) } } @@ -87,6 +94,11 @@ func (a genericPackageLockAdapter) parsePackageLock(_ context.Context, resolver name = pkgMeta.Name } + // skip packages that are only present as a dev dependency + if !a.cfg.IncludeDevDependencies && pkgMeta.Dev { + continue + } + // handles alias names if pkgMeta.Name != "" { name = pkgMeta.Name diff --git a/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-2.json b/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-2.json index 2373f27ca0a..211f8dcc8f7 100644 --- a/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-2.json +++ b/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-2.json @@ -9,6 +9,9 @@ "version": "6.14.6", "dependencies": { "@types/react": "^18.0.9" + }, + "devDependencies": { + "async": "^3.2.4" } }, "node_modules/@types/prop-types": { @@ -39,6 +42,12 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha1-TdysNxjXh8+d8NG30VAzklyPKfI=", "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true } }, "dependencies": { @@ -66,6 +75,12 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz", "integrity": "sha1-TdysNxjXh8+d8NG30VAzklyPKfI=" + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true } } } diff --git a/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-3.json b/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-3.json index 68008c089a5..dce055481bf 100644 --- a/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-3.json +++ b/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock-3.json @@ -9,6 +9,9 @@ "version": "1.0.0", "dependencies": { "@types/react": "^18.0.9" + }, + "devDependencies": { + "async": "^3.2.4" } }, "node_modules/@types/prop-types": { @@ -35,6 +38,12 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true } } } diff --git a/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock.json b/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock.json index 7a14a9e2120..fcaf8c0e3af 100644 --- a/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock.json +++ b/syft/pkg/cataloger/javascript/test-fixtures/pkg-lock/package-lock.json @@ -76,6 +76,12 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true } } }