syft/syft/pkg/cataloger/rpm/parse_rpm.go
Christopher Angelo Phillips 42fa9e4965
feat: update syft license concept to complex struct (#1743)
this PR makes the following changes to update the underlying license model to have more expressive capabilities
it also provides some guarantee's surrounding the license values themselves

- Licenses are updated from string -> pkg.LicenseSet which contain pkg.License with the following fields:
- original `Value` read by syft
- If it's possible to construct licenses will always have a valid SPDX expression for downstream consumption
- the above is run against a generated list of SPDX license ID to try and find the correct ID
- SPDX concluded vs declared is added to the new struct
- URL source for license is added to the new struct
- Location source is added to the new struct to show where the expression was pulled from
2023-05-15 16:23:39 -04:00

97 lines
2.5 KiB
Go

package rpm
import (
"fmt"
"strconv"
rpmdb "github.com/knqyf263/go-rpmdb/pkg"
"github.com/sassoftware/go-rpmutils"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
"github.com/anchore/syft/syft/source"
)
// parseRpm parses a single RPM
func parseRpm(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
rpm, err := rpmutils.ReadRpm(reader)
if err != nil {
return nil, nil, fmt.Errorf("RPM file found but unable to read: %s (%w)", reader.Location.RealPath, err)
}
nevra, err := rpm.Header.GetNEVRA()
if err != nil {
return nil, nil, err
}
licenses, _ := rpm.Header.GetStrings(rpmutils.LICENSE)
sourceRpm, _ := rpm.Header.GetString(rpmutils.SOURCERPM)
vendor, _ := rpm.Header.GetString(rpmutils.VENDOR)
digestAlgorithm := getDigestAlgorithm(rpm.Header)
size, _ := rpm.Header.InstalledSize()
files, _ := rpm.Header.GetFiles()
pd := parsedData{
Licenses: pkg.NewLicensesFromLocation(reader.Location, licenses...),
RpmMetadata: pkg.RpmMetadata{
Name: nevra.Name,
Version: nevra.Version,
Epoch: parseEpoch(nevra.Epoch),
Arch: nevra.Arch,
Release: nevra.Release,
SourceRpm: sourceRpm,
Vendor: vendor,
Size: int(size),
Files: mapFiles(files, digestAlgorithm),
},
}
return []pkg.Package{newPackage(reader.Location, pd, nil)}, nil, nil
}
func getDigestAlgorithm(header *rpmutils.RpmHeader) string {
digestAlgorithm, _ := header.GetString(rpmutils.FILEDIGESTALGO)
if digestAlgorithm != "" {
return digestAlgorithm
}
digestAlgorithms, _ := header.GetUint32s(rpmutils.FILEDIGESTALGO)
if len(digestAlgorithms) > 0 {
digestAlgo := int(digestAlgorithms[0])
return rpmutils.GetFileAlgoName(digestAlgo)
}
return ""
}
func mapFiles(files []rpmutils.FileInfo, digestAlgorithm string) []pkg.RpmdbFileRecord {
var out []pkg.RpmdbFileRecord
for _, f := range files {
digest := file.Digest{}
if f.Digest() != "" {
digest = file.Digest{
Algorithm: digestAlgorithm,
Value: f.Digest(),
}
}
out = append(out, pkg.RpmdbFileRecord{
Path: f.Name(),
Mode: pkg.RpmdbFileMode(f.Mode()),
Size: int(f.Size()),
Digest: digest,
UserName: f.UserName(),
GroupName: f.GroupName(),
Flags: rpmdb.FileFlags(f.Flags()).String(),
})
}
return out
}
func parseEpoch(epoch string) *int {
i, err := strconv.Atoi(epoch)
if err != nil {
return nil
}
return &i
}