github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/syft/pkg/cataloger/rpm/parse_rpm.go (about)

     1  package rpm
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	rpmdb "github.com/knqyf263/go-rpmdb/pkg"
     8  	"github.com/sassoftware/go-rpmutils"
     9  
    10  	"github.com/anchore/syft/syft/artifact"
    11  	"github.com/anchore/syft/syft/file"
    12  	"github.com/anchore/syft/syft/pkg"
    13  	"github.com/anchore/syft/syft/pkg/cataloger/generic"
    14  )
    15  
    16  // parseRpm parses a single RPM
    17  func parseRpm(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
    18  	rpm, err := rpmutils.ReadRpm(reader)
    19  	if err != nil {
    20  		return nil, nil, fmt.Errorf("RPM file found but unable to read: %s (%w)", reader.Location.RealPath, err)
    21  	}
    22  
    23  	nevra, err := rpm.Header.GetNEVRA()
    24  	if err != nil {
    25  		return nil, nil, err
    26  	}
    27  
    28  	licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE)
    29  	sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM)
    30  	vendor, _ := rpm.Header.GetString(rpmutils.VENDOR)
    31  	digestAlgorithm := getDigestAlgorithm(rpm.Header)
    32  	size, _ := rpm.Header.InstalledSize()
    33  	files, _ := rpm.Header.GetFiles()
    34  
    35  	pd := parsedData{
    36  		Licenses: pkg.NewLicensesFromLocation(reader.Location, licenses...),
    37  		RpmMetadata: pkg.RpmMetadata{
    38  			Name:      nevra.Name,
    39  			Version:   nevra.Version,
    40  			Epoch:     parseEpoch(nevra.Epoch),
    41  			Arch:      nevra.Arch,
    42  			Release:   nevra.Release,
    43  			SourceRpm: sourceRpm,
    44  			Vendor:    vendor,
    45  			Size:      int(size),
    46  			Files:     mapFiles(files, digestAlgorithm),
    47  		},
    48  	}
    49  
    50  	return []pkg.Package{newPackage(reader.Location, pd, nil)}, nil, nil
    51  }
    52  
    53  func getDigestAlgorithm(header *rpmutils.RpmHeader) string {
    54  	digestAlgorithm, _ := header.GetString(rpmutils.FILEDIGESTALGO)
    55  	if digestAlgorithm != "" {
    56  		return digestAlgorithm
    57  	}
    58  	digestAlgorithms, _ := header.GetUint32s(rpmutils.FILEDIGESTALGO)
    59  	if len(digestAlgorithms) > 0 {
    60  		digestAlgo := int(digestAlgorithms[0])
    61  		return rpmutils.GetFileAlgoName(digestAlgo)
    62  	}
    63  	return ""
    64  }
    65  
    66  func mapFiles(files []rpmutils.FileInfo, digestAlgorithm string) []pkg.RpmdbFileRecord {
    67  	var out []pkg.RpmdbFileRecord
    68  	for _, f := range files {
    69  		digest := file.Digest{}
    70  		if f.Digest() != "" {
    71  			digest = file.Digest{
    72  				Algorithm: digestAlgorithm,
    73  				Value:     f.Digest(),
    74  			}
    75  		}
    76  		out = append(out, pkg.RpmdbFileRecord{
    77  			Path:      f.Name(),
    78  			Mode:      pkg.RpmdbFileMode(f.Mode()),
    79  			Size:      int(f.Size()),
    80  			Digest:    digest,
    81  			UserName:  f.UserName(),
    82  			GroupName: f.GroupName(),
    83  			Flags:     rpmdb.FileFlags(f.Flags()).String(),
    84  		})
    85  	}
    86  	return out
    87  }
    88  
    89  func parseEpoch(epoch string) *int {
    90  	i, err := strconv.Atoi(epoch)
    91  	if err != nil {
    92  		return nil
    93  	}
    94  	return &i
    95  }