github.com/anchore/syft@v1.38.2/syft/format/syftjson/model/file.go (about) 1 package model 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strconv" 7 8 stereoFile "github.com/anchore/stereoscope/pkg/file" 9 "github.com/anchore/syft/syft/file" 10 "github.com/anchore/syft/syft/license" 11 ) 12 13 // File represents a file discovered during cataloging with its metadata, content digests, licenses, and relationships to packages. 14 type File struct { 15 // ID is a unique identifier for this file within the SBOM. 16 ID string `json:"id"` 17 18 // Location is the file path and layer information where this file was found. 19 Location file.Coordinates `json:"location"` 20 21 // Metadata contains filesystem metadata such as permissions, ownership, and file type. 22 Metadata *FileMetadataEntry `json:"metadata,omitempty"` 23 24 // Contents is the file contents for small files. 25 Contents string `json:"contents,omitempty"` 26 27 // Digests contains cryptographic hashes of the file contents. 28 Digests []file.Digest `json:"digests,omitempty"` 29 30 // Licenses contains license information discovered within this file. 31 Licenses []FileLicense `json:"licenses,omitempty"` 32 33 // Executable contains executable metadata if this file is a binary. 34 Executable *file.Executable `json:"executable,omitempty"` 35 36 // Unknowns contains unknown fields for forward compatibility. 37 Unknowns []string `json:"unknowns,omitempty"` 38 } 39 40 // FileMetadataEntry contains filesystem-level metadata attributes such as permissions, ownership, type, and size for a cataloged file. 41 type FileMetadataEntry struct { 42 // Mode is the Unix file permission mode in octal format. 43 Mode int `json:"mode"` 44 45 // Type is the file type (e.g., "RegularFile", "Directory", "SymbolicLink"). 46 Type string `json:"type"` 47 48 // LinkDestination is the target path for symbolic links. 49 LinkDestination string `json:"linkDestination,omitempty"` 50 51 // UserID is the file owner user ID. 52 UserID int `json:"userID"` 53 54 // GroupID is the file owner group ID. 55 GroupID int `json:"groupID"` 56 57 // MIMEType is the MIME type of the file contents. 58 MIMEType string `json:"mimeType"` 59 60 // Size is the file size in bytes. 61 Size int64 `json:"size"` 62 } 63 64 type auxFileMetadataEntry FileMetadataEntry 65 type fileMetadataEntryWithLegacyHint struct { 66 *auxFileMetadataEntry `json:",inline"` 67 LegacyHint any `json:"FileInfo"` 68 } 69 70 func (f *FileMetadataEntry) UnmarshalJSON(data []byte) error { 71 aux := fileMetadataEntryWithLegacyHint{ 72 auxFileMetadataEntry: (*auxFileMetadataEntry)(f), 73 } 74 if err := json.Unmarshal(data, &aux); err == nil { 75 fieldsSpecified := f.Mode != 0 || f.Type != "" || f.LinkDestination != "" || 76 f.UserID != 0 || f.GroupID != 0 || f.MIMEType != "" || f.Size != 0 77 if aux.LegacyHint == nil && fieldsSpecified { 78 // we should have at least one field set to a non-zero value... (this is not a legacy shape) 79 return nil 80 } 81 } 82 83 var legacy sbomImportLegacyFileMetadataEntry 84 if err := json.Unmarshal(data, &legacy); err != nil { 85 return err 86 } 87 88 if !legacy.Type.WasInt { 89 // this occurs for document shapes from a non-import path and indicates that the mode has already been converted to octal. 90 // That being said, we want to handle all legacy shapes the same, so we will convert this to base 10 for consistency. 91 legacy.Mode = convertBase8ToBase10(legacy.Mode) 92 } 93 94 f.Mode = legacy.Mode 95 f.Type = legacy.Type.Value 96 f.LinkDestination = legacy.LinkDestination 97 f.UserID = legacy.UserID 98 f.GroupID = legacy.GroupID 99 f.MIMEType = legacy.MIMEType 100 f.Size = legacy.Size 101 102 return nil 103 } 104 105 type sbomImportLegacyFileMetadataEntry struct { 106 Mode int `json:"Mode"` 107 Type intOrStringFileType `json:"Type"` 108 LinkDestination string `json:"LinkDestination"` 109 UserID int `json:"UserID"` 110 GroupID int `json:"GroupID"` 111 MIMEType string `json:"MIMEType"` 112 Size int64 `json:"Size"` 113 } 114 115 // FileLicense represents license information discovered within a file's contents or metadata, including the matched license text and SPDX expression. 116 type FileLicense struct { 117 // Value is the raw license identifier or text as found in the file. 118 Value string `json:"value"` 119 120 // SPDXExpression is the parsed SPDX license expression. 121 SPDXExpression string `json:"spdxExpression"` 122 123 // Type is the license type classification (e.g., declared, concluded, discovered). 124 Type license.Type `json:"type"` 125 126 // Evidence contains supporting evidence for this license detection. 127 Evidence *FileLicenseEvidence `json:"evidence,omitempty"` 128 } 129 130 // FileLicenseEvidence contains supporting evidence for a license detection in a file, including the byte offset, extent, and confidence level. 131 type FileLicenseEvidence struct { 132 // Confidence is the confidence score for this license detection (0-100). 133 Confidence int `json:"confidence"` 134 135 // Offset is the byte offset where the license text starts in the file. 136 Offset int `json:"offset"` 137 138 // Extent is the length of the license text in bytes. 139 Extent int `json:"extent"` 140 } 141 142 type intOrStringFileType struct { 143 Value string 144 WasInt bool 145 } 146 147 func (lt *intOrStringFileType) UnmarshalJSON(data []byte) error { 148 var str string 149 if err := json.Unmarshal(data, &str); err == nil { 150 lt.Value = str 151 return nil 152 } 153 154 var num stereoFile.Type 155 if err := json.Unmarshal(data, &num); err != nil { 156 return fmt.Errorf("file.Type must be either string or int, got: %s", string(data)) 157 } 158 159 lt.Value = num.String() 160 lt.WasInt = true 161 return nil 162 } 163 164 func convertBase10ToBase8(rawMode int) int { 165 octalStr := fmt.Sprintf("%o", rawMode) 166 // we don't need to check that this is a valid octal string since the input is always an integer 167 result, _ := strconv.Atoi(octalStr) 168 return result 169 } 170 171 func convertBase8ToBase10(octalMode int) int { 172 octalStr := strconv.Itoa(octalMode) 173 result, _ := strconv.ParseInt(octalStr, 8, 64) 174 175 return int(result) 176 }