mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
fix apk decode for older data shapes (#1341)
Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
09bf5b062c
commit
1ae577a035
@ -1,8 +1,13 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@ -35,6 +40,58 @@ type ApkMetadata struct {
|
||||
Files []ApkFileRecord `json:"files"`
|
||||
}
|
||||
|
||||
type spaceDelimitedStringSlice []string
|
||||
|
||||
func (m *ApkMetadata) UnmarshalJSON(data []byte) error {
|
||||
var fields []reflect.StructField
|
||||
t := reflect.TypeOf(ApkMetadata{})
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
f := t.Field(i)
|
||||
if f.Name == "Dependencies" {
|
||||
f.Type = reflect.TypeOf(spaceDelimitedStringSlice{})
|
||||
}
|
||||
fields = append(fields, f)
|
||||
}
|
||||
apkMetadata := reflect.StructOf(fields)
|
||||
inst := reflect.New(apkMetadata)
|
||||
if err := json.Unmarshal(data, inst.Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mapstructure.Decode(inst.Elem().Interface(), m)
|
||||
}
|
||||
|
||||
func (a *spaceDelimitedStringSlice) UnmarshalJSON(data []byte) error {
|
||||
var jsonObj interface{}
|
||||
|
||||
if err := json.Unmarshal(data, &jsonObj); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch obj := jsonObj.(type) {
|
||||
case string:
|
||||
if obj == "" {
|
||||
*a = nil
|
||||
} else {
|
||||
*a = strings.Split(obj, " ")
|
||||
}
|
||||
return nil
|
||||
case []interface{}:
|
||||
s := make([]string, 0, len(obj))
|
||||
for _, v := range obj {
|
||||
value, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid type for string array element: %T", v)
|
||||
}
|
||||
s = append(s, value)
|
||||
}
|
||||
*a = s
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid type for string array: %T", obj)
|
||||
}
|
||||
}
|
||||
|
||||
// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records).
|
||||
type ApkFileRecord struct {
|
||||
Path string `json:"path"`
|
||||
|
||||
156
syft/pkg/apk_metadata_test.go
Normal file
156
syft/pkg/apk_metadata_test.go
Normal file
@ -0,0 +1,156 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestApkMetadata_UnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want ApkMetadata
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
input: "{}",
|
||||
want: ApkMetadata{},
|
||||
},
|
||||
{
|
||||
name: "string array dependencies",
|
||||
input: `{
|
||||
"package": "scanelf",
|
||||
"originPackage": "pax-utils",
|
||||
"maintainer": "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
"version": "1.3.4-r0",
|
||||
"license": "GPL-2.0-only",
|
||||
"architecture": "x86_64",
|
||||
"url": "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities",
|
||||
"description": "Scan ELF binaries for stuff",
|
||||
"size": 36745,
|
||||
"installedSize": 94208,
|
||||
"pullChecksum": "Q1Gcqe+ND8DFOlhM3R0o5KyZjR2oE=",
|
||||
"gitCommitOfApkPort": "d7ae612a3cc5f827289d915783b4cbf8c7207947",
|
||||
"files": [
|
||||
{
|
||||
"path": "/usr"
|
||||
}
|
||||
],
|
||||
"pullDependencies": ["foo", "bar"]
|
||||
}`,
|
||||
want: ApkMetadata{
|
||||
Package: "scanelf",
|
||||
OriginPackage: "pax-utils",
|
||||
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
Version: "1.3.4-r0",
|
||||
License: "GPL-2.0-only",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities",
|
||||
Description: "Scan ELF binaries for stuff",
|
||||
Size: 36745,
|
||||
InstalledSize: 94208,
|
||||
Dependencies: []string{"foo", "bar"},
|
||||
Checksum: "Q1Gcqe+ND8DFOlhM3R0o5KyZjR2oE=",
|
||||
GitCommit: "d7ae612a3cc5f827289d915783b4cbf8c7207947",
|
||||
Files: []ApkFileRecord{{Path: "/usr"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "single string dependencies",
|
||||
input: `{
|
||||
"package": "scanelf",
|
||||
"originPackage": "pax-utils",
|
||||
"maintainer": "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
"version": "1.3.4-r0",
|
||||
"license": "GPL-2.0-only",
|
||||
"architecture": "x86_64",
|
||||
"url": "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities",
|
||||
"description": "Scan ELF binaries for stuff",
|
||||
"size": 36745,
|
||||
"installedSize": 94208,
|
||||
"pullChecksum": "Q1Gcqe+ND8DFOlhM3R0o5KyZjR2oE=",
|
||||
"gitCommitOfApkPort": "d7ae612a3cc5f827289d915783b4cbf8c7207947",
|
||||
"files": [
|
||||
{
|
||||
"path": "/usr"
|
||||
}
|
||||
],
|
||||
"pullDependencies": "foo bar"
|
||||
}`,
|
||||
want: ApkMetadata{
|
||||
Package: "scanelf",
|
||||
OriginPackage: "pax-utils",
|
||||
Maintainer: "Natanael Copa <ncopa@alpinelinux.org>",
|
||||
Version: "1.3.4-r0",
|
||||
License: "GPL-2.0-only",
|
||||
Architecture: "x86_64",
|
||||
URL: "https://wiki.gentoo.org/wiki/Hardened/PaX_Utilities",
|
||||
Description: "Scan ELF binaries for stuff",
|
||||
Size: 36745,
|
||||
InstalledSize: 94208,
|
||||
Dependencies: []string{"foo", "bar"},
|
||||
Checksum: "Q1Gcqe+ND8DFOlhM3R0o5KyZjR2oE=",
|
||||
GitCommit: "d7ae612a3cc5f827289d915783b4cbf8c7207947",
|
||||
Files: []ApkFileRecord{{Path: "/usr"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
var got ApkMetadata
|
||||
err := json.Unmarshal([]byte(tt.input), &got)
|
||||
tt.wantErr(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpaceDelimitedStringSlice_UnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data string
|
||||
want []string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "empty string",
|
||||
data: `""`,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "single string with one elements",
|
||||
data: `"foo"`,
|
||||
want: []string{"foo"},
|
||||
},
|
||||
{
|
||||
name: "single string with multiple elements",
|
||||
data: `"foo bar"`,
|
||||
want: []string{"foo", "bar"},
|
||||
},
|
||||
{
|
||||
name: "string array",
|
||||
data: `["foo", "bar"]`,
|
||||
want: []string{"foo", "bar"},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
element := spaceDelimitedStringSlice{}
|
||||
tt.wantErr(t, element.UnmarshalJSON([]byte(tt.data)))
|
||||
assert.Equal(t, tt.want, []string(element))
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user