From c1f3df85b1ff906490da616c0ea12b02639f725a Mon Sep 17 00:00:00 2001 From: cpendery Date: Wed, 17 Aug 2022 17:10:58 -0700 Subject: [PATCH] feat: add support for pnpm Signed-off-by: cpendery --- syft/pkg/cataloger/javascript/cataloger.go | 1 + .../cataloger/javascript/parse_pnpm_lock.go | 43 +++++++++++ .../javascript/parse_pnpm_lock_test.go | 52 ++++++++++++++ .../test-fixtures/pnpm/pnpm-lock.yaml | 72 +++++++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 syft/pkg/cataloger/javascript/parse_pnpm_lock.go create mode 100644 syft/pkg/cataloger/javascript/parse_pnpm_lock_test.go create mode 100644 syft/pkg/cataloger/javascript/test-fixtures/pnpm/pnpm-lock.yaml diff --git a/syft/pkg/cataloger/javascript/cataloger.go b/syft/pkg/cataloger/javascript/cataloger.go index 80b4d8077b7..3853c51f890 100644 --- a/syft/pkg/cataloger/javascript/cataloger.go +++ b/syft/pkg/cataloger/javascript/cataloger.go @@ -29,6 +29,7 @@ func NewJavascriptLockCataloger() *common.GenericCataloger { globParsers := map[string]common.ParserFn{ "**/package-lock.json": parsePackageLock, "**/yarn.lock": parseYarnLock, + "**/pnpm-lock.yaml": parsePnpmLock, } return common.NewGenericCataloger(nil, globParsers, "javascript-lock-cataloger", addLicenses) diff --git a/syft/pkg/cataloger/javascript/parse_pnpm_lock.go b/syft/pkg/cataloger/javascript/parse_pnpm_lock.go new file mode 100644 index 00000000000..fc0cb2cb493 --- /dev/null +++ b/syft/pkg/cataloger/javascript/parse_pnpm_lock.go @@ -0,0 +1,43 @@ +package javascript + +import ( + "fmt" + "io" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/common" + "gopkg.in/yaml.v3" +) + +// integrity check +var _ common.ParserFn = parsePnpmLock + +type pnpmLockYaml struct { + Dependencies map[string]string `json:"dependencies"` +} + +func parsePnpmLock(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) { + bytes, err := io.ReadAll(reader) + if err != nil { + return nil, nil, fmt.Errorf("failed to load pnpm-lock.yaml file: %w", err) + } + + var pkgs []*pkg.Package + var lockFile pnpmLockYaml + + if err := yaml.Unmarshal(bytes, &lockFile); err != nil { + return nil, nil, fmt.Errorf("failed to parse pnpm-lock.yaml file: %w", err) + } + + for name, version := range lockFile.Dependencies { + pkgs = append(pkgs, &pkg.Package{ + Name: name, + Version: version, + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + }) + } + + return pkgs, nil, nil +} diff --git a/syft/pkg/cataloger/javascript/parse_pnpm_lock_test.go b/syft/pkg/cataloger/javascript/parse_pnpm_lock_test.go new file mode 100644 index 00000000000..cf5c7dc91a3 --- /dev/null +++ b/syft/pkg/cataloger/javascript/parse_pnpm_lock_test.go @@ -0,0 +1,52 @@ +package javascript + +import ( + "os" + "testing" + + "github.com/anchore/syft/syft/pkg" + "github.com/go-test/deep" +) + +func fixtureP(str string) *string { + return &str +} + +func TestParsePnpmLock(t *testing.T) { + expected := []*pkg.Package{ + { + Name: "nanoid", + Version: "3.3.4", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + }, + { + Name: "picocolors", + Version: "1.0.0", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + }, + { + Name: "source-map-js", + Version: "1.0.2", + Language: pkg.JavaScript, + Type: pkg.NpmPkg, + }, + } + + fixture, err := os.Open("test-fixtures/pnpm/pnpm-lock.yaml") + if err != nil { + t.Fatalf("failed to open fixture: %+v", err) + } + + // TODO: no relationships are under test yet + actual, _, err := parsePnpmLock(fixture.Name(), fixture) + if err != nil { + t.Error(err) + } + + differences := deep.Equal(expected, actual) + if differences != nil { + t.Errorf("returned package list differed from expectation: %+v", differences) + } +} diff --git a/syft/pkg/cataloger/javascript/test-fixtures/pnpm/pnpm-lock.yaml b/syft/pkg/cataloger/javascript/test-fixtures/pnpm/pnpm-lock.yaml new file mode 100644 index 00000000000..70e3575188e --- /dev/null +++ b/syft/pkg/cataloger/javascript/test-fixtures/pnpm/pnpm-lock.yaml @@ -0,0 +1,72 @@ +lockfileVersion: 5.4 + +specifiers: + '@logux/eslint-config': ^47.2.0 + '@size-limit/preset-small-lib': ^8.0.0 + '@types/fs-extra': ^9.0.13 + '@types/node': ^18.6.4 + '@typescript-eslint/eslint-plugin': ^5.32.0 + '@typescript-eslint/parser': ^5.32.0 + c8: ^7.12.0 + check-dts: ^0.6.7 + clean-publish: ^4.0.1 + concat-with-sourcemaps: ^1.1.0 + eslint: ^8.21.0 + eslint-config-standard: ^17.0.0 + eslint-plugin-import: ^2.26.0 + eslint-plugin-n: ^15.2.4 + eslint-plugin-prefer-let: ^3.0.1 + eslint-plugin-promise: ^6.0.0 + fs-extra: ^10.1.0 + nanodelay: ^1.0.8 + nanoid: ^3.3.4 + nanospy: ^0.5.0 + picocolors: ^1.0.0 + postcss-parser-tests: ^8.5.1 + simple-git-hooks: ^2.8.0 + size-limit: ^8.0.0 + source-map-js: ^1.0.2 + strip-ansi: ^6.0.1 + ts-node: ^10.9.1 + typescript: ^4.7.4 + uvu: ^0.5.6 + +dependencies: + nanoid: 3.3.4 + picocolors: 1.0.0 + source-map-js: 1.0.2 + +devDependencies: + '@logux/eslint-config': 47.2.0_7hz3xvmviof7onfgk6hpedqcom + '@size-limit/preset-small-lib': 8.0.0_size-limit@8.0.0 + '@types/fs-extra': 9.0.13 + '@types/node': 18.6.4 + '@typescript-eslint/eslint-plugin': 5.32.0_iosr3hrei2tubxveewluhu5lhy + '@typescript-eslint/parser': 5.32.0_qugx7qdu5zevzvxaiqyxfiwquq + c8: 7.12.0 + check-dts: 0.6.7_typescript@4.7.4 + clean-publish: 4.0.1 + concat-with-sourcemaps: 1.1.0 + eslint: 8.21.0 + eslint-config-standard: 17.0.0_dfwa53o44x4e5xhsfv5mvfhk5a + eslint-plugin-import: 2.26.0_wuikv5nqgdfyng42xxm7lklfmi + eslint-plugin-n: 15.2.4_eslint@8.21.0 + eslint-plugin-prefer-let: 3.0.1 + eslint-plugin-promise: 6.0.0_eslint@8.21.0 + fs-extra: 10.1.0 + nanodelay: 1.0.8 + nanospy: 0.5.0 + postcss-parser-tests: 8.5.1 + simple-git-hooks: 2.8.0 + size-limit: 8.0.0 + strip-ansi: 6.0.1 + ts-node: 10.9.1_hn66opzbaneygq52jmwjxha6su + typescript: 4.7.4 + uvu: 0.5.6 + +packages: + /@bcoe/v8-coverage/0.2.3: + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + dev: true + + # removed other packages