github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/apk_metadata.go (about) 1 package pkg 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7 "sort" 8 "strings" 9 10 "github.com/mitchellh/mapstructure" 11 "github.com/scylladb/go-set/strset" 12 13 "github.com/anchore/syft/syft/file" 14 ) 15 16 const ApkDBGlob = "**/lib/apk/db/installed" 17 18 var _ FileOwner = (*ApkMetadata)(nil) 19 20 // ApkMetadata represents all captured data for a Alpine DB package entry. 21 // See the following sources for more information: 22 // - https://wiki.alpinelinux.org/wiki/Apk_spec 23 // - https://git.alpinelinux.org/apk-tools/tree/src/package.c 24 // - https://git.alpinelinux.org/apk-tools/tree/src/database.c 25 type ApkMetadata struct { 26 Package string `mapstructure:"P" json:"package"` 27 OriginPackage string `mapstructure:"o" json:"originPackage" cyclonedx:"originPackage"` 28 Maintainer string `mapstructure:"m" json:"maintainer"` 29 Version string `mapstructure:"V" json:"version"` 30 Architecture string `mapstructure:"A" json:"architecture"` 31 URL string `mapstructure:"U" json:"url"` 32 Description string `mapstructure:"T" json:"description"` 33 Size int `mapstructure:"S" json:"size" cyclonedx:"size"` 34 InstalledSize int `mapstructure:"I" json:"installedSize" cyclonedx:"installedSize"` 35 Dependencies []string `mapstructure:"D" json:"pullDependencies" cyclonedx:"pullDependencies"` 36 Provides []string `mapstructure:"p" json:"provides" cyclonedx:"provides"` 37 Checksum string `mapstructure:"C" json:"pullChecksum" cyclonedx:"pullChecksum"` 38 GitCommit string `mapstructure:"c" json:"gitCommitOfApkPort" cyclonedx:"gitCommitOfApkPort"` 39 Files []ApkFileRecord `json:"files"` 40 } 41 42 type spaceDelimitedStringSlice []string 43 44 func (m *ApkMetadata) UnmarshalJSON(data []byte) error { 45 var fields []reflect.StructField 46 t := reflect.TypeOf(ApkMetadata{}) 47 for i := 0; i < t.NumField(); i++ { 48 f := t.Field(i) 49 if f.Name == "Dependencies" { 50 f.Type = reflect.TypeOf(spaceDelimitedStringSlice{}) 51 } 52 fields = append(fields, f) 53 } 54 apkMetadata := reflect.StructOf(fields) 55 inst := reflect.New(apkMetadata) 56 if err := json.Unmarshal(data, inst.Interface()); err != nil { 57 return err 58 } 59 60 return mapstructure.Decode(inst.Elem().Interface(), m) 61 } 62 63 func (a *spaceDelimitedStringSlice) UnmarshalJSON(data []byte) error { 64 var jsonObj interface{} 65 66 if err := json.Unmarshal(data, &jsonObj); err != nil { 67 return err 68 } 69 70 switch obj := jsonObj.(type) { 71 case string: 72 if obj == "" { 73 *a = nil 74 } else { 75 *a = strings.Split(obj, " ") 76 } 77 return nil 78 case []interface{}: 79 s := make([]string, 0, len(obj)) 80 for _, v := range obj { 81 value, ok := v.(string) 82 if !ok { 83 return fmt.Errorf("invalid type for string array element: %T", v) 84 } 85 s = append(s, value) 86 } 87 *a = s 88 return nil 89 case nil: 90 return nil 91 default: 92 return fmt.Errorf("invalid type for string array: %T", obj) 93 } 94 } 95 96 // ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records). 97 type ApkFileRecord struct { 98 Path string `json:"path"` 99 OwnerUID string `json:"ownerUid,omitempty"` 100 OwnerGID string `json:"ownerGid,omitempty"` 101 Permissions string `json:"permissions,omitempty"` 102 Digest *file.Digest `json:"digest,omitempty"` 103 } 104 105 func (m ApkMetadata) OwnedFiles() (result []string) { 106 s := strset.New() 107 for _, f := range m.Files { 108 if f.Path != "" { 109 s.Add(f.Path) 110 } 111 } 112 result = s.List() 113 sort.Strings(result) 114 return result 115 }