Skip to content

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

@hakandilek

Description

@hakandilek

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:

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 devOptionals but it does not.

To Reproduce

Scanning cyclonedx-node-npm includes the devOptional dependency @isaacs/[email protected]:

  • git clone https://github.com/CycloneDX/cyclonedx-node-npm
  • pushd cyclonedx-node-npm
  • npm install
  • popd
  • npx @cyclonedx/[email protected] --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 file with the following [email protected] entry:

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

Expected behavior

Analyzing the whole dependency path for @isaacs/cliui,

@cyclonedx/cyclonedx-npm (root package)
└── @cyclonedx/[email protected]
    └── [email protected] (optional dependency)
        └── [email protected]
            └── [email protected]
                └── [email protected]
                    └── [email protected]
                        └── [email protected]
                            └── @isaacs/[email protected]

shows that, @isaacs/cliui has been pulled through a long tail of dependencies, but mainly by [email protected] optional dependency of the @cyclonedx/[email protected]. I'd expect that it would not show up after all.

Screenshots or output-paste

CLI output of the npx @cyclonedx/[email protected] --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:

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.

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

  • 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions