diff --git a/rpm/native_db.go b/rpm/native_db.go index a9dd664de..4a5f71f5c 100644 --- a/rpm/native_db.go +++ b/rpm/native_db.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "io" + "net/url" "runtime/trace" "strings" @@ -76,7 +77,7 @@ func packagesFromDB(ctx context.Context, pkgdb string, db nativeDB) ([]*claircor } p.Module = modStream p.Version = constructEVR(&b, &info) - p.RepositoryHint = constructHint(&b, &info) + p.RepositoryHint = constructHint(&info) if s, ok := src[info.SourceNEVR]; ok { p.Source = s @@ -185,14 +186,12 @@ func constructEVR(b *strings.Builder, info *Info) string { return b.String() } -func constructHint(b *strings.Builder, info *Info) string { - b.Reset() +func constructHint(info *Info) string { + v := url.Values{} if info.Digest != "" { - b.WriteString("hash:") switch info.DigestAlgo { case 8: - b.WriteString("sha256:") - b.WriteString(info.Digest) + v.Add("hash", fmt.Sprintf("sha256:%s", info.Digest)) } } if len(info.Signature) != 0 { @@ -204,20 +203,11 @@ func constructHint(b *strings.Builder, info *Info) string { if p.SigType != 0 { continue } - if b.Len() != 0 { - b.WriteByte('|') - } - fmt.Fprintf(b, "key:%016x", p.IssuerKeyId) + v.Add("key", fmt.Sprintf("%016x", p.IssuerKeyId)) case *packet.Signature: - if p.SigType != 0 || p.IssuerKeyId == nil { - continue - } - if b.Len() != 0 { - b.WriteByte('|') - } - fmt.Fprintf(b, "key:%016x", *p.IssuerKeyId) + v.Add("key", fmt.Sprintf("%016x", p.IssuerKeyId)) } } } - return b.String() + return v.Encode() } diff --git a/rpm/packagescanner.go b/rpm/packagescanner.go index 58858563a..1bac917c8 100644 --- a/rpm/packagescanner.go +++ b/rpm/packagescanner.go @@ -2,9 +2,11 @@ package rpm import ( "context" + "errors" "fmt" "io" "io/fs" + "net/url" "os" "path" "runtime/trace" @@ -167,6 +169,35 @@ func (ps *Scanner) Scan(ctx context.Context, layer *claircore.Layer) ([]*clairco } pkgs = append(pkgs, ps...) } + rm, err := repoMap(ctx, sys) + switch { + case errors.Is(err, nil) && len(rm) != 0: // OK + for _, pkg := range pkgs { + nerva := fmt.Sprintf("%s-%s.%s", pkg.Name, pkg.Version, pkg.Arch) + repoid, ok := rm[nerva] + if !ok { + // Packages not installed via dnf, which may happen during + // bootstrapping, aren't present in the dnf history database. + // This means the process shouldn't bail if the package is + // missing. + continue + } + v, err := url.ParseQuery(pkg.RepositoryHint) + if err != nil { // Shouldn't happen: + zlog.Warn(ctx). + AnErr("url.ParseQuery", err). + Msg("malformed RepositoryHint") + continue + } + v.Add("repoid", repoid) + pkg.RepositoryHint = v.Encode() + } + case errors.Is(err, nil) && len(rm) == 0: // nothing found + default: // some error + zlog.Warn(ctx). + AnErr("repoMap", err). + Msg("unable to open dnf history database") + } return pkgs, nil } diff --git a/test/rpmtest/manifest.go b/test/rpmtest/manifest.go index 087636dd6..490c193e2 100644 --- a/test/rpmtest/manifest.go +++ b/test/rpmtest/manifest.go @@ -3,7 +3,7 @@ package rpmtest import ( "encoding/json" "io" - "sort" + "net/url" "strings" "testing" @@ -41,7 +41,7 @@ func PackagesFromRPMManifest(t *testing.T, r io.Reader) []*claircore.Package { Version: rpm.Version + "-" + rpm.Release, Kind: "binary", Arch: rpm.Arch, - RepositoryHint: "key:" + rpm.GPG, + RepositoryHint: url.Values{"key": {rpm.GPG}}.Encode(), Module: rpm.Module, } if s, ok := src[rpm.Source]; ok { @@ -82,35 +82,18 @@ var Options = cmp.Options{ // so cook up a comparison function that understands the rpm package's packed format. var HintCompare = cmp.FilterPath( func(p cmp.Path) bool { return p.Last().String() == ".RepositoryHint" }, - cmpopts.AcyclicTransformer("NormalizeHint", func(h string) string { - n := [][2]string{} - for _, s := range strings.Split(h, "|") { - if s == "" { - continue - } - k, v, ok := strings.Cut(s, ":") - if !ok { - panic("odd format: " + s) - } - if k == "hash" { - continue - } - i := len(n) - n = append(n, [2]string{}) - n[i][0] = k - n[i][1] = v + cmp.Comparer(func(a, b string) bool { + av, err := url.ParseQuery(a) + if err != nil { + panic(err) } - sort.Slice(n, func(i, j int) bool { return n[i][0] < n[i][1] }) - var b strings.Builder - for i, s := range n { - if i != 0 { - b.WriteByte('|') - } - b.WriteString(s[0]) - b.WriteByte(':') - b.WriteString(s[1]) + bv, err := url.ParseQuery(b) + if err != nil { + panic(err) } - return b.String() + av.Del("hash") + bv.Del("hash") + return cmp.Equal(av.Encode(), bv.Encode()) }), )