1+ // Package epss provides a epss enricher.
12package epss
23
34import (
@@ -16,6 +17,7 @@ import (
1617 "strings"
1718 "time"
1819
20+ "github.com/google/uuid"
1921 "github.com/quay/zlog"
2022
2123 "github.com/quay/claircore"
@@ -42,7 +44,7 @@ type EPSSItem struct {
4244
4345const (
4446 // Type is the type of data returned from the Enricher's Enrich method.
45- Type = `message/vnd.clair.map.vulnerability; enricher=clair.epss schema=https://csrc.nist.gov/schema/nvd/baseURL/1.1/cvss-v3.x.json `
47+ Type = `message/vnd.clair.map.vulnerability; enricher=clair.epss schema=none `
4648
4749 // DefaultBaseURL is the default place to look for EPSS feeds.
4850 // epss_scores-YYYY-MM-DD.csv.gz needs to be specified to get all data
@@ -64,7 +66,7 @@ type Enricher struct {
6466
6567// Config is the configuration for Enricher.
6668type Config struct {
67- BaseURL * string `json:"url" yaml:"url"`
69+ URL * string `json:"url" yaml:"url"`
6870}
6971
7072func (e * Enricher ) Configure (ctx context.Context , f driver.ConfigUnmarshaler , c * http.Client ) error {
@@ -73,24 +75,23 @@ func (e *Enricher) Configure(ctx context.Context, f driver.ConfigUnmarshaler, c
7375 e .c = c
7476 e .feedPath = currentFeedURL ()
7577 if f == nil {
76- zlog .Debug (ctx ).Msg ("No configuration provided; proceeding with default settings" )
77- return nil
78+ return fmt .Errorf ("configuration is nil" )
7879 }
7980 if err := f (& cfg ); err != nil {
8081 return err
8182 }
82- if cfg .BaseURL != nil {
83+ if cfg .URL != nil {
8384 // validate the URL format
84- if _ , err := url .Parse (* cfg .BaseURL ); err != nil {
85- return fmt .Errorf ("invalid URL format for BaseURL : %w" , err )
85+ if _ , err := url .Parse (* cfg .URL ); err != nil {
86+ return fmt .Errorf ("invalid URL format for URL : %w" , err )
8687 }
8788
8889 // only .gz file is supported
89- if strings .HasSuffix (* cfg .BaseURL , ".gz" ) {
90+ if strings .HasSuffix (* cfg .URL , ".gz" ) {
9091 //overwrite feedPath is cfg provides another baseURL path
91- e .feedPath = * cfg .BaseURL
92+ e .feedPath = * cfg .URL
9293 } else {
93- return fmt .Errorf ("invalid baseURL root: expected a '.gz' file, but got '%q'" , * cfg .BaseURL )
94+ return fmt .Errorf ("invalid baseURL root: expected a '.gz' file, but got '%q'" , * cfg .URL )
9495 }
9596 }
9697
@@ -128,14 +129,19 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F
128129 if err = httputil .CheckResponse (resp , http .StatusOK ); err != nil {
129130 return nil , "" , fmt .Errorf ("unable to fetch file: %w" , err )
130131 }
131-
132- etag := resp .Header .Get ("etag" )
133- if etag == "" {
134- return nil , "" , fmt .Errorf ("ETag not found in response headers" )
132+ var str string
133+ var newFingerprint driver.Fingerprint
134+ str = resp .Header .Get ("etag" )
135+ if str == "" {
136+ newUUID , err := uuid .NewRandom ()
137+ if err != nil {
138+ return nil , "" , fmt .Errorf ("failed to generate UUID: %w" , err )
139+ }
140+ // Generate a UUID for customized URL
141+ str = newUUID .String ()
142+ zlog .Warn (ctx ).Msg ("ETag not found; generated UUID for fingerprint" )
135143 }
136-
137- newFingerprint := driver .Fingerprint (etag )
138-
144+ newFingerprint = driver .Fingerprint (str )
139145 if prevFingerprint == newFingerprint {
140146 zlog .Info (ctx ).Str ("fingerprint" , string (newFingerprint )).Msg ("file unchanged; skipping processing" )
141147 return nil , prevFingerprint , nil
@@ -158,7 +164,6 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F
158164
159165 var modelVersion , date string
160166 for _ , field := range record {
161- field = strings .TrimSpace (field )
162167 field = strings .TrimPrefix (strings .TrimSpace (field ), "#" )
163168 key , value , found := strings .Cut (field , ":" )
164169 if ! found {
@@ -176,11 +181,8 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F
176181 return nil , "" , fmt .Errorf ("missing metadata fields in record: %v" , record )
177182 }
178183 csvReader .Comment = '#'
179- csvReader .FieldsPerRecord = 3 // Expect exactly 3 fields per record
180184
181- if modelVersion == "" || date == "" {
182- return nil , "" , fmt .Errorf ("missing metadata fields in record: %v" , record )
183- }
185+ csvReader .FieldsPerRecord = 3 // Expect exactly 3 fields per record
184186
185187 // Read and validate header line
186188 record , err = csvReader .Read ()
@@ -230,9 +232,7 @@ func (e *Enricher) FetchEnrichment(ctx context.Context, prevFingerprint driver.F
230232func (e * Enricher ) ParseEnrichment (ctx context.Context , rc io.ReadCloser ) ([]driver.EnrichmentRecord , error ) {
231233 ctx = zlog .ContextWithValues (ctx , "component" , "enricher/epss/Enricher/ParseEnrichment" )
232234
233- defer func () {
234- _ = rc .Close ()
235- }()
235+ defer rc .Close ()
236236
237237 dec := json .NewDecoder (rc )
238238 ret := make ([]driver.EnrichmentRecord , 0 , 250_000 )
@@ -262,8 +262,8 @@ func (*Enricher) Name() string {
262262}
263263
264264func currentFeedURL () string {
265- currentDate := time .Now ()
266- formattedDate := currentDate .Format ("2006-01-02" )
265+ yesterday := time .Now (). AddDate ( 0 , 0 , - 1 ) // Get yesterday's date
266+ formattedDate := yesterday .Format ("2006-01-02" )
267267 filePath := fmt .Sprintf ("epss_scores-%s.csv.gz" , formattedDate )
268268
269269 feedURL , err := url .Parse (DefaultBaseURL )
@@ -354,7 +354,7 @@ func (e *Enricher) Enrich(ctx context.Context, g driver.EnrichmentGetter, r *cla
354354}
355355
356356func newItemFeed (record []string , modelVersion string , scoreDate string ) (driver.EnrichmentRecord , error ) {
357- // Assuming record has already been validated to have 3 fields
357+ // Validate the record has the expected length
358358 if len (record ) != 3 {
359359 return driver.EnrichmentRecord {}, fmt .Errorf ("unexpected record length: %d" , len (record ))
360360 }
0 commit comments