mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 00:13:15 +01:00
account for non-import shapes (#3997)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
79b6d5daa4
commit
96c34ffc43
@ -28,7 +28,7 @@ func (d *Document) UnmarshalJSON(data []byte) error {
|
||||
if d.Schema.Version == "1.0.0" && d.Descriptor.Name == "anchorectl" {
|
||||
// convert all file modes from decimal to octal
|
||||
for i := range d.Files {
|
||||
d.Files[i].Metadata.Mode = convertFileModeToBase8(d.Files[i].Metadata.Mode)
|
||||
d.Files[i].Metadata.Mode = convertBase10ToBase8(d.Files[i].Metadata.Mode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -31,14 +31,21 @@ type FileMetadataEntry struct {
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error {
|
||||
type Alias FileMetadataEntry
|
||||
aux := (*Alias)(f)
|
||||
type auxFileMetadataEntry FileMetadataEntry
|
||||
type fileMetadataEntryWithLegacyHint struct {
|
||||
*auxFileMetadataEntry `json:",inline"`
|
||||
LegacyHint any `json:"FileInfo"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, aux); err == nil {
|
||||
// we should have at least one field set to a non-zero value... otherwise this is a legacy entry
|
||||
if f.Mode != 0 || f.Type != "" || f.LinkDestination != "" ||
|
||||
f.UserID != 0 || f.GroupID != 0 || f.MIMEType != "" || f.Size != 0 {
|
||||
func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error {
|
||||
aux := fileMetadataEntryWithLegacyHint{
|
||||
auxFileMetadataEntry: (*auxFileMetadataEntry)(f),
|
||||
}
|
||||
if err := json.Unmarshal(data, &aux); err == nil {
|
||||
fieldsSpecified := f.Mode != 0 || f.Type != "" || f.LinkDestination != "" ||
|
||||
f.UserID != 0 || f.GroupID != 0 || f.MIMEType != "" || f.Size != 0
|
||||
if aux.LegacyHint == nil && fieldsSpecified {
|
||||
// we should have at least one field set to a non-zero value... (this is not a legacy shape)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -48,8 +55,14 @@ func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if !legacy.Type.WasInt {
|
||||
// this occurs for document shapes from a non-import path and indicates that the mode has already been converted to octal.
|
||||
// That being said, we want to handle all legacy shapes the same, so we will convert this to base 10 for consistency.
|
||||
legacy.Mode = convertBase8ToBase10(legacy.Mode)
|
||||
}
|
||||
|
||||
f.Mode = legacy.Mode
|
||||
f.Type = string(legacy.Type)
|
||||
f.Type = legacy.Type.Value
|
||||
f.LinkDestination = legacy.LinkDestination
|
||||
f.UserID = legacy.UserID
|
||||
f.GroupID = legacy.GroupID
|
||||
@ -82,12 +95,15 @@ type FileLicenseEvidence struct {
|
||||
Extent int `json:"extent"`
|
||||
}
|
||||
|
||||
type intOrStringFileType string
|
||||
type intOrStringFileType struct {
|
||||
Value string
|
||||
WasInt bool
|
||||
}
|
||||
|
||||
func (lt *intOrStringFileType) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
if err := json.Unmarshal(data, &str); err == nil {
|
||||
*lt = intOrStringFileType(str)
|
||||
lt.Value = str
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -96,13 +112,21 @@ func (lt *intOrStringFileType) UnmarshalJSON(data []byte) error {
|
||||
return fmt.Errorf("file.Type must be either string or int, got: %s", string(data))
|
||||
}
|
||||
|
||||
*lt = intOrStringFileType(num.String())
|
||||
lt.Value = num.String()
|
||||
lt.WasInt = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertFileModeToBase8(rawMode int) int {
|
||||
func convertBase10ToBase8(rawMode int) int {
|
||||
octalStr := fmt.Sprintf("%o", rawMode)
|
||||
// we don't need to check that this is a valid octal string since the input is always an integer
|
||||
result, _ := strconv.Atoi(octalStr)
|
||||
return result
|
||||
}
|
||||
|
||||
func convertBase8ToBase10(octalMode int) int {
|
||||
octalStr := strconv.Itoa(octalMode)
|
||||
result, _ := strconv.ParseInt(octalStr, 8, 64)
|
||||
|
||||
return int(result)
|
||||
}
|
||||
|
||||
@ -36,8 +36,9 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unmarshal legacy sbom import format",
|
||||
name: "unmarshal legacy image add internal document format",
|
||||
jsonData: []byte(`{
|
||||
"FileInfo": {},
|
||||
"Mode": 644,
|
||||
"Type": "RegularFile",
|
||||
"LinkDestination": "/usr/bin/python3",
|
||||
@ -47,7 +48,7 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
||||
"Size": 10174
|
||||
}`),
|
||||
expected: FileMetadataEntry{
|
||||
Mode: 644,
|
||||
Mode: 420, // important! we convert this to base 10 so that all documents are consistent
|
||||
Type: "RegularFile",
|
||||
LinkDestination: "/usr/bin/python3",
|
||||
UserID: 1000,
|
||||
@ -57,8 +58,9 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "unmarshal legacy sbom import format - integer type",
|
||||
name: "unmarshal legacy sbom import format",
|
||||
jsonData: []byte(`{
|
||||
"FileInfo": {},
|
||||
"Mode": 644,
|
||||
"Type": 0,
|
||||
"LinkDestination": "/usr/bin/python3",
|
||||
@ -93,6 +95,7 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
||||
{
|
||||
name: "unmarshal minimal legacy format",
|
||||
jsonData: []byte(`{
|
||||
"FileInfo": {},
|
||||
"Mode": 0,
|
||||
"Type": "RegularFile",
|
||||
"UserID": 0,
|
||||
@ -120,10 +123,11 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
||||
|
||||
func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
jsonData []byte
|
||||
expected string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
name string
|
||||
jsonData []byte
|
||||
expected string
|
||||
expectedWasInt bool
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
// string inputs - should pass through unchanged
|
||||
{
|
||||
@ -148,59 +152,70 @@ func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) {
|
||||
},
|
||||
// integer inputs - should convert to string representation
|
||||
{
|
||||
name: "int 0 (TypeRegular)",
|
||||
jsonData: []byte(`0`),
|
||||
expected: "RegularFile",
|
||||
name: "int 0 (TypeRegular)",
|
||||
jsonData: []byte(`0`),
|
||||
expected: "RegularFile",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 1 (TypeHardLink)",
|
||||
jsonData: []byte(`1`),
|
||||
expected: "HardLink",
|
||||
name: "int 1 (TypeHardLink)",
|
||||
jsonData: []byte(`1`),
|
||||
expected: "HardLink",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 2 (TypeSymLink)",
|
||||
jsonData: []byte(`2`),
|
||||
expected: "SymbolicLink",
|
||||
name: "int 2 (TypeSymLink)",
|
||||
jsonData: []byte(`2`),
|
||||
expected: "SymbolicLink",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 3 (TypeCharacterDevice)",
|
||||
jsonData: []byte(`3`),
|
||||
expected: "CharacterDevice",
|
||||
name: "int 3 (TypeCharacterDevice)",
|
||||
jsonData: []byte(`3`),
|
||||
expected: "CharacterDevice",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 4 (TypeBlockDevice)",
|
||||
jsonData: []byte(`4`),
|
||||
expected: "BlockDevice",
|
||||
name: "int 4 (TypeBlockDevice)",
|
||||
jsonData: []byte(`4`),
|
||||
expected: "BlockDevice",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 5 (TypeDirectory)",
|
||||
jsonData: []byte(`5`),
|
||||
expected: "Directory",
|
||||
name: "int 5 (TypeDirectory)",
|
||||
jsonData: []byte(`5`),
|
||||
expected: "Directory",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 6 (TypeFIFO)",
|
||||
jsonData: []byte(`6`),
|
||||
expected: "FIFONode",
|
||||
name: "int 6 (TypeFIFO)",
|
||||
jsonData: []byte(`6`),
|
||||
expected: "FIFONode",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 7 (TypeSocket)",
|
||||
jsonData: []byte(`7`),
|
||||
expected: "Socket",
|
||||
name: "int 7 (TypeSocket)",
|
||||
jsonData: []byte(`7`),
|
||||
expected: "Socket",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "int 8 (TypeIrregular)",
|
||||
jsonData: []byte(`8`),
|
||||
expected: "IrregularFile",
|
||||
name: "int 8 (TypeIrregular)",
|
||||
jsonData: []byte(`8`),
|
||||
expected: "IrregularFile",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "unknown int",
|
||||
jsonData: []byte(`99`),
|
||||
expected: "Unknown",
|
||||
name: "unknown int",
|
||||
jsonData: []byte(`99`),
|
||||
expected: "Unknown",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "negative int",
|
||||
jsonData: []byte(`-1`),
|
||||
expected: "Unknown",
|
||||
name: "negative int",
|
||||
jsonData: []byte(`-1`),
|
||||
expected: "Unknown",
|
||||
expectedWasInt: true,
|
||||
},
|
||||
{
|
||||
name: "null value",
|
||||
@ -244,12 +259,13 @@ func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, test.expected, string(ft))
|
||||
assert.Equal(t, test.expected, ft.Value)
|
||||
assert.Equal(t, test.expectedWasInt, ft.WasInt)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_convertFileModeToBase8(t *testing.T) {
|
||||
func Test_convertBase10ToBase8(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input int
|
||||
@ -269,7 +285,34 @@ func Test_convertFileModeToBase8(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual := convertFileModeToBase8(tt.input)
|
||||
actual := convertBase10ToBase8(tt.input)
|
||||
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_convertBase8ToBase10(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input int
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "no permissions",
|
||||
input: 0,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "symlink + rwxrwxrwx",
|
||||
input: 1000000777,
|
||||
expected: 134218239,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual := convertBase8ToBase10(tt.input)
|
||||
|
||||
require.Equal(t, tt.expected, actual)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user