mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23: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" {
|
if d.Schema.Version == "1.0.0" && d.Descriptor.Name == "anchorectl" {
|
||||||
// convert all file modes from decimal to octal
|
// convert all file modes from decimal to octal
|
||||||
for i := range d.Files {
|
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"`
|
Size int64 `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error {
|
type auxFileMetadataEntry FileMetadataEntry
|
||||||
type Alias FileMetadataEntry
|
type fileMetadataEntryWithLegacyHint struct {
|
||||||
aux := (*Alias)(f)
|
*auxFileMetadataEntry `json:",inline"`
|
||||||
|
LegacyHint any `json:"FileInfo"`
|
||||||
|
}
|
||||||
|
|
||||||
if err := json.Unmarshal(data, aux); err == nil {
|
func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error {
|
||||||
// we should have at least one field set to a non-zero value... otherwise this is a legacy entry
|
aux := fileMetadataEntryWithLegacyHint{
|
||||||
if f.Mode != 0 || f.Type != "" || f.LinkDestination != "" ||
|
auxFileMetadataEntry: (*auxFileMetadataEntry)(f),
|
||||||
f.UserID != 0 || f.GroupID != 0 || f.MIMEType != "" || f.Size != 0 {
|
}
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,8 +55,14 @@ func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error {
|
|||||||
return err
|
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.Mode = legacy.Mode
|
||||||
f.Type = string(legacy.Type)
|
f.Type = legacy.Type.Value
|
||||||
f.LinkDestination = legacy.LinkDestination
|
f.LinkDestination = legacy.LinkDestination
|
||||||
f.UserID = legacy.UserID
|
f.UserID = legacy.UserID
|
||||||
f.GroupID = legacy.GroupID
|
f.GroupID = legacy.GroupID
|
||||||
@ -82,12 +95,15 @@ type FileLicenseEvidence struct {
|
|||||||
Extent int `json:"extent"`
|
Extent int `json:"extent"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type intOrStringFileType string
|
type intOrStringFileType struct {
|
||||||
|
Value string
|
||||||
|
WasInt bool
|
||||||
|
}
|
||||||
|
|
||||||
func (lt *intOrStringFileType) UnmarshalJSON(data []byte) error {
|
func (lt *intOrStringFileType) UnmarshalJSON(data []byte) error {
|
||||||
var str string
|
var str string
|
||||||
if err := json.Unmarshal(data, &str); err == nil {
|
if err := json.Unmarshal(data, &str); err == nil {
|
||||||
*lt = intOrStringFileType(str)
|
lt.Value = str
|
||||||
return nil
|
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))
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertFileModeToBase8(rawMode int) int {
|
func convertBase10ToBase8(rawMode int) int {
|
||||||
octalStr := fmt.Sprintf("%o", rawMode)
|
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
|
// we don't need to check that this is a valid octal string since the input is always an integer
|
||||||
result, _ := strconv.Atoi(octalStr)
|
result, _ := strconv.Atoi(octalStr)
|
||||||
return result
|
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(`{
|
jsonData: []byte(`{
|
||||||
|
"FileInfo": {},
|
||||||
"Mode": 644,
|
"Mode": 644,
|
||||||
"Type": "RegularFile",
|
"Type": "RegularFile",
|
||||||
"LinkDestination": "/usr/bin/python3",
|
"LinkDestination": "/usr/bin/python3",
|
||||||
@ -47,7 +48,7 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
|||||||
"Size": 10174
|
"Size": 10174
|
||||||
}`),
|
}`),
|
||||||
expected: FileMetadataEntry{
|
expected: FileMetadataEntry{
|
||||||
Mode: 644,
|
Mode: 420, // important! we convert this to base 10 so that all documents are consistent
|
||||||
Type: "RegularFile",
|
Type: "RegularFile",
|
||||||
LinkDestination: "/usr/bin/python3",
|
LinkDestination: "/usr/bin/python3",
|
||||||
UserID: 1000,
|
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(`{
|
jsonData: []byte(`{
|
||||||
|
"FileInfo": {},
|
||||||
"Mode": 644,
|
"Mode": 644,
|
||||||
"Type": 0,
|
"Type": 0,
|
||||||
"LinkDestination": "/usr/bin/python3",
|
"LinkDestination": "/usr/bin/python3",
|
||||||
@ -93,6 +95,7 @@ func Test_FileMetadataEntry_UnmarshalJSON(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "unmarshal minimal legacy format",
|
name: "unmarshal minimal legacy format",
|
||||||
jsonData: []byte(`{
|
jsonData: []byte(`{
|
||||||
|
"FileInfo": {},
|
||||||
"Mode": 0,
|
"Mode": 0,
|
||||||
"Type": "RegularFile",
|
"Type": "RegularFile",
|
||||||
"UserID": 0,
|
"UserID": 0,
|
||||||
@ -123,6 +126,7 @@ func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
jsonData []byte
|
jsonData []byte
|
||||||
expected string
|
expected string
|
||||||
|
expectedWasInt bool
|
||||||
wantErr require.ErrorAssertionFunc
|
wantErr require.ErrorAssertionFunc
|
||||||
}{
|
}{
|
||||||
// string inputs - should pass through unchanged
|
// string inputs - should pass through unchanged
|
||||||
@ -151,56 +155,67 @@ func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) {
|
|||||||
name: "int 0 (TypeRegular)",
|
name: "int 0 (TypeRegular)",
|
||||||
jsonData: []byte(`0`),
|
jsonData: []byte(`0`),
|
||||||
expected: "RegularFile",
|
expected: "RegularFile",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 1 (TypeHardLink)",
|
name: "int 1 (TypeHardLink)",
|
||||||
jsonData: []byte(`1`),
|
jsonData: []byte(`1`),
|
||||||
expected: "HardLink",
|
expected: "HardLink",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 2 (TypeSymLink)",
|
name: "int 2 (TypeSymLink)",
|
||||||
jsonData: []byte(`2`),
|
jsonData: []byte(`2`),
|
||||||
expected: "SymbolicLink",
|
expected: "SymbolicLink",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 3 (TypeCharacterDevice)",
|
name: "int 3 (TypeCharacterDevice)",
|
||||||
jsonData: []byte(`3`),
|
jsonData: []byte(`3`),
|
||||||
expected: "CharacterDevice",
|
expected: "CharacterDevice",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 4 (TypeBlockDevice)",
|
name: "int 4 (TypeBlockDevice)",
|
||||||
jsonData: []byte(`4`),
|
jsonData: []byte(`4`),
|
||||||
expected: "BlockDevice",
|
expected: "BlockDevice",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 5 (TypeDirectory)",
|
name: "int 5 (TypeDirectory)",
|
||||||
jsonData: []byte(`5`),
|
jsonData: []byte(`5`),
|
||||||
expected: "Directory",
|
expected: "Directory",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 6 (TypeFIFO)",
|
name: "int 6 (TypeFIFO)",
|
||||||
jsonData: []byte(`6`),
|
jsonData: []byte(`6`),
|
||||||
expected: "FIFONode",
|
expected: "FIFONode",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 7 (TypeSocket)",
|
name: "int 7 (TypeSocket)",
|
||||||
jsonData: []byte(`7`),
|
jsonData: []byte(`7`),
|
||||||
expected: "Socket",
|
expected: "Socket",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "int 8 (TypeIrregular)",
|
name: "int 8 (TypeIrregular)",
|
||||||
jsonData: []byte(`8`),
|
jsonData: []byte(`8`),
|
||||||
expected: "IrregularFile",
|
expected: "IrregularFile",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "unknown int",
|
name: "unknown int",
|
||||||
jsonData: []byte(`99`),
|
jsonData: []byte(`99`),
|
||||||
expected: "Unknown",
|
expected: "Unknown",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "negative int",
|
name: "negative int",
|
||||||
jsonData: []byte(`-1`),
|
jsonData: []byte(`-1`),
|
||||||
expected: "Unknown",
|
expected: "Unknown",
|
||||||
|
expectedWasInt: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "null value",
|
name: "null value",
|
||||||
@ -244,12 +259,13 @@ func Test_intOrStringFileType_UnmarshalJSON(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
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 {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
input int
|
input int
|
||||||
@ -269,7 +285,34 @@ func Test_convertFileModeToBase8(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
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)
|
require.Equal(t, tt.expected, actual)
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user