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  }