port dart cataloger to new generic cataloger pattern (#1285)

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
Alex Goodman 2022-10-24 16:37:42 -04:00 committed by GitHub
parent eb8ebd9ffc
commit fbdde6d4f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 107 additions and 96 deletions

View File

@ -1,14 +1,13 @@
package dart package dart
import ( 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. const catalogerName = "dartlang-lock-cataloger"
func NewPubspecLockCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/pubspec.lock": parsePubspecLock,
}
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")
} }

View File

@ -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()
}

View File

@ -2,19 +2,19 @@ package dart
import ( import (
"fmt" "fmt"
"io"
"net/url" "net/url"
"sort"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/anchore/syft/internal/log" "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/common" "github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
) )
// integrity check var _ generic.Parser = parsePubspecLock
var _ common.ParserFn = parsePubspecLock
const defaultPubRegistry string = "https://pub.dartlang.org" const defaultPubRegistry string = "https://pub.dartlang.org"
@ -38,8 +38,8 @@ type pubspecLockDescription struct {
ResolvedRef string `yaml:"resolved-ref" mapstructure:"resolved-ref"` ResolvedRef string `yaml:"resolved-ref" mapstructure:"resolved-ref"`
} }
func parsePubspecLock(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) { func parsePubspecLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var packages []*pkg.Package var pkgs []pkg.Package
dec := yaml.NewDecoder(reader) 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) return nil, nil, fmt.Errorf("failed to parse pubspec.lock file: %w", err)
} }
for name, pubPkg := range p.Packages { var names []string
packages = append(packages, newPubspecLockPackage(name, pubPkg)) 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 { for _, name := range names {
return &pkg.Package{ pubPkg := p.Packages[name]
Name: name, pkgs = append(pkgs, newPubspecLockPackage(name, pubPkg, reader.Location))
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(),
},
} }
return pkgs, nil, nil
} }
func (p *pubspecLockPackage) getVcsURL() string { func (p *pubspecLockPackage) getVcsURL() string {

View File

@ -4,20 +4,19 @@ import (
"os" "os"
"testing" "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/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) { func TestParsePubspecLock(t *testing.T) {
expected := map[string]*pkg.Package{ expected := []pkg.Package{
"ale": { {
Name: "ale", Name: "ale",
Version: "3.3.0", Version: "3.3.0",
PURL: "pkg:pub/ale@3.3.0?hosted_url=pub.hosted.org",
Language: pkg.Dart, Language: pkg.Dart,
Type: pkg.DartPubPkg, Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType, MetadataType: pkg.DartPubMetadataType,
@ -27,9 +26,10 @@ func TestParsePubspecLock(t *testing.T) {
HostedURL: "pub.hosted.org", HostedURL: "pub.hosted.org",
}, },
}, },
"analyzer": { {
Name: "analyzer", Name: "analyzer",
Version: "0.40.7", Version: "0.40.7",
PURL: "pkg:pub/analyzer@0.40.7",
Language: pkg.Dart, Language: pkg.Dart,
Type: pkg.DartPubPkg, Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType, MetadataType: pkg.DartPubMetadataType,
@ -38,9 +38,10 @@ func TestParsePubspecLock(t *testing.T) {
Version: "0.40.7", Version: "0.40.7",
}, },
}, },
"ansicolor": { {
Name: "ansicolor", Name: "ansicolor",
Version: "1.1.1", Version: "1.1.1",
PURL: "pkg:pub/ansicolor@1.1.1",
Language: pkg.Dart, Language: pkg.Dart,
Type: pkg.DartPubPkg, Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType, MetadataType: pkg.DartPubMetadataType,
@ -49,9 +50,10 @@ func TestParsePubspecLock(t *testing.T) {
Version: "1.1.1", Version: "1.1.1",
}, },
}, },
"archive": { {
Name: "archive", Name: "archive",
Version: "2.0.13", Version: "2.0.13",
PURL: "pkg:pub/archive@2.0.13",
Language: pkg.Dart, Language: pkg.Dart,
Type: pkg.DartPubPkg, Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType, MetadataType: pkg.DartPubMetadataType,
@ -60,9 +62,10 @@ func TestParsePubspecLock(t *testing.T) {
Version: "2.0.13", Version: "2.0.13",
}, },
}, },
"args": { {
Name: "args", Name: "args",
Version: "1.6.0", Version: "1.6.0",
PURL: "pkg:pub/args@1.6.0",
Language: pkg.Dart, Language: pkg.Dart,
Type: pkg.DartPubPkg, Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType, MetadataType: pkg.DartPubMetadataType,
@ -71,29 +74,33 @@ func TestParsePubspecLock(t *testing.T) {
Version: "1.6.0", Version: "1.6.0",
}, },
}, },
"key_binder": { {
Name: "key_binder", Name: "key_binder",
Version: "1.11.20", 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, Language: pkg.Dart,
Type: pkg.DartPubPkg, Type: pkg.DartPubPkg,
MetadataType: pkg.DartPubMetadataType, MetadataType: pkg.DartPubMetadataType,
Metadata: pkg.DartPubMetadata{ Metadata: pkg.DartPubMetadata{
Name: "key_binder", Name: "key_binder",
Version: "1.11.20", 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") fixture, err := os.Open("test-fixtures/pubspec.lock")
if err != nil { require.NoError(t, err)
t.Fatalf("failed to open fixture: %+v", err)
}
actual, _, err := parsePubspecLock(fixture.Name(), fixture) // TODO: no relationships are under test yet
if err != nil { actual, _, err := parsePubspecLock(nil, nil, source.LocationReadCloser{
t.Fatalf("failed to parse pubspec.lock: %+v", err) 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)
}
} }

View File

@ -1,38 +1,8 @@
package pkg package pkg
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
)
type DartPubMetadata struct { type DartPubMetadata struct {
Name string `mapstructure:"name" json:"name"` Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"` Version string `mapstructure:"version" json:"version"`
HostedURL string `mapstructure:"hosted_url" json:"hosted_url,omitempty"` HostedURL string `mapstructure:"hosted_url" json:"hosted_url,omitempty"`
VcsURL string `mapstructure:"vcs_url" json:"vcs_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()
}

View File

@ -35,21 +35,6 @@ func TestPackageURL(t *testing.T) {
}, },
expected: "pkg:golang/go.opencensus.io@v0.23.0", 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", name: "dotnet",
pkg: Package{ pkg: Package{
@ -225,6 +210,7 @@ func TestPackageURL(t *testing.T) {
expectedTypes.Remove(string(AlpmPkg)) expectedTypes.Remove(string(AlpmPkg))
expectedTypes.Remove(string(ApkPkg)) expectedTypes.Remove(string(ApkPkg))
expectedTypes.Remove(string(ConanPkg)) expectedTypes.Remove(string(ConanPkg))
expectedTypes.Remove(string(DartPubPkg))
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {