github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/pkg/cataloger/redhat/parse_rpm_archive.go (about)

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