mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +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
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/file"
|
"github.com/anchore/syft/syft/file"
|
||||||
@ -35,6 +40,58 @@ type ApkMetadata struct {
|
|||||||
Files []ApkFileRecord `json:"files"`
|
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).
|
// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records).
|
||||||
type ApkFileRecord struct {
|
type ApkFileRecord struct {
|
||||||
Path string `json:"path"`
|
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