mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
fix: duplicate packages, support pnpm lockfile v6 (#1778)
This commit is contained in:
parent
798af57853
commit
a3c5550217
3 changed files with 277 additions and 5 deletions
|
@ -3,10 +3,13 @@ package javascript
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
|
@ -17,8 +20,9 @@ import (
|
|||
var _ generic.Parser = parsePnpmLock
|
||||
|
||||
type pnpmLockYaml struct {
|
||||
Dependencies map[string]string `json:"dependencies"`
|
||||
Packages map[string]interface{} `json:"packages"`
|
||||
Version string `json:"lockfileVersion" yaml:"lockfileVersion"`
|
||||
Dependencies map[string]interface{} `json:"dependencies" yaml:"dependencies"`
|
||||
Packages map[string]interface{} `json:"packages" yaml:"packages"`
|
||||
}
|
||||
|
||||
func parsePnpmLock(resolver source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
|
@ -34,19 +38,55 @@ func parsePnpmLock(resolver source.FileResolver, _ *generic.Environment, reader
|
|||
return nil, nil, fmt.Errorf("failed to parse pnpm-lock.yaml file: %w", err)
|
||||
}
|
||||
|
||||
for name, version := range lockFile.Dependencies {
|
||||
lockVersion, _ := strconv.ParseFloat(lockFile.Version, 64)
|
||||
|
||||
for name, info := range lockFile.Dependencies {
|
||||
version := ""
|
||||
|
||||
switch info := info.(type) {
|
||||
case string:
|
||||
version = info
|
||||
case map[string]interface{}:
|
||||
v, ok := info["version"]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
ver, ok := v.(string)
|
||||
if ok {
|
||||
version = parseVersion(ver)
|
||||
}
|
||||
default:
|
||||
log.Tracef("unsupported pnpm dependency type: %+v", info)
|
||||
continue
|
||||
}
|
||||
|
||||
if hasPkg(pkgs, name, version) {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPnpmPackage(resolver, reader.Location, name, version))
|
||||
}
|
||||
|
||||
packageNameRegex := regexp.MustCompile(`^/?([^(]*)(?:\(.*\))*$`)
|
||||
splitChar := "/"
|
||||
if lockVersion >= 6.0 {
|
||||
splitChar = "@"
|
||||
}
|
||||
|
||||
// parse packages from packages section of pnpm-lock.yaml
|
||||
for nameVersion := range lockFile.Packages {
|
||||
nameVersionSplit := strings.Split(strings.TrimPrefix(nameVersion, "/"), "/")
|
||||
nameVersion = packageNameRegex.ReplaceAllString(nameVersion, "$1")
|
||||
nameVersionSplit := strings.Split(strings.TrimPrefix(nameVersion, "/"), splitChar)
|
||||
|
||||
// last element in split array is version
|
||||
version := nameVersionSplit[len(nameVersionSplit)-1]
|
||||
|
||||
// construct name from all array items other than last item (version)
|
||||
name := strings.Join(nameVersionSplit[:len(nameVersionSplit)-1], "/")
|
||||
name := strings.Join(nameVersionSplit[:len(nameVersionSplit)-1], splitChar)
|
||||
|
||||
if hasPkg(pkgs, name, version) {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPnpmPackage(resolver, reader.Location, name, version))
|
||||
}
|
||||
|
@ -55,3 +95,16 @@ func parsePnpmLock(resolver source.FileResolver, _ *generic.Environment, reader
|
|||
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
func hasPkg(pkgs []pkg.Package, name, version string) bool {
|
||||
for _, p := range pkgs {
|
||||
if p.Name == name && p.Version == version {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseVersion(version string) string {
|
||||
return strings.SplitN(version, "(", 2)[0]
|
||||
}
|
||||
|
|
|
@ -52,3 +52,95 @@ func TestParsePnpmLock(t *testing.T) {
|
|||
|
||||
pkgtest.TestFileParser(t, fixture, parsePnpmLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func TestParsePnpmV6Lock(t *testing.T) {
|
||||
var expectedRelationships []artifact.Relationship
|
||||
fixture := "test-fixtures/pnpm-v6/pnpm-lock.yaml"
|
||||
|
||||
locationSet := source.NewLocationSet(source.NewLocation(fixture))
|
||||
|
||||
expectedPkgs := []pkg.Package{
|
||||
{
|
||||
Name: "@testing-library/jest-dom",
|
||||
Version: "5.16.5",
|
||||
PURL: "pkg:npm/%40testing-library/jest-dom@5.16.5",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "@testing-library/react",
|
||||
Version: "13.4.0",
|
||||
PURL: "pkg:npm/%40testing-library/react@13.4.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "@testing-library/user-event",
|
||||
Version: "13.5.0",
|
||||
PURL: "pkg:npm/%40testing-library/user-event@13.5.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "react",
|
||||
Version: "18.2.0",
|
||||
PURL: "pkg:npm/react@18.2.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "react-dom",
|
||||
Version: "18.2.0",
|
||||
PURL: "pkg:npm/react-dom@18.2.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "web-vitals",
|
||||
Version: "2.1.4",
|
||||
PURL: "pkg:npm/web-vitals@2.1.4",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "@babel/core",
|
||||
Version: "7.21.4",
|
||||
PURL: "pkg:npm/%40babel/core@7.21.4",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "@types/eslint",
|
||||
Version: "8.37.0",
|
||||
PURL: "pkg:npm/%40types/eslint@8.37.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "read-cache",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:npm/read-cache@1.0.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "schema-utils",
|
||||
Version: "3.1.2",
|
||||
PURL: "pkg:npm/schema-utils@3.1.2",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.TestFileParser(t, fixture, parsePnpmLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
lockfileVersion: '6.0'
|
||||
|
||||
dependencies:
|
||||
'@testing-library/jest-dom':
|
||||
specifier: ^5.16.5
|
||||
version: 5.16.5
|
||||
'@testing-library/react':
|
||||
specifier: ^13.4.0
|
||||
version: 13.4.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@testing-library/user-event':
|
||||
specifier: ^13.5.0
|
||||
version: 13.5.0(@testing-library/dom@9.2.0)
|
||||
react:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0
|
||||
react-dom:
|
||||
specifier: ^18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
web-vitals:
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4
|
||||
|
||||
packages:
|
||||
/@babel/core@7.21.4:
|
||||
resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.2.1
|
||||
'@babel/code-frame': 7.21.4
|
||||
'@babel/generator': 7.21.4
|
||||
'@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4)
|
||||
'@babel/helper-module-transforms': 7.21.2
|
||||
'@babel/helpers': 7.21.0
|
||||
'@babel/parser': 7.21.4
|
||||
'@babel/template': 7.20.7
|
||||
'@babel/traverse': 7.21.4
|
||||
'@babel/types': 7.21.4
|
||||
convert-source-map: 1.9.0
|
||||
debug: 4.3.4
|
||||
gensync: 1.0.0-beta.2
|
||||
json5: 2.2.3
|
||||
semver: 6.3.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
/@testing-library/jest-dom@5.16.5:
|
||||
resolution: {integrity: sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==}
|
||||
engines: {node: '>=8', npm: '>=6', yarn: '>=1'}
|
||||
dependencies:
|
||||
'@adobe/css-tools': 4.2.0
|
||||
'@babel/runtime': 7.21.0
|
||||
'@types/testing-library__jest-dom': 5.14.5
|
||||
aria-query: 5.1.3
|
||||
chalk: 3.0.0
|
||||
css.escape: 1.5.1
|
||||
dom-accessibility-api: 0.5.16
|
||||
lodash: 4.17.21
|
||||
redent: 3.0.0
|
||||
dev: false
|
||||
|
||||
/@testing-library/react@13.4.0(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==}
|
||||
engines: {node: '>=12'}
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@testing-library/dom': 8.20.0
|
||||
'@types/react-dom': 18.2.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@testing-library/user-event@13.5.0(@testing-library/dom@9.2.0):
|
||||
resolution: {integrity: sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg==}
|
||||
engines: {node: '>=10', npm: '>=6'}
|
||||
peerDependencies:
|
||||
'@testing-library/dom': '>=7.21.4'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.21.0
|
||||
'@testing-library/dom': 9.2.0
|
||||
dev: false
|
||||
|
||||
/@types/eslint@8.37.0:
|
||||
resolution: {integrity: sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ==}
|
||||
dependencies:
|
||||
'@types/estree': 1.0.1
|
||||
'@types/json-schema': 7.0.11
|
||||
dev: false
|
||||
|
||||
/react-dom@18.2.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
|
||||
peerDependencies:
|
||||
react: ^18.2.0
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
react: 18.2.0
|
||||
scheduler: 0.23.0
|
||||
dev: false
|
||||
|
||||
/react@18.2.0:
|
||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
loose-envify: 1.4.0
|
||||
dev: false
|
||||
|
||||
/read-cache@1.0.0:
|
||||
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
|
||||
dependencies:
|
||||
pify: 2.3.0
|
||||
dev: false
|
||||
|
||||
/schema-utils@3.1.2:
|
||||
resolution: {integrity: sha512-pvjEHOgWc9OWA/f/DE3ohBWTD6EleVLf7iFUkoSwAxttdBhB9QUebQgxER2kWueOvRJXPHNnyrvvh9eZINB8Eg==}
|
||||
engines: {node: '>= 10.13.0'}
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
ajv: 6.12.6
|
||||
ajv-keywords: 3.5.2(ajv@6.12.6)
|
||||
dev: false
|
||||
|
||||
/web-vitals@2.1.4:
|
||||
resolution: {integrity: sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==}
|
||||
dev: false
|
Loading…
Reference in a new issue