From 2cddf5b4021ec98d64d779cb55a159b19428b53e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 24 Oct 2022 15:56:11 -0400 Subject: [PATCH] port dart cataloger to new generic cataloger pattern Signed-off-by: Alex Goodman --- syft/pkg/cataloger/dart/cataloger.go | 13 ++--- syft/pkg/cataloger/dart/package.go | 56 +++++++++++++++++++ syft/pkg/cataloger/dart/parse_pubspec_lock.go | 39 ++++++------- .../cataloger/dart/parse_pubspec_lock_test.go | 49 +++++++++------- syft/pkg/dart_pub_metadata.go | 30 ---------- syft/pkg/url_test.go | 16 +----- 6 files changed, 107 insertions(+), 96 deletions(-) create mode 100644 syft/pkg/cataloger/dart/package.go diff --git a/syft/pkg/cataloger/dart/cataloger.go b/syft/pkg/cataloger/dart/cataloger.go index 30fd9203ff78..5fbff6f088ad 100644 --- a/syft/pkg/cataloger/dart/cataloger.go +++ b/syft/pkg/cataloger/dart/cataloger.go @@ -1,14 +1,13 @@ package dart import ( - "github.com/anchore/syft/syft/pkg/cataloger/common" + "github.com/anchore/syft/syft/pkg/cataloger/generic" ) -// NewPubspecLockCataloger returns a new Dartlang cataloger object base on pubspec lock files. -func NewPubspecLockCataloger() *common.GenericCataloger { - globParsers := map[string]common.ParserFn{ - "**/pubspec.lock": parsePubspecLock, - } +const catalogerName = "dartlang-lock-cataloger" - return common.NewGenericCataloger(nil, globParsers, "dartlang-lock-cataloger") +// NewPubspecLockCataloger returns a new Dartlang cataloger object base on pubspec lock files. +func NewPubspecLockCataloger() *generic.Cataloger { + return generic.NewCataloger(catalogerName). + WithParserByGlobs(parsePubspecLock, "**/pubspec.lock") } diff --git a/syft/pkg/cataloger/dart/package.go b/syft/pkg/cataloger/dart/package.go new file mode 100644 index 000000000000..1f78045536a5 --- /dev/null +++ b/syft/pkg/cataloger/dart/package.go @@ -0,0 +1,56 @@ +package dart + +import ( + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func newPubspecLockPackage(name string, raw pubspecLockPackage, locations ...source.Location) pkg.Package { + metadata := pkg.DartPubMetadata{ + Name: name, + Version: raw.Version, + HostedURL: raw.getHostedURL(), + VcsURL: raw.getVcsURL(), + } + + p := pkg.Package{ + Name: name, + Version: raw.Version, + Locations: source.NewLocationSet(locations...), + PURL: packageURL(metadata), + Language: pkg.Dart, + Type: pkg.DartPubPkg, + MetadataType: pkg.DartPubMetadataType, + Metadata: metadata, + } + + p.SetID() + + return p +} + +func packageURL(m pkg.DartPubMetadata) string { + var qualifiers packageurl.Qualifiers + + if m.HostedURL != "" { + qualifiers = append(qualifiers, packageurl.Qualifier{ + Key: "hosted_url", + Value: m.HostedURL, + }) + } else if m.VcsURL != "" { // Default to using Hosted if somehow both are provided + qualifiers = append(qualifiers, packageurl.Qualifier{ + Key: "vcs_url", + Value: m.VcsURL, + }) + } + + return packageurl.NewPackageURL( + packageurl.TypePub, + "", + m.Name, + m.Version, + qualifiers, + "", + ).ToString() +} diff --git a/syft/pkg/cataloger/dart/parse_pubspec_lock.go b/syft/pkg/cataloger/dart/parse_pubspec_lock.go index 38dfe87084de..0f12b0a41069 100644 --- a/syft/pkg/cataloger/dart/parse_pubspec_lock.go +++ b/syft/pkg/cataloger/dart/parse_pubspec_lock.go @@ -2,19 +2,19 @@ package dart import ( "fmt" - "io" "net/url" + "sort" "gopkg.in/yaml.v2" "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/common" + "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/source" ) -// integrity check -var _ common.ParserFn = parsePubspecLock +var _ generic.Parser = parsePubspecLock const defaultPubRegistry string = "https://pub.dartlang.org" @@ -38,8 +38,8 @@ type pubspecLockDescription struct { ResolvedRef string `yaml:"resolved-ref" mapstructure:"resolved-ref"` } -func parsePubspecLock(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) { - var packages []*pkg.Package +func parsePubspecLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + var pkgs []pkg.Package dec := yaml.NewDecoder(reader) @@ -48,27 +48,20 @@ func parsePubspecLock(path string, reader io.Reader) ([]*pkg.Package, []artifact return nil, nil, fmt.Errorf("failed to parse pubspec.lock file: %w", err) } - for name, pubPkg := range p.Packages { - packages = append(packages, newPubspecLockPackage(name, pubPkg)) + var names []string + for name := range p.Packages { + names = append(names, name) } - return packages, nil, nil -} + // always ensure there is a stable ordering of packages + sort.Strings(names) -func newPubspecLockPackage(name string, p pubspecLockPackage) *pkg.Package { - return &pkg.Package{ - Name: name, - Version: p.Version, - Language: pkg.Dart, - Type: pkg.DartPubPkg, - MetadataType: pkg.DartPubMetadataType, - Metadata: &pkg.DartPubMetadata{ - Name: name, - Version: p.Version, - HostedURL: p.getHostedURL(), - VcsURL: p.getVcsURL(), - }, + for _, name := range names { + pubPkg := p.Packages[name] + pkgs = append(pkgs, newPubspecLockPackage(name, pubPkg, reader.Location)) } + + return pkgs, nil, nil } func (p *pubspecLockPackage) getVcsURL() string { diff --git a/syft/pkg/cataloger/dart/parse_pubspec_lock_test.go b/syft/pkg/cataloger/dart/parse_pubspec_lock_test.go index f881524c82d6..73fb49bfd026 100644 --- a/syft/pkg/cataloger/dart/parse_pubspec_lock_test.go +++ b/syft/pkg/cataloger/dart/parse_pubspec_lock_test.go @@ -4,20 +4,19 @@ import ( "os" "testing" - "github.com/stretchr/testify/assert" + "github.com/go-test/deep" + "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" ) -func assertPackagesEqual(t *testing.T, actual []*pkg.Package, expected map[string]*pkg.Package) { - assert.Len(t, actual, len(expected)) -} - func TestParsePubspecLock(t *testing.T) { - expected := map[string]*pkg.Package{ - "ale": { + expected := []pkg.Package{ + { Name: "ale", Version: "3.3.0", + PURL: "pkg:pub/ale@3.3.0?hosted_url=pub.hosted.org", Language: pkg.Dart, Type: pkg.DartPubPkg, MetadataType: pkg.DartPubMetadataType, @@ -27,9 +26,10 @@ func TestParsePubspecLock(t *testing.T) { HostedURL: "pub.hosted.org", }, }, - "analyzer": { + { Name: "analyzer", Version: "0.40.7", + PURL: "pkg:pub/analyzer@0.40.7", Language: pkg.Dart, Type: pkg.DartPubPkg, MetadataType: pkg.DartPubMetadataType, @@ -38,9 +38,10 @@ func TestParsePubspecLock(t *testing.T) { Version: "0.40.7", }, }, - "ansicolor": { + { Name: "ansicolor", Version: "1.1.1", + PURL: "pkg:pub/ansicolor@1.1.1", Language: pkg.Dart, Type: pkg.DartPubPkg, MetadataType: pkg.DartPubMetadataType, @@ -49,9 +50,10 @@ func TestParsePubspecLock(t *testing.T) { Version: "1.1.1", }, }, - "archive": { + { Name: "archive", Version: "2.0.13", + PURL: "pkg:pub/archive@2.0.13", Language: pkg.Dart, Type: pkg.DartPubPkg, MetadataType: pkg.DartPubMetadataType, @@ -60,9 +62,10 @@ func TestParsePubspecLock(t *testing.T) { Version: "2.0.13", }, }, - "args": { + { Name: "args", Version: "1.6.0", + PURL: "pkg:pub/args@1.6.0", Language: pkg.Dart, Type: pkg.DartPubPkg, MetadataType: pkg.DartPubMetadataType, @@ -71,29 +74,33 @@ func TestParsePubspecLock(t *testing.T) { Version: "1.6.0", }, }, - "key_binder": { + { Name: "key_binder", Version: "1.11.20", + PURL: "pkg:pub/key_binder@1.11.20?vcs_url=git%40github.com:Workiva/key_binder.git%403f7b3a6350e73c7dcac45301c0e18fbd42af02f7", Language: pkg.Dart, Type: pkg.DartPubPkg, MetadataType: pkg.DartPubMetadataType, Metadata: pkg.DartPubMetadata{ Name: "key_binder", Version: "1.11.20", - VcsURL: "git@github.com:Workiva/key_binder.git#3f7b3a6350e73c7dcac45301c0e18fbd42af02f7", + VcsURL: "git@github.com:Workiva/key_binder.git@3f7b3a6350e73c7dcac45301c0e18fbd42af02f7", }, }, } fixture, err := os.Open("test-fixtures/pubspec.lock") - if err != nil { - t.Fatalf("failed to open fixture: %+v", err) - } + require.NoError(t, err) - actual, _, err := parsePubspecLock(fixture.Name(), fixture) - if err != nil { - t.Fatalf("failed to parse pubspec.lock: %+v", err) - } + // TODO: no relationships are under test yet + actual, _, err := parsePubspecLock(nil, nil, source.LocationReadCloser{ + Location: source.NewLocation(fixture.Name()), + ReadCloser: fixture, + }) + require.NoError(t, err) - assertPackagesEqual(t, actual, expected) + differences := deep.Equal(expected, actual) + if differences != nil { + t.Errorf("returned package list differed from expectation: %+v", differences) + } } diff --git a/syft/pkg/dart_pub_metadata.go b/syft/pkg/dart_pub_metadata.go index 3c800877be27..38884b9e01eb 100644 --- a/syft/pkg/dart_pub_metadata.go +++ b/syft/pkg/dart_pub_metadata.go @@ -1,38 +1,8 @@ package pkg -import ( - "github.com/anchore/packageurl-go" - "github.com/anchore/syft/syft/linux" -) - type DartPubMetadata struct { Name string `mapstructure:"name" json:"name"` Version string `mapstructure:"version" json:"version"` HostedURL string `mapstructure:"hosted_url" json:"hosted_url,omitempty"` VcsURL string `mapstructure:"vcs_url" json:"vcs_url,omitempty"` } - -func (m DartPubMetadata) PackageURL(_ *linux.Release) string { - var qualifiers packageurl.Qualifiers - - if m.HostedURL != "" { - qualifiers = append(qualifiers, packageurl.Qualifier{ - Key: "hosted_url", - Value: m.HostedURL, - }) - } else if m.VcsURL != "" { // Default to using Hosted if somehow both are provided - qualifiers = append(qualifiers, packageurl.Qualifier{ - Key: "vcs_url", - Value: m.VcsURL, - }) - } - - return packageurl.NewPackageURL( - packageurl.TypePub, - "", - m.Name, - m.Version, - qualifiers, - "", - ).ToString() -} diff --git a/syft/pkg/url_test.go b/syft/pkg/url_test.go index 0604e739e267..8b13394c7a42 100644 --- a/syft/pkg/url_test.go +++ b/syft/pkg/url_test.go @@ -35,21 +35,6 @@ func TestPackageURL(t *testing.T) { }, expected: "pkg:golang/go.opencensus.io@v0.23.0", }, - { - name: "pub", - pkg: Package{ - Name: "bad-name", - Version: "0.1.0", - Type: DartPubPkg, - Metadata: DartPubMetadata{ - Name: "name", - Version: "0.2.0", - HostedURL: "pub.hosted.org", - }, - }, - expected: "pkg:pub/name@0.2.0?hosted_url=pub.hosted.org", - }, - { name: "dotnet", pkg: Package{ @@ -225,6 +210,7 @@ func TestPackageURL(t *testing.T) { expectedTypes.Remove(string(AlpmPkg)) expectedTypes.Remove(string(ApkPkg)) expectedTypes.Remove(string(ConanPkg)) + expectedTypes.Remove(string(DartPubPkg)) for _, test := range tests { t.Run(test.name, func(t *testing.T) {