metadata decoding should be optional (#1154)

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-08-10 12:20:53 -04:00 committed by GitHub
parent 1344889766
commit 2693a8c19a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 148 additions and 15 deletions

View file

@ -2,6 +2,7 @@ package model
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"github.com/anchore/syft/syft/source" "github.com/anchore/syft/syft/source"
@ -10,6 +11,8 @@ import (
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
) )
var errUnknownMetadataType = errors.New("unknown metadata type")
// Package represents a pkg.Package object specialized for JSON marshaling and unmarshalling. // Package represents a pkg.Package object specialized for JSON marshaling and unmarshalling.
type Package struct { type Package struct {
PackageBasicData PackageBasicData
@ -60,13 +63,22 @@ func (p *Package) UnmarshalJSON(b []byte) error {
return err return err
} }
return unpackMetadata(p, unpacker) err := unpackMetadata(p, unpacker)
if errors.Is(err, errUnknownMetadataType) {
log.Warnf("unknown package metadata type=%q for packageID=%q", p.MetadataType, p.ID)
return nil
}
return err
} }
// nolint:funlen,gocognit,gocyclo // nolint:funlen,gocognit,gocyclo
func unpackMetadata(p *Package, unpacker packageMetadataUnpacker) error { func unpackMetadata(p *Package, unpacker packageMetadataUnpacker) error {
p.MetadataType = unpacker.MetadataType p.MetadataType = unpacker.MetadataType
switch p.MetadataType { switch p.MetadataType {
case "":
// there is no metadata, skip
break
case pkg.AlpmMetadataType: case pkg.AlpmMetadataType:
var payload pkg.AlpmMetadata var payload pkg.AlpmMetadata
if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil { if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil {
@ -176,8 +188,7 @@ func unpackMetadata(p *Package, unpacker packageMetadataUnpacker) error {
} }
p.Metadata = payload p.Metadata = payload
default: default:
log.Warnf("unknown package metadata type=%q for packageID=%q", p.MetadataType, p.ID) return errUnknownMetadataType
} }
return nil return nil
} }

View file

@ -1,21 +1,95 @@
package model package model
import ( import (
"testing" "encoding/json"
"github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
) )
func TestUnmarshalPackageGolang(t *testing.T) { func TestUnmarshalPackageGolang(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
p *Package
packageData []byte packageData []byte
assert func(*Package)
}{ }{
{ {
name: "Package.UnmarshalJSON unmarshals PackageBasicData", name: "unmarshal package metadata",
p: &Package{}, packageData: []byte(`{
"id": "8b594519bc23da50",
"name": "gopkg.in/square/go-jose.v2",
"version": "v2.6.0",
"type": "go-module",
"foundBy": "go-module-binary-cataloger",
"locations": [
{
"path": "/Users/hal/go/bin/syft"
}
],
"licenses": [],
"language": "go",
"cpes": [],
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0",
"metadataType": "GolangBinMetadata",
"metadata": {
"goCompiledVersion": "go1.18",
"architecture": "amd64",
"h1Digest": "h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI="
}
}`),
assert: func(p *Package) {
assert.NotNil(t, p.Metadata)
golangMetadata := p.Metadata.(pkg.GolangBinMetadata)
assert.NotEmpty(t, golangMetadata)
assert.Equal(t, "go1.18", golangMetadata.GoCompiledVersion)
},
},
{
name: "can handle package without metadata",
packageData: []byte(`{
"id": "8b594519bc23da50",
"name": "gopkg.in/square/go-jose.v2",
"version": "v2.6.0",
"type": "go-module",
"foundBy": "go-mod-cataloger",
"locations": [
{
"path": "/Users/hal/go/bin/syft"
}
],
"licenses": [],
"language": "go",
"cpes": [],
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0"
}`),
assert: func(p *Package) {
assert.Empty(t, p.MetadataType)
assert.Empty(t, p.Metadata)
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
p := &Package{}
err := p.UnmarshalJSON(test.packageData)
require.NoError(t, err)
test.assert(p)
})
}
}
func Test_unpackMetadata(t *testing.T) {
tests := []struct {
name string
packageData []byte
metadataType pkg.MetadataType
wantErr require.ErrorAssertionFunc
}{
{
name: "unmarshal package metadata",
metadataType: pkg.GolangBinMetadataType,
packageData: []byte(`{ packageData: []byte(`{
"id": "8b594519bc23da50", "id": "8b594519bc23da50",
"name": "gopkg.in/square/go-jose.v2", "name": "gopkg.in/square/go-jose.v2",
@ -39,19 +113,67 @@ func TestUnmarshalPackageGolang(t *testing.T) {
} }
}`), }`),
}, },
{
name: "can handle package without metadata",
metadataType: "",
packageData: []byte(`{
"id": "8b594519bc23da50",
"name": "gopkg.in/square/go-jose.v2",
"version": "v2.6.0",
"type": "go-module",
"foundBy": "go-mod-cataloger",
"locations": [
{
"path": "/Users/hal/go/bin/syft"
}
],
"licenses": [],
"language": "go",
"cpes": [],
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0"
}`),
},
{
name: "bad metadata type is an error",
metadataType: "BOGOSITY",
wantErr: require.Error,
packageData: []byte(`{
"id": "8b594519bc23da50",
"name": "gopkg.in/square/go-jose.v2",
"version": "v2.6.0",
"type": "go-module",
"foundBy": "go-mod-cataloger",
"locations": [
{
"path": "/Users/hal/go/bin/syft"
}
],
"licenses": [],
"language": "go",
"cpes": [],
"purl": "pkg:golang/gopkg.in/square/go-jose.v2@v2.6.0",
"metadataType": "BOGOSITY"
}`),
},
} }
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
err := test.p.UnmarshalJSON(test.packageData) if test.wantErr == nil {
if err != nil { test.wantErr = require.NoError
t.Fatalf("could not unmarshal packageData: %v", err)
} }
p := &Package{}
assert.NotNil(t, test.p.Metadata) var basic PackageBasicData
golangMetadata := test.p.Metadata.(pkg.GolangBinMetadata) require.NoError(t, json.Unmarshal(test.packageData, &basic))
assert.NotEmpty(t, golangMetadata) p.PackageBasicData = basic
assert.Equal(t, "go1.18", golangMetadata.GoCompiledVersion)
var unpacker packageMetadataUnpacker
require.NoError(t, json.Unmarshal(test.packageData, &unpacker))
err := unpackMetadata(p, unpacker)
assert.Equal(t, test.metadataType, p.MetadataType)
test.wantErr(t, err)
}) })
} }
} }