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