mirror of
https://github.com/anchore/syft.git
synced 2026-11-01 15:06:36 +01:00
Fix: unmarshal key values in Java, Go, and Conan metadata (#2603)
Previously, Syft represented several metadata fields as map[string]string, however this representation erased ordering, so Syft now represents these values as []KeyValue. Add custom unmarshaling so that JSON that was written by older versions of Syft using the map[string]string representation can be parsed into the new []KeyValue representation. Signed-off-by: Will Murphy <will.murphy@anchore.com>
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
@@ -70,6 +73,59 @@ type JavaManifest struct {
|
||||
Sections []KeyValues `json:"sections,omitempty"`
|
||||
}
|
||||
|
||||
type unmarshalJavaManifest JavaManifest
|
||||
|
||||
type legacyJavaManifest struct {
|
||||
Main map[string]string `json:"main"`
|
||||
NamedSections map[string]map[string]string `json:"namedSections"`
|
||||
}
|
||||
|
||||
func (m *JavaManifest) UnmarshalJSON(b []byte) error {
|
||||
var either map[string]any
|
||||
err := json.Unmarshal(b, &either)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not unmarshal java manifest: %w", err)
|
||||
}
|
||||
if _, ok := either["namedSections"]; ok {
|
||||
var lm legacyJavaManifest
|
||||
if err = json.Unmarshal(b, &lm); err != nil {
|
||||
return fmt.Errorf("could not unmarshal java manifest: %w", err)
|
||||
}
|
||||
*m = lm.toNewManifest()
|
||||
return nil
|
||||
}
|
||||
var jm unmarshalJavaManifest
|
||||
err = json.Unmarshal(b, &jm)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not unmarshal java manifest: %w", err)
|
||||
}
|
||||
*m = JavaManifest(jm)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (lm legacyJavaManifest) toNewManifest() JavaManifest {
|
||||
var result JavaManifest
|
||||
result.Main = keyValuesFromMap(lm.Main)
|
||||
var sectionNames []string
|
||||
for k := range lm.NamedSections {
|
||||
sectionNames = append(sectionNames, k)
|
||||
}
|
||||
sort.Strings(sectionNames)
|
||||
var sections []KeyValues
|
||||
for _, name := range sectionNames {
|
||||
section := KeyValues{
|
||||
KeyValue{
|
||||
Key: "Name",
|
||||
Value: name,
|
||||
},
|
||||
}
|
||||
section = append(section, keyValuesFromMap(lm.NamedSections[name])...)
|
||||
sections = append(sections, section)
|
||||
}
|
||||
result.Sections = sections
|
||||
return result
|
||||
}
|
||||
|
||||
func (m JavaManifest) Section(name string) KeyValues {
|
||||
for _, section := range m.Sections {
|
||||
if sectionName, ok := section.Get("Name"); ok && sectionName == name {
|
||||
|
||||
@@ -3,6 +3,7 @@ package pkg
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -109,3 +110,103 @@ func TestPomProperties_PkgTypeIndicated(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_legacyJavaManifest_toNewManifest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
lm legacyJavaManifest
|
||||
want JavaManifest
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
lm: legacyJavaManifest{},
|
||||
want: JavaManifest{},
|
||||
},
|
||||
{
|
||||
name: "main sections are sorted",
|
||||
lm: legacyJavaManifest{
|
||||
Main: map[string]string{
|
||||
"a key": "a value",
|
||||
"b key": "b value",
|
||||
"c key": "c value",
|
||||
},
|
||||
},
|
||||
want: JavaManifest{Main: KeyValues{
|
||||
{
|
||||
Key: "a key",
|
||||
Value: "a value",
|
||||
},
|
||||
{
|
||||
Key: "b key",
|
||||
Value: "b value",
|
||||
},
|
||||
{
|
||||
Key: "c key",
|
||||
Value: "c value",
|
||||
},
|
||||
}},
|
||||
},
|
||||
{
|
||||
name: "named sections have their name in the result",
|
||||
lm: legacyJavaManifest{
|
||||
NamedSections: map[string]map[string]string{
|
||||
"a section": {
|
||||
"a key": "a value",
|
||||
"b key": "b value",
|
||||
"c key": "c value",
|
||||
},
|
||||
"b section": {
|
||||
"d key": "d value",
|
||||
"e key": "e value",
|
||||
"f key": "f value",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: JavaManifest{Sections: []KeyValues{
|
||||
{
|
||||
{
|
||||
Key: "Name",
|
||||
Value: "a section",
|
||||
},
|
||||
{
|
||||
Key: "a key",
|
||||
Value: "a value",
|
||||
},
|
||||
{
|
||||
Key: "b key",
|
||||
Value: "b value",
|
||||
},
|
||||
{
|
||||
Key: "c key",
|
||||
Value: "c value",
|
||||
},
|
||||
},
|
||||
{
|
||||
{
|
||||
Key: "Name",
|
||||
Value: "b section",
|
||||
},
|
||||
{
|
||||
Key: "d key",
|
||||
Value: "d value",
|
||||
},
|
||||
{
|
||||
Key: "e key",
|
||||
Value: "e value",
|
||||
},
|
||||
{
|
||||
Key: "f key",
|
||||
Value: "f value",
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if diff := cmp.Diff(tt.want, tt.lm.toNewManifest()); diff != "" {
|
||||
t.Errorf("unexpected diff in manifest (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type KeyValue struct {
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
@@ -26,3 +32,39 @@ func (k KeyValues) MustGet(key string) string {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func keyValuesFromMap(m map[string]string) KeyValues {
|
||||
var result KeyValues
|
||||
var mapKeys []string
|
||||
for k := range m {
|
||||
mapKeys = append(mapKeys, k)
|
||||
}
|
||||
sort.Strings(mapKeys)
|
||||
for _, k := range mapKeys {
|
||||
result = append(result, KeyValue{
|
||||
Key: k,
|
||||
Value: m[k],
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (k *KeyValues) UnmarshalJSON(b []byte) error {
|
||||
var kvs []KeyValue
|
||||
if err := json.Unmarshal(b, &kvs); err != nil {
|
||||
var legacyMap map[string]string
|
||||
if err := json.Unmarshal(b, &legacyMap); err != nil {
|
||||
return fmt.Errorf("unable to unmarshal KeyValues: %w", err)
|
||||
}
|
||||
var keys []string
|
||||
for k := range legacyMap {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
kvs = append(kvs, KeyValue{Key: k, Value: legacyMap[k]})
|
||||
}
|
||||
}
|
||||
*k = kvs
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user