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/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 file with the following cliui@8.0.2 entry:

{
// ...
  "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,

@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:

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