From 48c7dee9dab2a38f56514a65ba321be9c775fbb6 Mon Sep 17 00:00:00 2001 From: Toure Date: Fri, 23 Oct 2020 08:50:38 -0400 Subject: [PATCH] included additional support for older npm spec. Signed-off-by: Toure --- .../javascript/parse_package_json.go | 35 ++++++++-- .../javascript/parse_package_json_test.go | 15 +++++ .../pkg-json/package-repo-string.json | 66 +++++++++++++++++++ 3 files changed, 110 insertions(+), 6 deletions(-) create mode 100644 syft/cataloger/javascript/test-fixtures/pkg-json/package-repo-string.json diff --git a/syft/cataloger/javascript/parse_package_json.go b/syft/cataloger/javascript/parse_package_json.go index a0ebc899f..b8d5a3dfb 100644 --- a/syft/cataloger/javascript/parse_package_json.go +++ b/syft/cataloger/javascript/parse_package_json.go @@ -37,16 +37,15 @@ type Author struct { } type Repository struct { - Type string `json:"type"` - URL string `json:"url"` + Type string `json:"type" mapstructure:"type"` + URL string `json:"url" mapstructure:"url"` } // match example: "author": "Isaac Z. Schlueter (http://blog.izs.me)" // ---> name: "Isaac Z. Schlueter" email: "i@izs.me" url: "http://blog.izs.me" var authorPattern = regexp.MustCompile(`^\s*(?P[^<(]*)(\s+<(?P.*)>)?(\s\((?P.*)\))?\s*$`) -// This method implements the UnmarshalJSON interface to help normalize -// the json structure. +// Exports Author.UnmarshalJSON interface to help normalize the json structure. func (a *Author) UnmarshalJSON(b []byte) error { var authorStr string var fields map[string]string @@ -73,7 +72,7 @@ func (a *Author) UnmarshalJSON(b []byte) error { return nil } -func (a *Author) String() string { +func (a *Author) AuthorString() string { result := a.Name if a.Email != "" { result += fmt.Sprintf(" <%s>", a.Email) @@ -84,6 +83,30 @@ func (a *Author) String() string { return result } +func (r *Repository) UnmarshalJSON(b []byte) error { + var repositoryStr string + var fields map[string]string + var repository Repository + + if err := json.Unmarshal(b, &repositoryStr); err != nil { + // string parsing did not work, assume a map was given + // for more information: https://docs.npmjs.com/files/package.json#people-fields-author-contributors + if err := json.Unmarshal(b, &fields); err != nil { + return fmt.Errorf("unable to parse package.json author: %w", err) + } + // translate the map into a structure + if err := mapstructure.Decode(fields, &repository); err != nil { + return fmt.Errorf("unable to decode package.json author: %w", err) + } + + *r = repository + } else { + r.URL = repositoryStr + } + + return nil +} + // parsePackageJson parses a package.json and returns the discovered JavaScript packages. func parsePackageJSON(_ string, reader io.Reader) ([]pkg.Package, error) { packages := make([]pkg.Package, 0) @@ -104,7 +127,7 @@ func parsePackageJSON(_ string, reader io.Reader) ([]pkg.Package, error) { Language: pkg.JavaScript, Type: pkg.NpmPkg, Metadata: pkg.NpmMetadata{ - Author: p.Author.String(), + Author: p.Author.AuthorString(), Homepage: p.Homepage, URL: p.Repository.URL, }, diff --git a/syft/cataloger/javascript/parse_package_json_test.go b/syft/cataloger/javascript/parse_package_json_test.go index b135a8e27..bb5d26d08 100644 --- a/syft/cataloger/javascript/parse_package_json_test.go +++ b/syft/cataloger/javascript/parse_package_json_test.go @@ -43,6 +43,21 @@ func TestParsePackageJSON(t *testing.T) { }, }, }, + { + Fixture: "test-fixtures/pkg-json/package-repo-string.json", + ExpectedPkg: pkg.Package{ + Name: "function-bind", + Version: "1.1.1", + Type: pkg.NpmPkg, + Licenses: []string{"MIT"}, + Language: pkg.JavaScript, + Metadata: pkg.NpmMetadata{ + Author: "Raynos ", + Homepage: "https://github.com/Raynos/function-bind", + URL: "git://github.com/Raynos/function-bind.git", + }, + }, + }, } for _, test := range tests { diff --git a/syft/cataloger/javascript/test-fixtures/pkg-json/package-repo-string.json b/syft/cataloger/javascript/test-fixtures/pkg-json/package-repo-string.json new file mode 100644 index 000000000..d42a73b5c --- /dev/null +++ b/syft/cataloger/javascript/test-fixtures/pkg-json/package-repo-string.json @@ -0,0 +1,66 @@ +{ + "name": "function-bind", + "version": "1.1.1", + "description": "Implementation of Function.prototype.bind", + "keywords": [ + "function", + "bind", + "shim", + "es5" + ], + "author": "Raynos ", + "repository": "git://github.com/Raynos/function-bind.git", + "main": "index", + "homepage": "https://github.com/Raynos/function-bind", + "contributors": [ + { + "name": "Raynos" + }, + { + "name": "Jordan Harband", + "url": "https://github.com/ljharb" + } + ], + "bugs": { + "url": "https://github.com/Raynos/function-bind/issues", + "email": "raynos2@gmail.com" + }, + "dependencies": {}, + "devDependencies": { + "@ljharb/eslint-config": "^12.2.1", + "covert": "^1.1.0", + "eslint": "^4.5.0", + "jscs": "^3.0.7", + "tape": "^4.8.0" + }, + "license": "MIT", + "scripts": { + "pretest": "npm run lint", + "test": "npm run tests-only", + "posttest": "npm run coverage -- --quiet", + "tests-only": "node test", + "coverage": "covert test/*.js", + "lint": "npm run jscs && npm run eslint", + "jscs": "jscs *.js */*.js", + "eslint": "eslint *.js */*.js" + }, + "testling": { + "files": "test/index.js", + "browsers": [ + "ie/8..latest", + "firefox/16..latest", + "firefox/nightly", + "chrome/22..latest", + "chrome/canary", + "opera/12..latest", + "opera/next", + "safari/5.1..latest", + "ipad/6.0..latest", + "iphone/6.0..latest", + "android-browser/4.2..latest" + ] + } +,"_resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" +,"_integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" +,"_from": "function-bind@1.1.1" +} \ No newline at end of file