Update swift cataloger to generic cataloger (#1324)

* port swift cataloger to new generic cataloger pattern

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

* add cocopods metadata to json schema defs

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

* update json test fixture with latest schema version

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-11-04 13:51:59 -04:00 committed by GitHub
parent f319713821
commit 5ed002e1a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1791 additions and 186 deletions

View File

@ -6,5 +6,5 @@ const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "5.0.0"
JSONSchemaVersion = "5.0.1"
)

View File

@ -46,6 +46,7 @@ type artifactMetadataContainer struct {
ConanLock pkg.ConanLockMetadata
KbPackage pkg.KbPackageMetadata
Hackage pkg.HackageMetadata
SwiftCocopods pkg.CocoapodsMetadata
}
func main() {

File diff suppressed because it is too large Load Diff

View File

@ -89,7 +89,7 @@
}
},
"schema": {
"version": "5.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.0.json"
"version": "5.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.1.json"
}
}

View File

@ -185,7 +185,7 @@
}
},
"schema": {
"version": "5.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.0.json"
"version": "5.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.1.json"
}
}

View File

@ -112,7 +112,7 @@
}
},
"schema": {
"version": "5.0.0",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.0.json"
"version": "5.0.1",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-5.0.1.json"
}
}

View File

@ -4,14 +4,11 @@ Package swift provides a concrete Cataloger implementation for Podfile.lock file
package swift
import (
"github.com/anchore/syft/syft/pkg/cataloger/common"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
// NewCocoapodsCataloger returns a new Swift Cocoapods lock file cataloger object.
func NewCocoapodsCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/Podfile.lock": parsePodfileLock,
}
return common.NewGenericCataloger(nil, globParsers, "cocoapods-cataloger")
func NewCocoapodsCataloger() *generic.Cataloger {
return generic.NewCataloger("cocoapods-cataloger").
WithParserByGlobs(parsePodfileLock, "**/Podfile.lock")
}

View File

@ -0,0 +1,39 @@
package swift
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
)
func newPackage(name, version, hash string, locations ...source.Location) pkg.Package {
p := pkg.Package{
Name: name,
Version: version,
PURL: packageURL(name, version),
Locations: source.NewLocationSet(locations...),
Type: pkg.CocoapodsPkg,
Language: pkg.Swift,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Checksum: hash,
},
}
p.SetID()
return p
}
func packageURL(name, version string) string {
var qualifiers packageurl.Qualifiers
return packageurl.NewPackageURL(
packageurl.TypeCocoapods,
"",
name,
version,
qualifiers,
"",
).ToString()
}

View File

@ -0,0 +1,33 @@
package swift
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_packageURL(t *testing.T) {
type args struct {
name string
version string
}
tests := []struct {
name string
args args
want string
}{
{
name: "go case",
args: args{
name: "name",
version: "v0.1.0",
},
want: "pkg:cocoapods/name@v0.1.0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, packageURL(tt.args.name, tt.args.version))
})
}
}

View File

@ -9,36 +9,34 @@ import (
"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 = parsePodfileLock
var _ generic.Parser = parsePodfileLock
type podfileLock struct {
Pods []interface{} `yaml:"PODS"`
Dependencies []string `yaml:"DEPENDENCIES"`
SpecRepos map[string][]string `yaml:"SPEC REPOS"`
SpecChecksums map[string]string `yaml:"SPEC CHECKSUMS"`
PodfileChecksum string `yaml:"PODFILE CHECKSUM"`
Cocopods string `yaml:"COCOAPODS"`
}
// parsePodfileLock is a parser function for Podfile.lock contents, returning all cocoapods pods discovered.
func parsePodfileLock(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
func parsePodfileLock(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
bytes, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("unable to read file: %w", err)
}
var podfile map[string]interface{}
var podfile podfileLock
if err = yaml.Unmarshal(bytes, &podfile); err != nil {
return nil, nil, fmt.Errorf("unable to parse yaml: %w", err)
}
c, exists := podfile["SPEC CHECKSUMS"]
if !exists {
return nil, nil, fmt.Errorf("malformed podfile.lock: missing checksums")
}
checksums := c.(map[string]interface{})
p, exists := podfile["PODS"]
if !exists {
return nil, nil, fmt.Errorf("malformed podfile.lock: missing checksums")
}
pods := p.([]interface{})
pkgs := []*pkg.Package{}
for _, podInterface := range pods {
var pkgs []pkg.Package
for _, podInterface := range podfile.Pods {
var podBlob string
switch v := podInterface.(type) {
case map[string]interface{}:
@ -54,22 +52,14 @@ func parsePodfileLock(_ string, reader io.Reader) ([]*pkg.Package, []artifact.Re
podName := splits[0]
podVersion := strings.TrimSuffix(strings.TrimPrefix(splits[1], "("), ")")
podRootPkg := strings.Split(podName, "/")[0]
pkgHash, exists := checksums[podRootPkg]
var pkgHash string
pkgHash, exists := podfile.SpecChecksums[podRootPkg]
if !exists {
return nil, nil, fmt.Errorf("malformed podfile.lock: incomplete checksums")
}
pkgs = append(pkgs, &pkg.Package{
Name: podName,
Version: podVersion,
Type: pkg.CocoapodsPkg,
Language: pkg.Swift,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: podName,
Version: podVersion,
PkgHash: pkgHash.(string),
},
})
pkgs = append(pkgs, newPackage(podName, podVersion, pkgHash, reader.Location))
}
return pkgs, nil, nil

View File

@ -1,307 +1,298 @@
package swift
import (
"os"
"testing"
"github.com/go-test/deep"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/anchore/syft/syft/source"
)
func TestParsePodfileLock(t *testing.T) {
expected := []*pkg.Package{
fixture := "test-fixtures/Podfile.lock"
locations := source.NewLocationSet(source.NewLocation(fixture))
expectedPkgs := []pkg.Package{
{
Name: "GlossButtonNode",
Version: "3.1.2",
PURL: "pkg:cocoapods/GlossButtonNode@3.1.2",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "GlossButtonNode",
Version: "3.1.2",
PkgHash: "4ea1197a744f2fb5fb875fe31caf17ded4762e8f",
Checksum: "4ea1197a744f2fb5fb875fe31caf17ded4762e8f",
},
},
{
Name: "PINCache",
Version: "3.0.3",
PURL: "pkg:cocoapods/PINCache@3.0.3",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINCache",
Version: "3.0.3",
PkgHash: "7a8fc1a691173d21dbddbf86cd515de6efa55086",
Checksum: "7a8fc1a691173d21dbddbf86cd515de6efa55086",
},
},
{
Name: "PINCache/Arc-exception-safe",
Version: "3.0.3",
PURL: "pkg:cocoapods/PINCache/Arc-exception-safe@3.0.3",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINCache/Arc-exception-safe",
Version: "3.0.3",
PkgHash: "7a8fc1a691173d21dbddbf86cd515de6efa55086",
Checksum: "7a8fc1a691173d21dbddbf86cd515de6efa55086",
},
},
{
Name: "PINCache/Core",
Version: "3.0.3",
PURL: "pkg:cocoapods/PINCache/Core@3.0.3",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINCache/Core",
Version: "3.0.3",
PkgHash: "7a8fc1a691173d21dbddbf86cd515de6efa55086",
Checksum: "7a8fc1a691173d21dbddbf86cd515de6efa55086",
},
},
{
Name: "PINOperation",
Version: "1.2.1",
PURL: "pkg:cocoapods/PINOperation@1.2.1",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINOperation",
Version: "1.2.1",
PkgHash: "00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20",
Checksum: "00c935935f1e8cf0d1e2d6b542e75b88fc3e5e20",
},
},
{
Name: "PINRemoteImage/Core",
Version: "3.0.3",
PURL: "pkg:cocoapods/PINRemoteImage/Core@3.0.3",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINRemoteImage/Core",
Version: "3.0.3",
PkgHash: "f1295b29f8c5e640e25335a1b2bd9d805171bd01",
Checksum: "f1295b29f8c5e640e25335a1b2bd9d805171bd01",
},
},
{
Name: "PINRemoteImage/iOS",
Version: "3.0.3",
PURL: "pkg:cocoapods/PINRemoteImage/iOS@3.0.3",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINRemoteImage/iOS",
Version: "3.0.3",
PkgHash: "f1295b29f8c5e640e25335a1b2bd9d805171bd01",
Checksum: "f1295b29f8c5e640e25335a1b2bd9d805171bd01",
},
},
{
Name: "PINRemoteImage/PINCache",
Version: "3.0.3",
PURL: "pkg:cocoapods/PINRemoteImage/PINCache@3.0.3",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "PINRemoteImage/PINCache",
Version: "3.0.3",
PkgHash: "f1295b29f8c5e640e25335a1b2bd9d805171bd01",
Checksum: "f1295b29f8c5e640e25335a1b2bd9d805171bd01",
},
},
{
Name: "Reveal-SDK",
Version: "33",
PURL: "pkg:cocoapods/Reveal-SDK@33",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Reveal-SDK",
Version: "33",
PkgHash: "effba1c940b8337195563c425a6b5862ec875caa",
Checksum: "effba1c940b8337195563c425a6b5862ec875caa",
},
},
{
Name: "SwiftGen",
Version: "6.5.1",
PURL: "pkg:cocoapods/SwiftGen@6.5.1",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "SwiftGen",
Version: "6.5.1",
PkgHash: "a6d22010845f08fe18fbdf3a07a8e380fd22e0ea",
Checksum: "a6d22010845f08fe18fbdf3a07a8e380fd22e0ea",
},
},
{
Name: "Texture",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "Texture/AssetsLibrary",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture/AssetsLibrary@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture/AssetsLibrary",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "Texture/Core",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture/Core@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture/Core",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "Texture/MapKit",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture/MapKit@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture/MapKit",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "Texture/Photos",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture/Photos@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture/Photos",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "Texture/PINRemoteImage",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture/PINRemoteImage@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture/PINRemoteImage",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "Texture/Video",
Version: "3.1.0",
PURL: "pkg:cocoapods/Texture/Video@3.1.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "Texture/Video",
Version: "3.1.0",
PkgHash: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
Checksum: "2e8ab2519452515f7f5a520f5a8f7e0a413abfa3",
},
},
{
Name: "TextureSwiftSupport",
Version: "3.13.0",
PURL: "pkg:cocoapods/TextureSwiftSupport@3.13.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "TextureSwiftSupport",
Version: "3.13.0",
PkgHash: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
Checksum: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
},
},
{
Name: "TextureSwiftSupport/Components",
Version: "3.13.0",
PURL: "pkg:cocoapods/TextureSwiftSupport/Components@3.13.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "TextureSwiftSupport/Components",
Version: "3.13.0",
PkgHash: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
Checksum: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
},
},
{
Name: "TextureSwiftSupport/Experiments",
Version: "3.13.0",
PURL: "pkg:cocoapods/TextureSwiftSupport/Experiments@3.13.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "TextureSwiftSupport/Experiments",
Version: "3.13.0",
PkgHash: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
Checksum: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
},
},
{
Name: "TextureSwiftSupport/Extensions",
Version: "3.13.0",
PURL: "pkg:cocoapods/TextureSwiftSupport/Extensions@3.13.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "TextureSwiftSupport/Extensions",
Version: "3.13.0",
PkgHash: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
Checksum: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
},
},
{
Name: "TextureSwiftSupport/LayoutSpecBuilders",
Version: "3.13.0",
PURL: "pkg:cocoapods/TextureSwiftSupport/LayoutSpecBuilders@3.13.0",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "TextureSwiftSupport/LayoutSpecBuilders",
Version: "3.13.0",
PkgHash: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
Checksum: "c515c7927fab92d0d9485f49b885b8c5de34fbfb",
},
},
{
Name: "TinyConstraints",
Version: "4.0.2",
PURL: "pkg:cocoapods/TinyConstraints@4.0.2",
Locations: locations,
Language: pkg.Swift,
Type: pkg.CocoapodsPkg,
MetadataType: pkg.CocoapodsMetadataType,
Metadata: pkg.CocoapodsMetadata{
Name: "TinyConstraints",
Version: "4.0.2",
PkgHash: "7b7ccc0c485bb3bb47082138ff28bc33cd49897f",
Checksum: "7b7ccc0c485bb3bb47082138ff28bc33cd49897f",
},
},
}
fixture, err := os.Open("test-fixtures/Podfile.lock")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
// TODO: no relationships are under test yet
actual, _, err := parsePodfileLock(fixture.Name(), fixture)
if err != nil {
t.Error(err)
}
var expectedRelationships []artifact.Relationship
differences := deep.Equal(expected, actual)
if differences != nil {
t.Errorf("returned package list differed from expectation: %+v", differences)
}
pkgtest.TestFileParser(t, fixture, parsePodfileLock, expectedPkgs, expectedRelationships)
}

View File

@ -1,27 +1,5 @@
package pkg
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
)
var _ urlIdentifier = (*CocoapodsMetadata)(nil)
type CocoapodsMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
PkgHash string `mapstructure:"pkgHash" json:"pkgHash"`
}
func (m CocoapodsMetadata) PackageURL(_ *linux.Release) string {
var qualifiers packageurl.Qualifiers
return packageurl.NewPackageURL(
packageurl.TypeCocoapods,
"",
m.Name,
m.Version,
qualifiers,
"",
).ToString()
Checksum string `mapstructure:"checksum" json:"checksum"`
}

View File

@ -45,20 +45,6 @@ func TestPackageURL(t *testing.T) {
expected: "pkg:maven/g.id/a@v",
},
{
name: "cocoapods",
pkg: Package{
Name: "GlossButtonNode",
Version: "3.1.2",
Language: Swift,
Type: CocoapodsPkg,
Metadata: CocoapodsMetadata{
Name: "GlossButtonNode",
Version: "3.1.2",
},
},
expected: "pkg:cocoapods/GlossButtonNode@3.1.2",
},
}
var pkgTypes []string
@ -85,6 +71,7 @@ func TestPackageURL(t *testing.T) {
expectedTypes.Remove(string(GemPkg))
expectedTypes.Remove(string(NpmPkg))
expectedTypes.Remove(string(RustPkg))
expectedTypes.Remove(string(CocoapodsPkg))
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {