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
- Sasha (Software Developer)
- Devon (DevOps Engineer)
- Sam (Security Analyst)
- Alex (Security Operations Engineer)
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:
- 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) - Parse the packages listed in the
package.jsondependencies section and add them to a queue. - 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.
- After iterating through all dependencies, set the package as visited.
visited["pkgName"] = true - 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"