github.com/quay/claircore@v1.5.28/rhel/updaterset.go (about) 1 package rhel 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "net/url" 8 "regexp" 9 "strconv" 10 "strings" 11 12 "github.com/quay/zlog" 13 14 "github.com/quay/claircore/libvuln/driver" 15 "github.com/quay/claircore/rhel/internal/pulp" 16 ) 17 18 // DefaultManifest is the url for the Red Hat OVAL pulp repository. 19 // 20 //doc:url updater 21 const DefaultManifest = `https://access.redhat.com/security/data/oval/v2/PULP_MANIFEST` 22 23 // NewFactory creates a Factory making updaters based on the contents of the 24 // provided pulp manifest. 25 func NewFactory(_ context.Context, manifest string) (*Factory, error) { 26 var err error 27 var f Factory 28 f.url, err = url.Parse(manifest) 29 if err != nil { 30 return nil, err 31 } 32 33 return &f, nil 34 } 35 36 // Factory contains the configuration for fetching and parsing a Pulp manifest. 37 type Factory struct { 38 url *url.URL 39 client *http.Client 40 manifestEtag string 41 ignoreUnpatched bool 42 } 43 44 // FactoryConfig is the configuration accepted by the rhel updaters. 45 // 46 // By convention, this should be in a map called "rhel". 47 type FactoryConfig struct { 48 URL string `json:"url" yaml:"url"` 49 // IgnoreUnpatched dictates whether to ingest unpatched advisory data 50 // from the RHEL security feeds. 51 IgnoreUnpatched bool `json:"ignore_unpatched" yaml:"ignore_unpatched"` 52 } 53 54 var _ driver.Configurable = (*Factory)(nil) 55 56 // Configure implements [driver.Configurable]. 57 func (f *Factory) Configure(ctx context.Context, cfg driver.ConfigUnmarshaler, c *http.Client) error { 58 ctx = zlog.ContextWithValues(ctx, "component", "rhel/Factory.Configure") 59 var fc FactoryConfig 60 61 if err := cfg(&fc); err != nil { 62 return err 63 } 64 zlog.Debug(ctx).Msg("loaded incoming config") 65 66 if fc.URL != "" { 67 u, err := url.Parse(fc.URL) 68 if err != nil { 69 return err 70 } 71 zlog.Info(ctx). 72 Stringer("url", u). 73 Msg("configured manifest URL") 74 f.url = u 75 } 76 77 if c != nil { 78 zlog.Info(ctx). 79 Msg("configured HTTP client") 80 f.client = c 81 } 82 f.ignoreUnpatched = fc.IgnoreUnpatched 83 return nil 84 } 85 86 // UpdaterSet implements [driver.UpdaterSetFactory]. 87 // 88 // The returned Updaters determine the [claircore.Distribution] it's associated 89 // with based on the path in the Pulp manifest. 90 func (f *Factory) UpdaterSet(ctx context.Context) (driver.UpdaterSet, error) { 91 s := driver.NewUpdaterSet() 92 93 req, err := http.NewRequestWithContext(ctx, http.MethodGet, f.url.String(), nil) 94 if err != nil { 95 return s, err 96 } 97 if f.manifestEtag != "" { 98 req.Header.Set("if-none-match", f.manifestEtag) 99 } 100 101 res, err := f.client.Do(req) 102 if res != nil { 103 defer res.Body.Close() 104 } 105 if err != nil { 106 return s, err 107 } 108 109 switch res.StatusCode { 110 case http.StatusOK: 111 if t := f.manifestEtag; t == "" || t != res.Header.Get("etag") { 112 break 113 } 114 fallthrough 115 case http.StatusNotModified: 116 // return stub updater to allow us to record that all rhel updaters are up to date 117 stubUpdater := Updater{name: "rhel-all"} 118 s.Add(&stubUpdater) 119 return s, nil 120 default: 121 return s, fmt.Errorf("unexpected response: %v", res.Status) 122 } 123 124 m := pulp.Manifest{} 125 if err := m.Load(res.Body); err != nil { 126 return s, err 127 } 128 129 for _, e := range m { 130 name := strings.TrimSuffix(strings.Replace(e.Path, "/", "-", -1), ".oval.xml.bz2") 131 // We need to disregard this OVAL stream because some advisories therein have 132 // been released with the CPEs identical to those used in classic RHEL stream. 133 // This in turn causes false CVEs to appear in scanned images. Red Hat Product 134 // Security is working on fixing this situation and the plan is to remove this 135 // exception in the future. 136 if name == "RHEL7-rhel-7-alt" { 137 continue 138 } 139 uri, err := f.url.Parse(e.Path) 140 if err != nil { 141 return s, err 142 } 143 m := guessFromPath.FindStringSubmatch(uri.Path) 144 if m == nil { 145 continue 146 } 147 r, err := strconv.Atoi(m[1]) 148 if err != nil { 149 zlog.Info(ctx). 150 Err(err). 151 Str("path", uri.Path). 152 Msg("unable to parse pattern into int") 153 continue 154 } 155 up, err := NewUpdater(name, r, uri.String(), f.ignoreUnpatched) 156 if err != nil { 157 return s, err 158 } 159 _ = s.Add(up) 160 } 161 f.manifestEtag = res.Header.Get("etag") 162 163 return s, nil 164 } 165 166 var guessFromPath = regexp.MustCompile(`RHEL([0-9]+)`)