Skip to content

Add ability to optionally ignore dev dependencies in Yarn projects

Release Notes

Problem to solve

When running Dependency Scanning on Yarn projects, Gemnasium scans all dependencies, including devDependencies. Gemnasium should utilize the DS_INCLUDE_DEV_DEPENDENCIES variable when determining if devDependencies should be included during the scan.

For instance, in a project having the following package.json dependency file, my_test_framework and another_dev_dep and their dependencies should not be scanned.

{
  "dependencies": {
    "my_dep": "^1.0.0",
    "another_dep": "~2.2.0"
  },
  "devDependencies" : {
    "my_test_framework": "^3.1.0".
    "another_dev_dep": "1.0.0 - 1.2.0"
  }
}

Intended users

Proposal

When DS_INCLUDE_DEV_DEPENDENCIES is "false", the Dependency Scanning job should ignore development dependencies when scanning Yarn projects. To do so, the Yarn project will require a package.json since it is the only file that references devDependencies. The package.json file only lists direct dependencies, and as a result, graph traversal will be needed to see if a dependency is reachable from a package listed in the dependencies section of the package.json. The approach might look like the following pseudocode:

  1. Create an index that maps a package name to a list of its dependencies and another that identifies if it's been visited.
    dependencyIndex := make(map[string][]string)
    // Add yarn.lock packages to dependencyIndex 
    visited := make(map[string]bool)
  2. Parse the packages listed in the package.json dependencies section and add them to a queue.
  3. Pop a package name from the queue and iterate through all of its dependencies. a. If the package name has not been visited, add it to the queue. b. If a package has been visited, do not add it to the queue. i.e. skip to next package in list.
  4. After iterating through all dependencies, set the package as visited.
    visited["pkgName"] = true
  5. Repeat until queue is empty.

If the yarn project does not have a package.json file, then it should revert DS_INCLUDE_DEV_DEPENDENCIES to true and log a warning message of the reverted option.

Documentation

Document Yarn support for DS_INCLUDE_DEV_DEPENDENCIES in Configuring specific analyzers used by dependency scanning.

Testing

A new Yarn integration test is added to gemnasium, to check that devDependencies are ignored when DS_INCLUDE_DEV_DEPENDENCIES is "false".

Also, the unit tests of the Yarn parser are updated.

Workaround

You can run the following script to generate an SBOM that populates the dependency list only with the production dependencies.

#!/bin/sh

set -eu

yarn dlx -q @cyclonedx/yarn-plugin-cyclonedx --production --output-file gl-sbom-node-yarn.cdx.json

find . -path '*.cdx.json' -print0 >sboms.txt

while IFS= read -r -d '' FILE; do
  DIR="$(dirname "$FILE")"
  SCRIPT=$(
    cat <<EOF
.metadata.properties = [
  {
    "name": "gitlab:dependency_scanning:input_file:path",
    "value": "$DIR/yarn.lock"
  },
  {
    "name": "gitlab:dependency_scanning:package_manager:name",
    "value": "yarn"
  },
  {
    "name": "gitlab:dependency_scanning:language:name",
    "value": "node"
  },
  {
    "name": "gitlab:meta:schema_version",
    "value": "1"
  }
]
EOF
  )

  jq "$SCRIPT" "$FILE" > "$FILE.tmp"
  mv "$FILE.tmp" "$FILE"
done < sboms.txt

rm -f sboms.txt

You can run this in pipeline with a configuration like so:

dependency-scanning:
  image: node:22-alpine
  stage: test
  before_script:
    - apk add --no-cache jq
  script:
    - ./generate-sbom.sh
  artifacts:
    untracked: false
    when: on_success
    access: developer
    paths:
      - "**/*.cdx.json"
    reports:
      cyclonedx: "**/*.cdx.json"
Edited by Oscar Tovar