mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
fix: duplicate packages, support pnpm lockfile v6 (#1778)
This commit is contained in:
parent
798af57853
commit
a3c5550217
@ -3,10 +3,13 @@ package javascript
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
@ -17,8 +20,9 @@ import (
|
|||||||
var _ generic.Parser = parsePnpmLock
|
var _ generic.Parser = parsePnpmLock
|
||||||
|
|
||||||
type pnpmLockYaml struct {
|
type pnpmLockYaml struct {
|
||||||
Dependencies map[string]string `json:"dependencies"`
|
Version string `json:"lockfileVersion" yaml:"lockfileVersion"`
|
||||||
Packages map[string]interface{} `json:"packages"`
|
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) {
|
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)
|
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))
|
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
|
// parse packages from packages section of pnpm-lock.yaml
|
||||||
for nameVersion := range lockFile.Packages {
|
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
|
// last element in split array is version
|
||||||
version := nameVersionSplit[len(nameVersionSplit)-1]
|
version := nameVersionSplit[len(nameVersionSplit)-1]
|
||||||
|
|
||||||
// construct name from all array items other than last item (version)
|
// 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))
|
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
|
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)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
127
syft/pkg/cataloger/javascript/test-fixtures/pnpm-v6/pnpm-lock.yaml
generated
Normal file
127
syft/pkg/cataloger/javascript/test-fixtures/pnpm-v6/pnpm-lock.yaml
generated
Normal file
@ -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…
x
Reference in New Issue
Block a user