syft icon indicating copy to clipboard operation
syft copied to clipboard

Dependency's MIT license not picked up when scanning package-lock.json

Open robertlagrant opened this issue 2 years ago • 7 comments

What happened Outputting a CycloneDX SBOM does not pick up the sole dependency's licence in its output.

What you expected to happen: Given I can see the dependency inside node_modules has a licence (MIT) in its package.json, and it also comes with a LICENSE file with the MIT licence text in it, I would expect the licence to appear in the cyclonedx output.

How to reproduce it (as minimally and precisely as possible):

% mkdir demo-syft-licence-bug
% cd demo-syft-licence-bug
demo-syft-licence-bug % npm init
<!snip!>
demo-syft-licence-bug % cat package.json
{
  "name": "demo-syft-licence-bug",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Rob Grant <>",
  "license": "MIT"
}
demo-syft-licence-bug % npm install es-leftpad
added 1 package, and audited 2 packages in 2s
found 0 vulnerabilities
demo-syft-licence-bug %
demo-syft-licence-bug % demo-syft-licence-bug % syft file:package-lock.json -o cyclonedx
 ✔ Indexed package-lock.json
 ✔ Cataloged packages      [1 packages]
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:fbd2213e-3623-413a-89e6-df0eb77043cf" version="1">
  <metadata>
    <timestamp>2022-07-21T09:47:20+01:00</timestamp>
    <tools>
      <tool>
        <vendor>anchore</vendor>
        <name>syft</name>
        <version>0.51.0</version>
      </tool>
    </tools>
    <component bom-ref="f11fc099fef741ab" type="file">
      <name>package-lock.json</name>
    </component>
  </metadata>
  <components>
    <component bom-ref="pkg:npm/[email protected]?package-id=38a238232ed53007" type="library">
      <name>es-leftpad</name>
      <version>1.0.0</version>
      <cpe>cpe:2.3:a:es-leftpad:es-leftpad:1.0.0:*:*:*:*:*:*:*</cpe>
      <purl>pkg:npm/[email protected]</purl>
      <properties>
        <property name="syft:package:foundBy">javascript-lock-cataloger</property>
        <property name="syft:package:language">javascript</property>
        <property name="syft:package:type">npm</property>
        <property name="syft:cpe23">cpe:2.3:a:es-leftpad:es_leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:cpe23">cpe:2.3:a:es_leftpad:es-leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:cpe23">cpe:2.3:a:es_leftpad:es_leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:cpe23">cpe:2.3:a:es:es-leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:cpe23">cpe:2.3:a:es:es_leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:cpe23">cpe:2.3:a:*:es-leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:cpe23">cpe:2.3:a:*:es_leftpad:1.0.0:*:*:*:*:*:*:*</property>
        <property name="syft:location:0:path">/Users/rob.grant/code/demo-syft-licence-bug/package-lock.json</property>
      </properties>
    </component>
  </components>
</bom>
demo-syft-licence-bug %

^ no licence listed in the cyclonedx output.

Anything else we need to know?: It also doesn't appear if I output with SPDX format. If I try syft-json then it says "licences": [],.

Environment:

  • Output of syft version:
demo-syft-licence-bug % syft version
Application:        syft
Version:            0.51.0
JsonSchemaVersion:  3.3.1
BuildDate:          2022-07-11T17:12:56Z
GitCommit:          470b13045bbbf150f3c79d1487a01ae6acc5592d
GitDescription:     v0.51.0
Platform:           darwin/arm64
GoVersion:          go1.18.3
Compiler:           gc
  • OS (e.g: cat /etc/os-release or similar): OSX 12.4 Monterey.

robertlagrant avatar Jul 21 '22 09:07 robertlagrant

Similar issue linked here! Thanks for filing the bug. We'll take a look and see what needs to be done to get licenses populated for dir scans.

https://github.com/anchore/syft/issues/845

spiffcs avatar Jul 21 '22 13:07 spiffcs

@spiffcs thank you! Just to be doubly sure, the above was meant to be me running against a lockfile, and not doing a dir scan! I ran this: syft file:package-lock.json -o cyclonedx.

robertlagrant avatar Jul 21 '22 14:07 robertlagrant

Thanks for the clarification!

It's an interesting case because it poses the question "Given some manifest file, should syft traverse extra noninput paths to find additional metadata information?"

package-lock.json does not have license information in and of itself, so syft in its current state is doing the best it can with the information provided.

I'm going to add it to our community meeting today for discussion since we've knocked around crossing this boundary before but have stayed on the side of reporting only on the truth of the input and not something the tool inferred.

At any rate we definitely should solve for dir:. in this case and conclude licenses for our raw JSON output when node_modules are included in the scan.

spiffcs avatar Jul 21 '22 14:07 spiffcs

Yes, when you lay it out like that I see more the depth of the complexity. It's even trickier when perhaps the package.json license field and the text in the LICEN[CS]E file don't match!

I'm currently using a tool called cdxgen which optionally calls out to a server to get the results, so for big projects it takes a while to respond. Might be worth a look.

robertlagrant avatar Jul 22 '22 16:07 robertlagrant

I believe this may have been fixed when https://github.com/anchore/syft/issues/845 was implemented -- note: this requires running an npm install or similar, so package.json files are present in node_modules. Does this work for you now @robertlagrant ? (Also note: npm lockfiles v2 are now supported, which may include license information for dependencies.)

kzantow avatar Aug 23 '22 15:08 kzantow

I just went back to validate this and it looks like after running the reproduce steps above we do not get the license from just package-lock.json:

Screen Shot 2022-08-31 at 10 27 53 AM

The license IS picked up when running syft dir:. -o cyclonedx: Screen Shot 2022-08-31 at 10 28 31 AM

It looks like resolver.FilesByPath in the function addLicenses is not returning a location. https://github.com/anchore/syft/blob/1b0cfe7246a1c3aef0ea3dcaccee976ec8e21814/syft/pkg/cataloger/javascript/cataloger.go#L38-L45

The correct path for the package.json is requested: Screen Shot 2022-08-31 at 11 00 17 AM

But the resolver returns an empty slice of locations: Screen Shot 2022-08-31 at 11 02 14 AM

I can't remember if we opted for allowing syft to traverse over to node modules even if the user specified only the file directive as a prefix.

spiffcs avatar Aug 31 '22 15:08 spiffcs

If we do want this to work where node_modules is accounted for as a post catalog task I think we would need to rebuild the tree for this post process function and then use RelativeFileByPath forming something like ../node_modules/{package}/package.json

spiffcs avatar Aug 31 '22 15:08 spiffcs

This is expected behavior when using the file: prefix... if you want directory traversals, you should use the dir: prefix.

kzantow avatar Nov 17 '22 21:11 kzantow