mirror of
https://github.com/anchore/syft.git
synced 2026-05-20 04:05:24 +02:00
fix(javascript): accept scalar people fields in package.json (#4779)
Signed-off-by: Yoav Alon <yoav@orca.security>
This commit is contained in:
parent
952469f0f0
commit
30fe53e629
@ -215,46 +215,23 @@ func pathContainsNodeModulesDirectory(p string) bool {
|
||||
}
|
||||
|
||||
func (p *people) UnmarshalJSON(b []byte) error {
|
||||
// Try to unmarshal as an array of strings
|
||||
var authorStrings []string
|
||||
if err := json.Unmarshal(b, &authorStrings); err == nil {
|
||||
// Successfully parsed as an array of strings
|
||||
auths := make([]person, len(authorStrings))
|
||||
for i, authorStr := range authorStrings {
|
||||
// Parse each string into author fields
|
||||
fields := internal.MatchNamedCaptureGroups(authorPattern, authorStr)
|
||||
var auth person
|
||||
if err := mapstructure.Decode(fields, &auth); err != nil {
|
||||
return fmt.Errorf("unable to decode package.json author: %w", err)
|
||||
}
|
||||
// Trim whitespace from name if it was parsed
|
||||
if auth.Name != "" {
|
||||
auth.Name = strings.TrimSpace(auth.Name)
|
||||
}
|
||||
auths[i] = auth
|
||||
}
|
||||
*p = auths
|
||||
return nil
|
||||
// Accept either a JSON array of authors, or a single author as a string or
|
||||
// object — the latter is used in the wild (e.g. ghost@5.98.1) and dropping
|
||||
// the whole package.json on those was https://github.com/anchore/syft/issues/4778.
|
||||
var elements []json.RawMessage
|
||||
if err := json.Unmarshal(b, &elements); err != nil {
|
||||
// not an array — treat the whole payload as a single element
|
||||
elements = []json.RawMessage{b}
|
||||
}
|
||||
|
||||
// Try to unmarshal as an array of objects
|
||||
var authorObjs []map[string]any
|
||||
if err := json.Unmarshal(b, &authorObjs); err == nil {
|
||||
// Successfully parsed as an array of objects
|
||||
auths := make([]person, len(authorObjs))
|
||||
for i, fields := range authorObjs {
|
||||
var auth person
|
||||
if err := mapstructure.Decode(fields, &auth); err != nil {
|
||||
return fmt.Errorf("unable to decode package.json author object: %w", err)
|
||||
}
|
||||
auths[i] = auth
|
||||
auths := make([]person, len(elements))
|
||||
for i, e := range elements {
|
||||
if err := json.Unmarshal(e, &auths[i]); err != nil {
|
||||
return fmt.Errorf("unable to parse package.json author: %w", err)
|
||||
}
|
||||
*p = auths
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we get here, it means neither format matched
|
||||
return fmt.Errorf("unable to parse package.json authors field: expected array of strings or array of objects")
|
||||
*p = auths
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p people) String() string {
|
||||
|
||||
@ -307,6 +307,71 @@ func TestParsePackageJSON(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// see https://github.com/anchore/syft/issues/4778
|
||||
// Ghost CMS publishes package.json with contributors as a single string
|
||||
Fixture: "testdata/pkg-json/package-contributors-string.json",
|
||||
ExpectedPkg: pkg.Package{
|
||||
Name: "ghost",
|
||||
Version: "5.98.1",
|
||||
PURL: "pkg:npm/ghost@5.98.1",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("testdata/pkg-json/package-contributors-string.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
Name: "ghost",
|
||||
Version: "5.98.1",
|
||||
Author: "https://github.com/TryGhost/Ghost/graphs/contributors",
|
||||
Homepage: "https://docs.npmjs.com/",
|
||||
URL: "https://github.com/npm/cli",
|
||||
Description: "a package manager for JavaScript",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Fixture: "testdata/pkg-json/package-authors-string.json",
|
||||
ExpectedPkg: pkg.Package{
|
||||
Name: "npm",
|
||||
Version: "6.14.6",
|
||||
PURL: "pkg:npm/npm@6.14.6",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("testdata/pkg-json/package-authors-string.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
Name: "npm",
|
||||
Version: "6.14.6",
|
||||
Author: "Harry Potter <hp@hogwards.com> (http://youknowwho.com/)",
|
||||
Homepage: "https://docs.npmjs.com/",
|
||||
URL: "https://github.com/npm/cli",
|
||||
Description: "a package manager for JavaScript",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Fixture: "testdata/pkg-json/package-maintainers-object.json",
|
||||
ExpectedPkg: pkg.Package{
|
||||
Name: "npm",
|
||||
Version: "6.14.6",
|
||||
PURL: "pkg:npm/npm@6.14.6",
|
||||
Type: pkg.NpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Artistic-2.0", file.NewLocation("testdata/pkg-json/package-maintainers-object.json")),
|
||||
),
|
||||
Language: pkg.JavaScript,
|
||||
Metadata: pkg.NpmPackage{
|
||||
Name: "npm",
|
||||
Version: "6.14.6",
|
||||
Author: "Charlie Maintainer <charlie@example.com>",
|
||||
Homepage: "https://docs.npmjs.com/",
|
||||
URL: "https://github.com/npm/cli",
|
||||
Description: "a package manager for JavaScript",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Fixture: "testdata/pkg-json/package-all-author-fields.json",
|
||||
ExpectedPkg: pkg.Package{
|
||||
|
||||
12
syft/pkg/cataloger/javascript/testdata/pkg-json/package-authors-string.json
vendored
Normal file
12
syft/pkg/cataloger/javascript/testdata/pkg-json/package-authors-string.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "6.14.6",
|
||||
"name": "npm",
|
||||
"description": "a package manager for JavaScript",
|
||||
"homepage": "https://docs.npmjs.com/",
|
||||
"authors": "Harry Potter <hp@hogwards.com> (http://youknowwho.com/)",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/npm/cli"
|
||||
},
|
||||
"license": "Artistic-2.0"
|
||||
}
|
||||
12
syft/pkg/cataloger/javascript/testdata/pkg-json/package-contributors-string.json
vendored
Normal file
12
syft/pkg/cataloger/javascript/testdata/pkg-json/package-contributors-string.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": "5.98.1",
|
||||
"name": "ghost",
|
||||
"description": "a package manager for JavaScript",
|
||||
"homepage": "https://docs.npmjs.com/",
|
||||
"contributors": "https://github.com/TryGhost/Ghost/graphs/contributors",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/npm/cli"
|
||||
},
|
||||
"license": "Artistic-2.0"
|
||||
}
|
||||
15
syft/pkg/cataloger/javascript/testdata/pkg-json/package-maintainers-object.json
vendored
Normal file
15
syft/pkg/cataloger/javascript/testdata/pkg-json/package-maintainers-object.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "6.14.6",
|
||||
"name": "npm",
|
||||
"description": "a package manager for JavaScript",
|
||||
"homepage": "https://docs.npmjs.com/",
|
||||
"maintainers": {
|
||||
"name": "Charlie Maintainer",
|
||||
"email": "charlie@example.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/npm/cli"
|
||||
},
|
||||
"license": "Artistic-2.0"
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user