CycloneDX / CycloneDX/cyclonedx-node-npm

[BUG] `--omit=dev` and `--omit=optional` flags don't exclude `devOptional` dependencies, causing misleading output

Open
#1,325 13 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
JavaScript
Stars
150
Forks
29
Avg merge
1h 3m
Merged PRs (30d)
1

Description

## Describe the bug

With #1307, dependency tree resolution is mainly based on the `npm ls` output. The `npm ls` command's `--omit=dev` and `--omit=optional` flags are misleading when dealing with `devOptional` dependencies. These dependencies are not excluded by either flag individually or in combination, despite their development-oriented nature.

[npm documentation states](https://docs.npmjs.com/cli/v11/configuring-npm/package-lock-json):

> dev, optional, devOptional: If the package is strictly part of the devDependencies tree, then dev will be true. If it is strictly part of the optionalDependencies tree, then optional will be set. If it is both a dev dependency and an optional dependency of a non-dev dependency, then devOptional will be set. (An optional dependency of a dev dependency will have both dev and optional set.)

Following this logic, I'd expect, using `--omit=dev` and `--omit=optional` at the same time, would also omit `devOptional`s but it does not.

## To Reproduce

Scanning [cyclonedx-node-npm](https://github.com/CycloneDX/cyclonedx-node-npm) includes the `devOptional` dependency `@isaacs/cliui@8.0.2`:

- `git clone https://github.com/CycloneDX/cyclonedx-node-npm`
- `pushd cyclonedx-node-npm`
- `npm install`
- `popd`
- `npx @cyclonedx/cyclonedx-npm@4.0.0 --verbose --verbose --verbose --flatten-components --short-PURLs --spec-version 1.6 --output-format JSON --output-file - --omit dev --omit optional -- cyclonedx-node-npm/package.json > cyclonedx-node-npm/cyclonedx-node-npm-4.cdx.json`

Produces the [cyclonedx-node-npm-4.cdx.json](https://github.com/user-attachments/files/21403017/cyclonedx-node-npm-4.cdx.json) file with the following `cliui@8.0.2` entry:

```json5
{
// ...
"components": [
// ...
{
"type": "library",
"name": "cliui",
"group": "@isaacs",
"version": "8.0.2",
"bom-ref": "@cyclonedx/cyclonedx-npm@4.0.0|@isaacs/cliui@8.0.2",
"author": "Ben Coe",
"description": "easily create complex multi-column command-line-interfaces",
"licenses": ...,
"purl": "pkg:npm/%40isaacs/cliui@8.0.2",
"externalReferences": ...
"properties": [
{
"name": "cdx:npm:package:development",
"value": "true"
},
{
"name": "cdx:npm:package:path",
"value": "node_modules/@isaacs/cliui"
}
]
},
// ...
"dependencies": [
// ...
{
"ref": "@cyclonedx/cyclonedx-npm@4.0.0|jackspeak@3.4.3",
"dependsOn": [
"@cyclonedx/cyclonedx-npm@4.0.0|@isaacs/cliui@8.0.2",
"@cyclonedx/cyclonedx-npm@4.0.0|@pkgjs/parseargs@0.11.0"
]
},
// ...
}
```

## Expected behavior

Analyzing the whole dependency path for `@isaacs/cliui`,

```text
@cyclonedx/cyclonedx-npm (root package)
└── @cyclonedx/cyclonedx-library@8.5.0
└── libxmljs2@0.37.0 (optional dependency)
└── node-gyp@11.2.0
└── make-fetch-happen@14.0.3
└── cacache@19.0.1
└── glob@10.4.5
└── jackspeak@3.4.3
└── @isaacs/cliui@8.0.2
```

shows that, `@isaacs/cliui` has been pulled through a long tail of dependencies, but mainly by `libxmljs2@0.37.0` optional dependency of the `@cyclonedx/cyclonedx-library@8.5.0`. I'd expect that it would not show up after all.

## Screenshots or output-paste

CLI output of the `npx @cyclonedx/cyclonedx-npm@4.0.0 --verbose --verbose --verbose --flatten-components --short-PURLs --spec-version 1.6 --output-format JSON --output-file - --omit dev --omit optional -- cyclonedx-node-npm/package.json > cyclonedx-node-npm/cyclonedx-node-npm-4.cdx.json` given above is:

```text
DEBUG | options: {"ignoreNpmErrors":false,"packageLockOnly":false,"omit":["dev","optional"],"workspace":[],"workspaces":true,"gatherLicenseTexts":false,"flattenComponents":true,"shortPURLs":true,"specVersion":"1.6","outputFormat":"JSON","outputFile":"-","mcType":"application","verbose":3}
DEBUG | makeNpmRunner caused execFileSync "/opt/homebrew/Cellar/node@22/22.17.1/bin/node" with "-- /opt/homebrew/Cellar/node@22/22.17.1/lib/node_modules/npm/bin/npm-cli.js"
DEBUG | found NPM version "10.9.2"
DEBUG | packageFile: /Users/addiha5/work/cyclonedx-node-npm/package.json
INFO | projectDir: /Users/addiha5/work/cyclonedx-node-npm
INFO | detected a `node_modules` dir
LOG | gathering BOM data ...
INFO | gathering dependency tree ...
DEBUG | npm-ls: run npm with ["ls","--json","--long","--all","--omit=dev","--omit=optional"] in "/Users/addiha5/work/cyclonedx-node-npm"
INFO | building BOM ...
LOG | serializing BOM ...
LOG | try validating BOM result ...
INFO | BOM result appears valid
LOG | writing BOM to -
INFO | wrote 366448 bytes to -
```

Indicating that `npm ls --json --long --all --omit=dev --omit=optional` has been called under the hood.
Calling `npm ls --json --long --all --omit=dev --omit=optional > npm-ls-no-dev-op.json` would give me [npm-ls-no-dev-op.json](https://github.com/user-attachments/files/21402999/npm-ls-no-dev-op.json).

## Environment

- _@cyclonedx/cyclonedx-npm_ version: v4.0.0
- NPM version: 10.9.2
- Node version: v22.17.1
- OS: macOS 15.5

## Additional context

Add any other context about the problem here.

## Contribution

- [x] I am willing to provide a fix (given that someone give some pointers to the right direction)
- [ ] I will wait until somebody else fixes it

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.