From c239d957264391c29cab9dc16bf95c5c8f9a8ae3 Mon Sep 17 00:00:00 2001 From: crozzy Date: Mon, 28 Oct 2024 09:27:07 -0700 Subject: [PATCH] suse: dynamic distribution discovery Currently Suse distributions are predefined in the code, this change adds dynamic support for two Suse distro flavors: suse.linux.enterprise.server and opensuse.leap. Signed-off-by: crozzy --- go.mod | 1 + go.sum | 2 + pkg/ovalutil/pool.go | 18 +-- suse/distributionscanner.go | 88 +++++++----- suse/distributionscanner_test.go | 90 ++++++------ suse/factory.go | 234 +++++++++++++++++++++++++++++++ suse/factory_test.go | 24 ++++ suse/parser.go | 7 +- suse/releases.go | 88 ------------ suse/suse.go | 79 +---------- suse/updaterset.go | 31 ---- test/periodic/updater_test.go | 8 +- updater/defaults/defaults.go | 2 +- 13 files changed, 377 insertions(+), 295 deletions(-) create mode 100644 suse/factory.go create mode 100644 suse/factory_test.go delete mode 100644 suse/releases.go delete mode 100644 suse/updaterset.go diff --git a/go.mod b/go.mod index e82684fc6..8e83564fa 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( go.opentelemetry.io/otel/trace v1.31.0 go.uber.org/mock v0.5.0 golang.org/x/crypto v0.28.0 + golang.org/x/net v0.30.0 golang.org/x/sync v0.8.0 golang.org/x/sys v0.26.0 golang.org/x/text v0.19.0 diff --git a/go.sum b/go.sum index aee7a3994..7811aae2b 100644 --- a/go.sum +++ b/go.sum @@ -229,6 +229,8 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= diff --git a/pkg/ovalutil/pool.go b/pkg/ovalutil/pool.go index 822092aee..e6d30bfa7 100644 --- a/pkg/ovalutil/pool.go +++ b/pkg/ovalutil/pool.go @@ -9,15 +9,20 @@ import ( ) var ( - gzipPool sync.Pool - zstdPool sync.Pool + gzipPool = sync.Pool{ + New: func() any { + return new(gzip.Reader) + }, + } + zstdPool = sync.Pool{ + New: func() any { + return new(zstd.Decoder) + }, + } ) func getGzip(r io.Reader) (*gzip.Reader, error) { z := gzipPool.Get().(*gzip.Reader) - if z == nil { - return gzip.NewReader(r) - } if err := z.Reset(r); err != nil { return nil, err } @@ -30,9 +35,6 @@ func putGzip(z *gzip.Reader) { func getZstd(r io.Reader) (*zstd.Decoder, error) { z := zstdPool.Get().(*zstd.Decoder) - if z == nil { - return zstd.NewReader(r) - } if err := z.Reset(r); err != nil { return nil, err } diff --git a/suse/distributionscanner.go b/suse/distributionscanner.go index d038fa54c..c49efdebc 100644 --- a/suse/distributionscanner.go +++ b/suse/distributionscanner.go @@ -3,13 +3,16 @@ package suse import ( "bytes" "context" - "regexp" + "fmt" "runtime/trace" + "github.com/Masterminds/semver" "github.com/quay/zlog" "github.com/quay/claircore" "github.com/quay/claircore/indexer" + "github.com/quay/claircore/osrelease" + "github.com/quay/claircore/pkg/cpe" ) // Suse Enterprise Server has service pack releases however their security database files are bundled together @@ -29,38 +32,12 @@ const ( suseReleasePath = `etc/SuSE-release` ) -type suseRegex struct { - release Release - regexp *regexp.Regexp -} +type suseType string -var suseRegexes = []suseRegex{ - { - release: EnterpriseServer15, - // regex for /etc/issue - regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 15`), - }, - { - release: EnterpriseServer12, - regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 12`), - }, - { - release: EnterpriseServer11, - regexp: regexp.MustCompile(`(?i)SUSE Linux Enterprise Server 11`), - }, - { - release: Leap151, - regexp: regexp.MustCompile(`(?i)openSUSE Leap 15.1`), - }, - { - release: Leap150, - regexp: regexp.MustCompile(`(?i)openSUSE Leap 15.0`), - }, - { - release: Leap423, - regexp: regexp.MustCompile(`(?i)openSUSE Leap 42.3`), - }, -} +var ( + SLES suseType = "sles" + LEAP suseType = "leap" +) var ( _ indexer.DistributionScanner = (*DistributionScanner)(nil) @@ -99,7 +76,7 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] return nil, nil } for _, buff := range files { - dist := ds.parse(buff) + dist := ds.parse(ctx, buff) if dist != nil { return []*claircore.Distribution{dist}, nil } @@ -111,11 +88,46 @@ func (ds *DistributionScanner) Scan(ctx context.Context, l *claircore.Layer) ([] // distribution if it exists. // // separated into its own method to aid testing. -func (ds *DistributionScanner) parse(buff *bytes.Buffer) *claircore.Distribution { - for _, ur := range suseRegexes { - if ur.regexp.Match(buff.Bytes()) { - return releaseToDist(ur.release) +func (ds *DistributionScanner) parse(ctx context.Context, buff *bytes.Buffer) *claircore.Distribution { + kv, err := osrelease.Parse(ctx, buff) + if err != nil { + zlog.Warn(ctx).Err(err).Msg("malformed os-release file") + } + cpeName, cpeOK := kv["CPE_NAME"] + if !cpeOK { + return nil + } + // Instead of regexing through, we can grab the CPE. + c, err := cpe.Unbind(cpeName) + if err != nil { + zlog.Warn(ctx).Err(err).Msg("could not unbind CPE") + return nil + } + + d, err := cpeToDist(c) + if err != nil { + zlog.Warn(ctx).Err(err).Msg("error converting cpe to distribution") + return nil + } + + return d +} + +func cpeToDist(r cpe.WFN) (*claircore.Distribution, error) { + if vendor, err := cpe.NewValue("opensuse"); err == nil && r.Attr[cpe.Vendor] == vendor { + if prod, err := cpe.NewValue("leap"); err == nil && r.Attr[cpe.Product] == prod { + return mkLeapDist(r.String(), r.Attr[cpe.Version].String()), nil + } + } + if vendor, err := cpe.NewValue("suse"); err == nil && r.Attr[cpe.Vendor] == vendor { + if prod, err := cpe.NewValue("sles"); err == nil && r.Attr[cpe.Product] == prod { + // Canonicalize the version to the major. + v, err := semver.NewVersion(r.Attr[cpe.Version].String()) + if err != nil { + return nil, err + } + return mkELDist(r.String(), fmt.Sprint(v.Major())), nil } } - return nil + return nil, nil } diff --git a/suse/distributionscanner_test.go b/suse/distributionscanner_test.go index b5f515fdd..0fdd01119 100644 --- a/suse/distributionscanner_test.go +++ b/suse/distributionscanner_test.go @@ -2,9 +2,11 @@ package suse import ( "bytes" + "context" "testing" "github.com/google/go-cmp/cmp" + "github.com/quay/claircore" ) var enterpriseServer15OSRelease []byte = []byte(`NAME="SLES" @@ -16,7 +18,7 @@ ID_LIKE="suse" ANSI_COLOR="0;32" CPE_NAME="cpe:/o:suse:sles:15:sp1"`) -var enterpriseServer12OSRelase []byte = []byte(`NAME="SLES" +var enterpriseServer12OSRelease []byte = []byte(`NAME="SLES" VERSION="12-SP5" VERSION_ID="12.5" PRETTY_NAME="SUSE Linux Enterprise Server 12 SP5" @@ -24,25 +26,6 @@ ID="sles" ANSI_COLOR="0;32" CPE_NAME="cpe:/o:suse:sles:12:sp5"`) -var enterpriseServer11OSRelease []byte = []byte(`NAME="SLES" -VERSION="11-SP5" -VERSION_ID="11.2" -PRETTY_NAME="SUSE Linux Enterprise Server 11 SP5" -ID="sles" -ANSI_COLOR="0;32" -CPE_NAME="cpe:/o:suse:sles:12:sp5"`) - -var leap151OSRelease []byte = []byte(`NAME="openSUSE Leap" -VERSION="15.1" -ID="opensuse-leap" -ID_LIKE="suse opensuse" -VERSION_ID="15.1" -PRETTY_NAME="openSUSE Leap 15.1" -ANSI_COLOR="0;32" -CPE_NAME="cpe:/o:opensuse:leap:15.1" -BUG_REPORT_URL="https://bugs.opensuse.org" -HOME_URL="https://www.opensuse.org/"`) - var leap15OSRelease []byte = []byte(`NAME="openSUSE Leap" VERSION="15.0" ID="opensuse-leap" @@ -54,60 +37,75 @@ CPE_NAME="cpe:/o:opensuse:leap:15.0" BUG_REPORT_URL="https://bugs.opensuse.org" HOME_URL="https://www.opensuse.org/"`) -var leap423OSRelease []byte = []byte(`NAME="openSUSE Leap" -VERSION="42.3" -ID=opensuse -ID_LIKE="suse" -VERSION_ID="42.3" -PRETTY_NAME="openSUSE Leap 42.3" +var leap151OSRelease []byte = []byte(`NAME="openSUSE Leap" +VERSION="15.1" +ID="opensuse-leap" +ID_LIKE="suse opensuse" +VERSION_ID="15.1" +PRETTY_NAME="openSUSE Leap 15.1" ANSI_COLOR="0;32" -CPE_NAME="cpe:/o:opensuse:leap:42.3" +CPE_NAME="cpe:/o:opensuse:leap:15.1" BUG_REPORT_URL="https://bugs.opensuse.org" HOME_URL="https://www.opensuse.org/"`) func TestDistributionScanner(t *testing.T) { + ctx := context.Background() table := []struct { name string - release Release + dist *claircore.Distribution osRelease []byte }{ { name: "enterprise server 15", - release: EnterpriseServer15, osRelease: enterpriseServer15OSRelease, + dist: &claircore.Distribution{ + DID: "sles", + Name: "SLES", + Version: "15", + VersionID: "15", + PrettyName: "SUSE Linux Enterprise Server 15", + }, }, { name: "enterprise server 12", - release: EnterpriseServer12, - osRelease: enterpriseServer12OSRelase, - }, - { - name: "enterprise server 11", - release: EnterpriseServer11, - osRelease: enterpriseServer11OSRelease, + osRelease: enterpriseServer12OSRelease, + dist: &claircore.Distribution{ + DID: "sles", + Name: "SLES", + Version: "12", + VersionID: "12", + PrettyName: "SUSE Linux Enterprise Server 12", + }, }, { name: "leap 15.0", - release: Leap150, osRelease: leap15OSRelease, + dist: &claircore.Distribution{ + DID: "opensuse-leap", + Name: "openSUSE Leap", + Version: "15.0", + VersionID: "15.0", + PrettyName: "openSUSE Leap 15.0", + }, }, { name: "leap 15.1", - release: Leap151, osRelease: leap151OSRelease, - }, - { - name: "leap 42.3", - release: Leap423, - osRelease: leap423OSRelease, + dist: &claircore.Distribution{ + DID: "opensuse-leap", + Name: "openSUSE Leap", + Version: "15.1", + VersionID: "15.1", + PrettyName: "openSUSE Leap 15.1", + }, }, } for _, tt := range table { t.Run(tt.name, func(t *testing.T) { scanner := DistributionScanner{} - dist := scanner.parse(bytes.NewBuffer(tt.osRelease)) - if !cmp.Equal(dist, releaseToDist(tt.release)) { - t.Fatalf("%v", cmp.Diff(dist, releaseToDist(tt.release))) + dist := scanner.parse(ctx, bytes.NewBuffer(tt.osRelease)) + if !cmp.Equal(dist, tt.dist) { + t.Fatalf("%v", cmp.Diff(dist, tt.dist)) } }) } diff --git a/suse/factory.go b/suse/factory.go new file mode 100644 index 000000000..e87b4921f --- /dev/null +++ b/suse/factory.go @@ -0,0 +1,234 @@ +package suse + +import ( + "context" + "fmt" + "log" + "net/http" + "net/url" + "regexp" + "strings" + "sync" + + "github.com/Masterminds/semver" + "github.com/quay/claircore" + "github.com/quay/claircore/libvuln/driver" + "github.com/quay/claircore/pkg/ovalutil" + "github.com/quay/zlog" + "golang.org/x/net/html" +) + +//doc:url updater +const base = `https://ftp.suse.com/pub/projects/security/oval/` + +var ( + reELFile = regexp.MustCompile(`suse.linux.enterprise.server.([1-9][1-9]).xml.gz`) + // This regex is specific enough to exclude 4x.x releases, it will need to be + // revisited if LEAP gets to 20 and above. + reLeapFile = regexp.MustCompile(`opensuse.leap.(1[0-9].[0-9]+).xml.gz`) + + minimumLEAP = semver.MustParse("15.5") +) + +type Factory struct { + c *http.Client + base *url.URL +} + +// UpdaterSet implements [driver.UpdaterSetFactory]. +func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { + ctx = zlog.ContextWithValues(ctx, "component", "suse/Factory.UpdaterSet") + us := driver.NewUpdaterSet() + if f.c == nil { + zlog.Info(ctx). + Msg("unconfigured") + return us, nil + } + req, err := http.NewRequestWithContext(ctx, http.MethodGet, f.base.String(), nil) + if err != nil { + return us, fmt.Errorf("suse: unable to construct request: %w", err) + } + zlog.Debug(ctx). + Stringer("url", f.base). + Msg("making request") + res, err := f.c.Do(req) + if err != nil { + return us, fmt.Errorf("suse: error requesting %q: %w", f.base.String(), err) + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK { + return us, fmt.Errorf("suse: unexpected status requesting OVAL dir: %v", res.Status) + } + + // It's kind of gross having to parse the HTML but the other path, + // parsing the SHA256SUMs, is more fragile. At least this way we + // know the file exists with the expected compression. + dir, err := html.Parse(res.Body) + if err != nil { + log.Fatal(err) + } + + ups := []*Updater{} + err = f.createUpdater(&ups, dir) + if err != nil { + return us, fmt.Errorf("suse: problems processing OVAL directory: %w", err) + } + for _, u := range ups { + err := us.Add(u) + if err != nil { + return us, err + } + } + return us, nil +} + +func (f *Factory) createUpdater(ups *[]*Updater, n *html.Node) error { + if n.Type == html.ElementNode && n.Data == "a" { + for _, a := range n.Attr { + if a.Key == "href" { + var d *claircore.Distribution + if parts := reELFile.FindAllStringSubmatch(a.Val, -1); len(parts) == 1 { + d = mkELDist(a.Val, parts[0][1]) + } + if parts := reLeapFile.FindAllStringSubmatch(a.Val, -1); len(parts) == 1 { + ver := parts[0][1] + sv, err := semver.NewVersion(ver) + if err != nil { + continue + } + if sv.Compare(minimumLEAP) > -1 { + d = mkLeapDist(a.Val, ver) + } + } + if d == nil { + continue + } + uri, err := f.base.Parse(a.Val) + if err != nil { + return fmt.Errorf("unable to construct request for %q: %w", a.Val, err) + } + u, err := NewUpdater(d, WithURL(uri.String(), "gz")) + if err != nil { + return fmt.Errorf("failed to parse uri %q: %w", uri, err) + } + *ups = append(*ups, u) + } + } + } + for c := n.FirstChild; c != nil; c = c.NextSibling { + err := f.createUpdater(ups, c) + if err != nil { + return err + } + } + return nil +} + +var releases sync.Map + +func mkELDist(oURL, ver string) *claircore.Distribution { + name := strings.TrimSuffix(oURL, ".xml.gz") + v, _ := releases.LoadOrStore(name, &claircore.Distribution{ + Name: "SLES", + DID: "sles", + Version: ver, + VersionID: ver, + PrettyName: "SUSE Linux Enterprise Server " + ver, + }) + return v.(*claircore.Distribution) +} + +func mkLeapDist(oURL, ver string) *claircore.Distribution { + name := strings.TrimSuffix(oURL, ".xml.gz") + v, _ := releases.LoadOrStore(name, &claircore.Distribution{ + Name: "openSUSE Leap", + DID: "opensuse-leap", + Version: ver, + VersionID: ver, + PrettyName: "openSUSE Leap " + ver, + }) + return v.(*claircore.Distribution) +} + +// FactoryConfig is the configuration accepted by the Factory. +type FactoryConfig struct { + // URL indicates the base URL for the SecDB layout. It should have a trailing slash. + URL string `json:"url" yaml:"url"` +} + +// Configure implements driver.Configurable. +func (f *Factory) Configure(ctx context.Context, cf driver.ConfigUnmarshaler, c *http.Client) error { + f.c = c + var cfg FactoryConfig + if err := cf(&cfg); err != nil { + return err + } + var err error + u := base + if cfg.URL != "" { + u = cfg.URL + if !strings.HasSuffix(u, "/") { + u += "/" + } + } + f.base, err = url.Parse(u) + if err != nil { + return err + } + return nil +} + +// Updater implements driver.Updater for SUSE. +type Updater struct { + d *claircore.Distribution + u *url.URL + ovalutil.Fetcher // promoted Fetch method +} + +var ( + _ driver.Updater = (*Updater)(nil) + _ driver.Fetcher = (*Updater)(nil) + _ driver.Configurable = (*Updater)(nil) +) + +// NewUpdater configures an updater to fetch the specified Release. +func NewUpdater(d *claircore.Distribution, opts ...Option) (*Updater, error) { + u := &Updater{ + d: d, + } + for _, o := range opts { + if err := o(u); err != nil { + return nil, err + } + } + if u.Fetcher.URL == nil { + u.Fetcher.URL = u.u + } + return u, nil +} + +// Option configures an Updater. +type Option func(*Updater) error + +// WithURL overrides the default URL to fetch an OVAL database. +func WithURL(uri, compression string) Option { + c, cerr := ovalutil.ParseCompressor(compression) + u, uerr := url.Parse(uri) + return func(up *Updater) error { + // Return any errors from the outer function. + switch { + case cerr != nil: + return cerr + case uerr != nil: + return uerr + } + up.Fetcher.Compression = c + up.Fetcher.URL = u + return nil + } +} + +// Name satisfies driver.Updater. +func (u *Updater) Name() string { + return fmt.Sprintf(`suse-updater-%s`, strings.ReplaceAll(strings.ToLower(u.d.PrettyName), " ", ".")) +} diff --git a/suse/factory_test.go b/suse/factory_test.go new file mode 100644 index 000000000..a595454a4 --- /dev/null +++ b/suse/factory_test.go @@ -0,0 +1,24 @@ +package suse + +import ( + "context" + "net/http" + "net/url" + "testing" +) + +func TestFactory(t *testing.T) { + ctx := context.Background() + u, _ := url.Parse("https://ftp.suse.com/pub/projects/security/oval/") + f := &Factory{ + c: http.DefaultClient, + base: u, + } + us, err := f.UpdaterSet(ctx) + if err != nil { + t.Fatal(err) + } + for _, u := range us.Updaters() { + t.Log(u.Name()) + } +} diff --git a/suse/parser.go b/suse/parser.go index b2d193c6d..e53cde0e0 100644 --- a/suse/parser.go +++ b/suse/parser.go @@ -31,7 +31,7 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln zlog.Debug(ctx).Msg("xml decoded") protoVulns := func(def oval.Definition) ([]*claircore.Vulnerability, error) { return []*claircore.Vulnerability{ - &claircore.Vulnerability{ + { Updater: u.Name(), Name: def.Title, Description: def.Description, @@ -39,9 +39,8 @@ func (u *Updater) Parse(ctx context.Context, r io.ReadCloser) ([]*claircore.Vuln Severity: def.Advisory.Severity, NormalizedSeverity: NormalizeSeverity(def.Advisory.Severity), // each updater is configured to parse a suse release - // specific xml database. we'll use the updater's release - // to map the parsed vulnerabilities - Dist: releaseToDist(u.release), + // specific xml database. + Dist: u.d, }, }, nil } diff --git a/suse/releases.go b/suse/releases.go deleted file mode 100644 index dc50fe3d6..000000000 --- a/suse/releases.go +++ /dev/null @@ -1,88 +0,0 @@ -package suse - -import "github.com/quay/claircore" - -// Suse has service pack releases however their security database files are bundled together -// by major version. for example `SUSE Linux Enterprise Server 15 (all Service Packs) - suse.linux.enterprise.server.15.xml` -// we choose to normalize detected distributions into major releases and parse vulnerabilities by major release versions. - -// Release indicates the SUSE release OVAL database to pull from. -type Release string - -// These are some known Releases. -const ( - EnterpriseServer15 Release = `suse.linux.enterprise.server.15` - EnterpriseServer12 Release = `suse.linux.enterprise.server.12` - EnterpriseServer11 Release = `suse.linux.enterprise.server.11` - Leap151 Release = `opensuse.leap.15.1` - Leap150 Release = `opensuse.leap.15.0` - Leap423 Release = `opensuse.leap.42.3` -) - -var enterpriseServer15Dist = &claircore.Distribution{ - Name: "SLES", - Version: "15", - VersionID: "15", - PrettyName: "SUSE Linux Enterprise Server 15", - DID: "sles", -} - -var enterpriseServer12Dist = &claircore.Distribution{ - Name: "SLES", - Version: "12", - VersionID: "12", - PrettyName: "SUSE Linux Enterprise Server 12", - DID: "sles", -} - -var enterpriseServer11Dist = &claircore.Distribution{ - Name: "SLES", - Version: "11", - VersionID: "11", - PrettyName: "SUSE Linux Enterprise Server 11", - DID: "sles", -} - -var leap151Dist = &claircore.Distribution{ - Name: "openSUSE Leap", - Version: "15.1", - DID: "opensuse-leap", - VersionID: "15.1", - PrettyName: "openSUSE Leap 15.1", -} - -var leap15Dist = &claircore.Distribution{ - Name: "openSUSE Leap", - Version: "15.0", - DID: "opensuse-leap", - VersionID: "15.0", - PrettyName: "openSUSE Leap 15.0", -} - -var leap423Dist = &claircore.Distribution{ - Name: "openSUSE Leap", - Version: "42.3", - DID: "opensuse", - VersionID: "42.3", - PrettyName: "openSUSE Leap 42.3", -} - -func releaseToDist(r Release) *claircore.Distribution { - switch r { - case EnterpriseServer15: - return enterpriseServer15Dist - case EnterpriseServer12: - return enterpriseServer12Dist - case EnterpriseServer11: - return enterpriseServer11Dist - case Leap150: - return leap15Dist - case Leap151: - return leap151Dist - case Leap423: - return leap423Dist - default: - // return empty dist - return &claircore.Distribution{} - } -} diff --git a/suse/suse.go b/suse/suse.go index 43d041c52..ab5a43ad0 100644 --- a/suse/suse.go +++ b/suse/suse.go @@ -1,79 +1,2 @@ +// Package suse contains an Indexer, Matcher, and Updater for SUSE Linux and OpenSUSE. package suse - -import ( - "fmt" - "net/url" - - "github.com/quay/claircore/libvuln/driver" - "github.com/quay/claircore/pkg/ovalutil" -) - -var upstreamBase *url.URL - -func init() { - //doc:url updater - const base = `https://support.novell.com/security/oval/` - var err error - upstreamBase, err = url.Parse(base) - if err != nil { - panic("static url somehow didn't parse") - } -} - -// Updater implements driver.Updater for SUSE. -type Updater struct { - release Release - ovalutil.Fetcher // promoted Fetch method -} - -var ( - _ driver.Updater = (*Updater)(nil) - _ driver.Fetcher = (*Updater)(nil) - _ driver.Configurable = (*Updater)(nil) -) - -// NewUpdater configures an updater to fetch the specified Release. -func NewUpdater(r Release, opts ...Option) (*Updater, error) { - u := &Updater{ - release: r, - } - for _, o := range opts { - if err := o(u); err != nil { - return nil, err - } - } - if u.Fetcher.URL == nil { - var err error - u.Fetcher.URL, err = upstreamBase.Parse(string(u.release) + ".xml") - if err != nil { - return nil, err - } - } - return u, nil -} - -// Option configures an Updater. -type Option func(*Updater) error - -// WithURL overrides the default URL to fetch an OVAL database. -func WithURL(uri, compression string) Option { - c, cerr := ovalutil.ParseCompressor(compression) - u, uerr := url.Parse(uri) - return func(up *Updater) error { - // Return any errors from the outer function. - switch { - case cerr != nil: - return cerr - case uerr != nil: - return uerr - } - up.Fetcher.Compression = c - up.Fetcher.URL = u - return nil - } -} - -// Name satisfies driver.Updater. -func (u *Updater) Name() string { - return fmt.Sprintf(`suse-updater-%s`, u.release) -} diff --git a/suse/updaterset.go b/suse/updaterset.go deleted file mode 100644 index 7207d21e2..000000000 --- a/suse/updaterset.go +++ /dev/null @@ -1,31 +0,0 @@ -package suse - -import ( - "context" - "fmt" - - "github.com/quay/claircore/libvuln/driver" -) - -var suseReleases = []Release{ - EnterpriseServer15, - EnterpriseServer12, - EnterpriseServer11, - Leap150, - Leap151, -} - -func UpdaterSet(_ context.Context) (driver.UpdaterSet, error) { - us := driver.NewUpdaterSet() - for _, release := range suseReleases { - u, err := NewUpdater(release) - if err != nil { - return us, fmt.Errorf("failed to create updater: %v", err) - } - err = us.Add(u) - if err != nil { - return us, err - } - } - return us, nil -} diff --git a/test/periodic/updater_test.go b/test/periodic/updater_test.go index a94ba039f..1cab274ff 100644 --- a/test/periodic/updater_test.go +++ b/test/periodic/updater_test.go @@ -105,7 +105,13 @@ func TestRHEL(t *testing.T) { func TestSUSE(t *testing.T) { ctx := zlog.Test(context.Background(), t) - set, err := suse.UpdaterSet(ctx) + fac := new(suse.Factory) + err := fac.Configure(ctx, noopConfigure, pkgClient) + if err != nil { + t.Fatal(err) + } + + set, err := fac.UpdaterSet(ctx) if err != nil { t.Fatal() } diff --git a/updater/defaults/defaults.go b/updater/defaults/defaults.go index a03bc8f2c..deb27de2d 100644 --- a/updater/defaults/defaults.go +++ b/updater/defaults/defaults.go @@ -62,7 +62,7 @@ func inner(ctx context.Context) error { updater.Register("aws", driver.UpdaterSetFactoryFunc(aws.UpdaterSet)) updater.Register("oracle", driver.UpdaterSetFactoryFunc(oracle.UpdaterSet)) updater.Register("photon", driver.UpdaterSetFactoryFunc(photon.UpdaterSet)) - updater.Register("suse", driver.UpdaterSetFactoryFunc(suse.UpdaterSet)) + updater.Register("suse", new(suse.Factory)) updater.Register("rhcc", driver.UpdaterSetFactoryFunc(rhcc.UpdaterSet)) cvssSet := driver.NewUpdaterSet()