mirror of
https://github.com/anchore/syft.git
synced 2026-11-04 00:16:38 +01:00
Associate node package licenses from node_modules (#1152)
This commit is contained in:
@@ -6,10 +6,9 @@ package common
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
@@ -19,14 +18,18 @@ import (
|
||||
type GenericCataloger struct {
|
||||
globParsers map[string]ParserFn
|
||||
pathParsers map[string]ParserFn
|
||||
postProcessors []PostProcessFunc
|
||||
upstreamCataloger string
|
||||
}
|
||||
|
||||
type PostProcessFunc func(resolver source.FileResolver, location source.Location, p *pkg.Package) error
|
||||
|
||||
// NewGenericCataloger if provided path-to-parser-function and glob-to-parser-function lookups creates a GenericCataloger
|
||||
func NewGenericCataloger(pathParsers map[string]ParserFn, globParsers map[string]ParserFn, upstreamCataloger string) *GenericCataloger {
|
||||
func NewGenericCataloger(pathParsers map[string]ParserFn, globParsers map[string]ParserFn, upstreamCataloger string, postProcessors ...PostProcessFunc) *GenericCataloger {
|
||||
return &GenericCataloger{
|
||||
globParsers: globParsers,
|
||||
pathParsers: pathParsers,
|
||||
postProcessors: postProcessors,
|
||||
upstreamCataloger: upstreamCataloger,
|
||||
}
|
||||
}
|
||||
@@ -69,6 +72,13 @@ func (c *GenericCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package,
|
||||
continue
|
||||
}
|
||||
|
||||
for _, postProcess := range c.postProcessors {
|
||||
err = postProcess(resolver, location, p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
packages = append(packages, *p)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,15 @@ Package javascript provides a concrete Cataloger implementation for JavaScript e
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/common"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// NewJavascriptPackageCataloger returns a new JavaScript cataloger object based on detection of npm based packages.
|
||||
@@ -23,5 +31,53 @@ func NewJavascriptLockCataloger() *common.GenericCataloger {
|
||||
"**/yarn.lock": parseYarnLock,
|
||||
}
|
||||
|
||||
return common.NewGenericCataloger(nil, globParsers, "javascript-lock-cataloger")
|
||||
return common.NewGenericCataloger(nil, globParsers, "javascript-lock-cataloger", addLicenses)
|
||||
}
|
||||
|
||||
func addLicenses(resolver source.FileResolver, location source.Location, p *pkg.Package) error {
|
||||
dir := path.Dir(location.RealPath)
|
||||
pkgPath := []string{dir, "node_modules"}
|
||||
pkgPath = append(pkgPath, strings.Split(p.Name, "/")...)
|
||||
pkgPath = append(pkgPath, "package.json")
|
||||
pkgFile := path.Join(pkgPath...)
|
||||
locations, err := resolver.FilesByPath(pkgFile)
|
||||
if err != nil {
|
||||
log.Debugf("an error occurred attempting to read: %s - %+v", pkgFile, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(locations) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, location := range locations {
|
||||
contentReader, err := resolver.FileContentsByLocation(location)
|
||||
if err != nil {
|
||||
log.Debugf("error getting file content reader for %s: %v", pkgFile, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
contents, err := io.ReadAll(contentReader)
|
||||
if err != nil {
|
||||
log.Debugf("error reading file contents for %s: %v", pkgFile, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var pkgJSON packageJSON
|
||||
err = json.Unmarshal(contents, &pkgJSON)
|
||||
if err != nil {
|
||||
log.Debugf("error parsing %s: %v", pkgFile, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
licenses, err := pkgJSON.licensesFromJSON()
|
||||
if err != nil {
|
||||
log.Debugf("error getting licenses from %s: %v", pkgFile, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
p.Licenses = licenses
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package javascript
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_JavascriptCataloger(t *testing.T) {
|
||||
expected := map[string]pkg.Package{
|
||||
"@actions/core": {
|
||||
Name: "@actions/core",
|
||||
Version: "1.6.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
"wordwrap": {
|
||||
Name: "wordwrap",
|
||||
Version: "0.0.3",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"get-stdin": {
|
||||
Name: "get-stdin",
|
||||
Version: "5.0.1",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"minimist": {
|
||||
Name: "minimist",
|
||||
Version: "0.0.10",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"optimist": {
|
||||
Name: "optimist",
|
||||
Version: "0.6.1",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"string-width": {
|
||||
Name: "string-width",
|
||||
Version: "2.1.1",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"strip-ansi": {
|
||||
Name: "strip-ansi",
|
||||
Version: "4.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"strip-eof": {
|
||||
Name: "wordwrap",
|
||||
Version: "1.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"ansi-regex": {
|
||||
Name: "ansi-regex",
|
||||
Version: "3.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
Name: "is-fullwidth-code-point",
|
||||
Version: "2.0.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"cowsay": {
|
||||
Name: "cowsay",
|
||||
Version: "1.4.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: []string{"MIT"},
|
||||
},
|
||||
}
|
||||
|
||||
s, err := source.NewFromDirectory("test-fixtures/pkg-lock")
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := s.FileResolver(source.AllLayersScope)
|
||||
require.NoError(t, err)
|
||||
|
||||
actual, _, err := NewJavascriptLockCataloger().Catalog(resolver)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to parse package-lock.json: %+v", err)
|
||||
}
|
||||
|
||||
var pkgs []*pkg.Package
|
||||
for _, p := range actual {
|
||||
p2 := p
|
||||
pkgs = append(pkgs, &p2)
|
||||
}
|
||||
|
||||
assertPkgsEqual(t, pkgs, expected)
|
||||
}
|
||||
@@ -30,6 +30,12 @@ func assertPkgsEqual(t *testing.T, actual []*pkg.Package, expected map[string]pk
|
||||
|
||||
func TestParsePackageLock(t *testing.T) {
|
||||
expected := map[string]pkg.Package{
|
||||
"@actions/core": {
|
||||
Name: "@actions/core",
|
||||
Version: "1.6.0",
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
"wordwrap": {
|
||||
Name: "wordwrap",
|
||||
Version: "0.0.3",
|
||||
|
||||
Generated
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@actions/core",
|
||||
"version": "1.6.0",
|
||||
"description": "Actions core lib",
|
||||
"keywords": [
|
||||
"github",
|
||||
"actions",
|
||||
"core"
|
||||
],
|
||||
"homepage": "https://github.com/actions/toolkit/tree/main/packages/core",
|
||||
"license": "MIT",
|
||||
"main": "lib/core.js",
|
||||
"types": "lib/core.d.ts",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"!.DS_Store"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/actions/toolkit.git",
|
||||
"directory": "packages/core"
|
||||
},
|
||||
"scripts": {
|
||||
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1",
|
||||
"tsc": "tsc"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/actions/toolkit/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^1.0.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.2"
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "cowsay",
|
||||
"version": "1.4.0",
|
||||
"description": "cowsay is a configurable talking cow",
|
||||
"keywords": [
|
||||
"cow",
|
||||
"cowsay",
|
||||
"cowthink",
|
||||
"figlet",
|
||||
"talking",
|
||||
"ASCII"
|
||||
],
|
||||
"homepage": "https://github.com/piuccio/cowsay",
|
||||
"author": {
|
||||
"name": "Fabio Crisci",
|
||||
"email": "piuccio@gmail.com",
|
||||
"url": "https://github.com/piuccio/"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "./index",
|
||||
"module": "./build/cowsay.es.js",
|
||||
"browser": "./build/cowsay.umd.js",
|
||||
"bin": {
|
||||
"cowsay": "./cli.js",
|
||||
"cowthink": "./cli.js"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"cli.js",
|
||||
"build/",
|
||||
"cows/",
|
||||
"lib/"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/piuccio/cowsay.git"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "rollup -c",
|
||||
"test": "node test.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"get-stdin": "^5.0.1",
|
||||
"optimist": "~0.6.1",
|
||||
"string-width": "~2.1.1",
|
||||
"strip-eof": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodeunit": "~0.11.1",
|
||||
"rollup": "^0.48.2",
|
||||
"rollup-plugin-commonjs": "^8.2.0",
|
||||
"rollup-plugin-node-resolve": "^3.0.0",
|
||||
"rollup-plugin-string": "^2.0.2"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,14 @@
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
|
||||
"integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^1.0.11"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
|
||||
|
||||
Reference in New Issue
Block a user