Skip to content

Commit

Permalink
fix apk decode for older data shapes (anchore#1341)
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>
  • Loading branch information
wagoodman committed Nov 15, 2022
1 parent 182bd8d commit 611667e
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 0 deletions.
57 changes: 57 additions & 0 deletions syft/pkg/apk_metadata.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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"`
Expand Down
156 changes: 156 additions & 0 deletions syft/pkg/apk_metadata_test.go
Original file line number Diff line number Diff line change
@@ -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))
})
}
}

0 comments on commit 611667e

Please sign in to comment.