syft
syft copied to clipboard
Duplicate entry in SBOM with local NPM dependencies
What happened:
Syft creates two entries in the SBOM for the local dependency, one of which doesn't have the details like version or license
What you expected to happen:
For there to only be one entry in the SBOM output
Steps to reproduce the issue:
- Create a
package.json
for your main project, e.g.
{
"name": "test",
"private": true,
"dependencies": {
"jquery": "file:./packages/example"
}
}
-
Create a local dependency, e.g. un the relative folder
./packages/example
-
Create a
package.json
file for the local dependency, e.g.
{
"name": "example",
"private": true,
"version": "8.8.8",
"license": "Apache-2.0"
}
- Build and run Syft to generate the SBOM like so
npm i && syft . -o cyclonedx-json=sbom.cyclonedx.json --exclude './packages/*'
- Read the SBOM, Syft has created two components based on reading the
package-lock.json
, one for the declared dependency in thepackage.json
, and another from NPM resolving it to the local dependency.
E.g.
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.5",
"serialNumber": "urn:uuid:7e154fa4-8d8a-41ed-aaba-6f67b5659164",
"version": 1,
"metadata": {
"timestamp": "2024-01-29T21:41:02+01:00",
"tools": {
"components": [
{
"type": "application",
"author": "anchore",
"name": "syft",
"version": "0.101.1"
}
]
},
"component": { "bom-ref": "af63bd4c8601b7f1", "type": "file", "name": "." }
},
"components": [
{
"bom-ref": "pkg:npm/example?package-id=da2139eb2ab28c91",
"type": "library",
"name": "example",
"cpe": "cpe:2.3:a:example:example:*:*:*:*:*:*:*:*",
"purl": "pkg:npm/example",
"properties": [
{
"name": "syft:package:foundBy",
"value": "javascript-lock-cataloger"
},
{ "name": "syft:package:language", "value": "javascript" },
{ "name": "syft:package:type", "value": "npm" },
{
"name": "syft:package:metadataType",
"value": "javascript-npm-package-lock-entry"
},
{ "name": "syft:location:0:path", "value": "/package-lock.json" }
]
},
{
"bom-ref": "pkg:npm/packages/[email protected]?package-id=4043fd3647ed6400",
"type": "library",
"name": "packages/example",
"version": "8.8.8",
"licenses": [{ "license": { "id": "Apache-2.0" } }],
"cpe": "cpe:2.3:a:packages\\/example:packages\\/example:8.8.8:*:*:*:*:*:*:*",
"purl": "pkg:npm/packages/[email protected]",
"properties": [
{
"name": "syft:package:foundBy",
"value": "javascript-lock-cataloger"
},
{ "name": "syft:package:language", "value": "javascript" },
{ "name": "syft:package:type", "value": "npm" },
{
"name": "syft:package:metadataType",
"value": "javascript-npm-package-lock-entry"
},
{ "name": "syft:location:0:path", "value": "/package-lock.json" }
]
},
{
"bom-ref": "pkg:npm/test?package-id=d4cce09498717bb5",
"type": "library",
"name": "test",
"cpe": "cpe:2.3:a:test:test:*:*:*:*:*:*:*:*",
"purl": "pkg:npm/test",
"properties": [
{
"name": "syft:package:foundBy",
"value": "javascript-lock-cataloger"
},
{ "name": "syft:package:language", "value": "javascript" },
{ "name": "syft:package:type", "value": "npm" },
{
"name": "syft:package:metadataType",
"value": "javascript-npm-package-lock-entry"
},
{ "name": "syft:location:0:path", "value": "/package-lock.json" }
]
}
]
}
Anything else we need to know?:
Purged the package-lock.json
and node_modules
folder before npm i
and generating the SBOMs just to make sure it's fresh
Environment:
-
Node v20.10.0
-
NPM 10.2.3
-
Output of
syft version
:
Application: syft
Version: 0.101.1
BuildDate: 2024-01-19T22:02:04Z
GitCommit: 3eab5932e5271eea5506ab9710239b1415c827f8
GitDescription: v0.101.1
Platform: linux/amd64
GoVersion: go1.21.5
Compiler: gc
- OS (e.g:
cat /etc/os-release
or similar):
WSL2 Ubuntu 22.04
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.3 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
@atl-mk I definitely see your point that the "link" package in the package-lock.json is does not seem to be handled correctly here.
I'm unsure on which package do you think should be dropped here.
Is exclude working on the way you expect it ?
Do you think that we should be dropping the package that is under the path that was excluded?
Or
Do you think we should be dropping the link packages and not reporting on those as part of the SBOM?
How do you think these "link" packages should be reported as so that the SBOM doesn't lose this information and can express the resolved nature of what was declared in the package.json vs what was cataloged by syft?
cc @willmurphyscode
@spiffcs Exclude has no effect here, because Syft is only creating the SBOM from the package-lock.json
file. It doesn't matter if the exclude argument is used. I only included it in the reproduction steps to show that's not the issue.
The top and middle one should be merged, like so:
{
"bom-ref": "pkg:npm/example?package-id=4790f192e386e4d1",
"type": "library",
"name": "example",
"version": "8.8.8",
"licenses": [
{
"license": {
"id": "Apache-2.0"
}
}
],
"cpe": "cpe:2.3:a:packages\\/example:packages\\/example:8.8.8:*:*:*:*:*:*:*",
"purl": "pkg:npm/packages/[email protected]",
"properties": [
{
"name": "syft:package:foundBy",
"value": "javascript-lock-cataloger"
},
{
"name": "syft:package:language",
"value": "javascript"
},
{
"name": "syft:package:type",
"value": "npm"
},
{
"name": "syft:package:metadataType",
"value": "javascript-npm-package-lock-entry"
},
{
"name": "syft:location:0:path",
"value": "/package-lock.json"
}
]
},
The name should reflect what is declared normally (just example
), and CPE+PackageURL should map to the local files on disk.
For reference, this is what the package-lock.json
file looks like
{
"name": "test",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "test",
"dependencies": {
"example": "file:./packages/example"
}
},
"node_modules/example": {
"resolved": "packages/example",
"link": true
},
"packages/example": {
"version": "8.8.8",
"license": "Apache-2.0"
}
}
}
So clearly Syft has some level of resolving linked dependencies from the package-lock.json
, I don't see any reason to report the dependency alias.
Here's a related use-case that I'm not personally worried about, but could be of interest to you: https://gist.github.com/nandorojo/1b969a0d88cf81ca8a2a334a5bd2ee4a
@anchore/tools I've added this one to Ready.
Based on our team sync we have agreed on removing node_modules/example
from the final SBOM.
Here is the documentation for package-lock.json: https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#packages
There does not seem to be extra information in the "link" package so taking one over the other (not merging) should be the action here.
No relationships need to be updated for this ticket.