diff --git a/alpine/parser.go b/alpine/parser.go index f71c510d1..5da7d6d32 100644 --- a/alpine/parser.go +++ b/alpine/parser.go @@ -5,15 +5,16 @@ import ( "encoding/json" "io" "log/slog" + "unique" "github.com/quay/claircore" "github.com/quay/claircore/libvuln/driver" "github.com/quay/claircore/toolkit/types" ) -const ( - cveURLPrefix = "https://security.alpinelinux.org/vuln/" -) +const cveURLPrefix = "https://security.alpinelinux.org/vuln/" + +var space = unique.Make(cveURLPrefix) var _ driver.Parser = (*updater)(nil) @@ -50,15 +51,21 @@ func (u *updater) parse(ctx context.Context, sdb *SecurityDB) ([]*claircore.Vuln return out, nil } -// unpackSecFixes takes a map of secFixes and creates a claircore.Vulnerability for each all CVEs present. -func unpackSecFixes(partial claircore.Vulnerability, secFixes map[string][]string) []*claircore.Vulnerability { +// UnpackSecFixes creates a [claircore.Vulnerability] for every flaw ID on every +// version. +func unpackSecFixes(partial claircore.Vulnerability, secFixes map[string][]Flaw) []*claircore.Vulnerability { out := []*claircore.Vulnerability{} - for fixedIn, IDs := range secFixes { - for _, id := range IDs { + for fixedIn, flaws := range secFixes { + for _, flaw := range flaws { v := partial - v.Name = id + v.Name = flaw.String() v.FixedInVersion = fixedIn - v.Links = cveURLPrefix + id + v.Links = cveURLPrefix + flaw.String() + self, aka := flaw.Aliases() + v.Self = self + if aka.Valid() { + v.Aliases = append(v.Aliases, aka) + } out = append(out, &v) } } diff --git a/alpine/parser_test.go b/alpine/parser_test.go index cd76ba65c..cfdf9a66b 100644 --- a/alpine/parser_test.go +++ b/alpine/parser_test.go @@ -5,7 +5,9 @@ import ( "fmt" "os" "sort" + "strings" "testing" + "unique" "github.com/google/go-cmp/cmp" @@ -27,7 +29,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "botan", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2018-20187"), + Aliases: cveAlias("CVE-2018-20187"), }, { Name: "CVE-2018-12435", @@ -39,7 +43,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "botan", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2018-12435"), + Aliases: cveAlias("CVE-2018-12435"), }, { Name: "CVE-2018-9860", @@ -51,7 +57,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "botan", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2018-9860"), + Aliases: cveAlias("CVE-2018-9860"), }, { Name: "CVE-2018-9127", @@ -63,7 +71,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "botan", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2018-9127"), + Aliases: cveAlias("CVE-2018-9127"), }, { Name: "CVE-2019-9929", @@ -75,7 +85,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "cfengine", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2019-9929"), + Aliases: cveAlias("CVE-2019-9929"), }, { Name: "CVE-2017-6949", @@ -87,7 +99,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "chicken", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2017-6949"), + Aliases: cveAlias("CVE-2017-6949"), }, { Name: "CVE-2017-9334", @@ -99,7 +113,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "chicken", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2017-9334"), + Aliases: cveAlias("CVE-2017-9334"), }, { Name: "CVE-2016-6830", @@ -111,7 +127,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "chicken", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2016-6830"), + Aliases: cveAlias("CVE-2016-6830"), }, { Name: "CVE-2016-6831", @@ -123,7 +141,9 @@ var v3_10CommunityTruncatedVulns = []*claircore.Vulnerability{ Name: "chicken", Kind: types.SourcePackage, }, - Dist: dist310, + Dist: dist310, + Self: selfAlias("CVE-2016-6831"), + Aliases: cveAlias("CVE-2016-6831"), }, } @@ -173,3 +193,23 @@ func TestParser(t *testing.T) { }) } } + +func selfAlias(id string) claircore.Alias { + return claircore.Alias{ + Space: space, + Name: id, + } +} + +func cveAlias(id string) []claircore.Alias { + space, name, ok := strings.Cut(id, `-`) + if !ok || space != `CVE` { + panic("programmer error: bad CVE id") + } + return []claircore.Alias{ + { + Space: unique.Make(`CVE`), + Name: name, + }, + } +} diff --git a/alpine/secdb.go b/alpine/secdb.go index bf6c68e42..cae7591a1 100644 --- a/alpine/secdb.go +++ b/alpine/secdb.go @@ -1,18 +1,11 @@ package alpine -// Details define a package's name and relevant security fixes included in a -// given version. -type Details struct { - Name string `json:"name"` - // Fixed package version string mapped to an array of CVE ids affecting the - // package. - Secfixes map[string][]string `json:"secfixes"` -} +import ( + "strings" + "unique" -// Package wraps the Details. -type Package struct { - Pkg Details `json:"pkg"` -} + "github.com/quay/claircore" +) // SecurityDB is the security database structure. type SecurityDB struct { @@ -22,3 +15,38 @@ type SecurityDB struct { Apkurl string `json:"apkurl"` Packages []Package `json:"packages"` } + +// Package wraps the Details. +type Package struct { + Pkg Details `json:"pkg"` +} + +// Details define a package's name and relevant security fixes included in a +// given version. +type Details struct { + Name string `json:"name"` + // Fixed package version string mapped to an array of CVE ids affecting the + // package. + Secfixes map[string][]Flaw `json:"secfixes"` +} + +// Flaw is a helper to create Aliases on demand. +type Flaw string + +// Aliases constructs aliases for the Flaw. +// +// The "aka" alias may not be valid. The caller should check with +// [claircore.Alias.Valid]. +func (f Flaw) Aliases() (self, aka claircore.Alias) { + s := string(f) + self.Space = space + self.Name = s + if space, name, ok := strings.Cut(s, `-`); ok { + aka.Space = unique.Make(space) + aka.Name = name + } + return +} + +// String implements [fmt.Stringer]. +func (f Flaw) String() string { return string(f) } diff --git a/alpine/secdb_test.go b/alpine/secdb_test.go index 51ba73c95..392a8dbfe 100644 --- a/alpine/secdb_test.go +++ b/alpine/secdb_test.go @@ -18,7 +18,7 @@ var v3_10CommunityTruncatedSecDB = SecurityDB{ { Pkg: Details{ Name: "botan", - Secfixes: map[string][]string{ + Secfixes: map[string][]Flaw{ "2.9.0-r0": {"CVE-2018-20187"}, "2.7.0-r0": {"CVE-2018-12435"}, "2.6.0-r0": {"CVE-2018-9860"}, @@ -29,7 +29,7 @@ var v3_10CommunityTruncatedSecDB = SecurityDB{ { Pkg: Details{ Name: "cfengine", - Secfixes: map[string][]string{ + Secfixes: map[string][]Flaw{ "3.12.2-r0": {"CVE-2019-9929"}, }, }, @@ -37,7 +37,7 @@ var v3_10CommunityTruncatedSecDB = SecurityDB{ { Pkg: Details{ Name: "chicken", - Secfixes: map[string][]string{ + Secfixes: map[string][]Flaw{ "4.12.0-r3": {"CVE-2017-6949"}, "4.12.0-r2": {"CVE-2017-9334"}, "4.11.1-r0": {"CVE-2016-6830", "CVE-2016-6831"},