github.com/anchore/syft@v1.38.2/syft/format/syftjson/model/source.go (about) 1 package model 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7 "strconv" 8 "strings" 9 10 "github.com/anchore/syft/internal/sourcemetadata" 11 "github.com/anchore/syft/syft/source" 12 ) 13 14 // Source represents the artifact that was analyzed to generate this SBOM, such as a container image, directory, or file archive. 15 // The Supplier field can be provided by users to fulfill NTIA minimum elements requirements. 16 type Source struct { 17 // ID is a unique identifier for the analyzed source artifact. 18 ID string `json:"id"` 19 20 // Name is the name of the analyzed artifact (e.g., image name, directory path). 21 Name string `json:"name"` 22 23 // Version is the version of the analyzed artifact (e.g., image tag). 24 Version string `json:"version"` 25 26 // Supplier is supplier information, which can be user-provided for NTIA minimum elements compliance. 27 Supplier string `json:"supplier,omitempty"` 28 29 // Type is the source type (e.g., "image", "directory", "file"). 30 Type string `json:"type"` 31 32 // Metadata contains additional source-specific metadata. 33 Metadata interface{} `json:"metadata"` 34 } 35 36 // sourceUnpacker is used to unmarshal Source objects 37 type sourceUnpacker struct { 38 ID string `json:"id,omitempty"` 39 Name string `json:"name"` 40 Version string `json:"version"` 41 Supplier string `json:"supplier,omitempty"` 42 Type string `json:"type"` 43 Metadata json.RawMessage `json:"metadata"` 44 Target json.RawMessage `json:"target"` // pre-v9 schema support 45 } 46 47 // UnmarshalJSON populates a source object from JSON bytes. 48 func (s *Source) UnmarshalJSON(b []byte) error { 49 var unpacker sourceUnpacker 50 err := json.Unmarshal(b, &unpacker) 51 if err != nil { 52 return err 53 } 54 55 s.Name = unpacker.Name 56 s.Version = unpacker.Version 57 s.Supplier = unpacker.Supplier 58 s.Type = unpacker.Type 59 s.ID = unpacker.ID 60 61 if len(unpacker.Target) > 0 { 62 s.Type = cleanPreSchemaV9MetadataType(s.Type) 63 s.Metadata, err = extractPreSchemaV9Metadata(s.Type, unpacker.Target) 64 if err != nil { 65 return fmt.Errorf("unable to extract pre-schema-v9 source metadata: %w", err) 66 } 67 return nil 68 } 69 70 return unpackSrcMetadata(s, unpacker) 71 } 72 73 func unpackSrcMetadata(s *Source, unpacker sourceUnpacker) error { 74 rt := sourcemetadata.ReflectTypeFromJSONName(s.Type) 75 if rt == nil { 76 // in cases where we are converting from an SBOM without any source information, we don't want this to be fatal 77 return nil 78 } 79 80 val := reflect.New(rt).Interface() 81 if len(unpacker.Metadata) > 0 { 82 if err := json.Unmarshal(unpacker.Metadata, val); err != nil { 83 return err 84 } 85 } 86 87 s.Metadata = reflect.ValueOf(val).Elem().Interface() 88 89 return nil 90 } 91 92 func cleanPreSchemaV9MetadataType(t string) string { 93 t = strings.ToLower(t) 94 if t == "dir" { 95 return "directory" 96 } 97 return t 98 } 99 100 func extractPreSchemaV9Metadata(t string, target []byte) (interface{}, error) { 101 switch t { 102 case "directory", "dir": 103 cleanTarget, err := strconv.Unquote(string(target)) 104 if err != nil { 105 cleanTarget = string(target) 106 } 107 108 return source.DirectoryMetadata{ 109 Path: cleanTarget, 110 }, nil 111 112 case "file": 113 cleanTarget, err := strconv.Unquote(string(target)) 114 if err != nil { 115 cleanTarget = string(target) 116 } 117 118 return source.FileMetadata{ 119 Path: cleanTarget, 120 }, nil 121 122 case "image": 123 var payload source.ImageMetadata 124 if err := json.Unmarshal(target, &payload); err != nil { 125 return nil, err 126 } 127 return payload, nil 128 129 default: 130 return nil, fmt.Errorf("unsupported package metadata type: %+v", t) 131 } 132 }