diff --git a/csaf/advisories.go b/csaf/advisories.go index 5286d23..26ff850 100644 --- a/csaf/advisories.go +++ b/csaf/advisories.go @@ -9,7 +9,9 @@ package csaf import ( - "bufio" + "encoding/csv" + "fmt" + "io" "log" "net/http" "net/url" @@ -173,7 +175,8 @@ func (afp *AdvisoryFileProcessor) Process( continue } - files, err := afp.loadIndex(base, lg) + // Use changes.csv to be able to filter by age. + files, err := afp.loadChanges(base, lg) if err != nil { return err } @@ -186,9 +189,9 @@ func (afp *AdvisoryFileProcessor) Process( return nil } -// loadIndex loads baseURL/index.txt and returns a list of files +// loadChanges loads baseURL/changes.csv and returns a list of files // prefixed by baseURL/. -func (afp *AdvisoryFileProcessor) loadIndex( +func (afp *AdvisoryFileProcessor) loadChanges( baseURL string, lg func(string, ...any), ) ([]AdvisoryFile, error) { @@ -197,29 +200,53 @@ func (afp *AdvisoryFileProcessor) loadIndex( if err != nil { return nil, err } + changesURL := base.JoinPath("changes.csv").String() - indexURL := base.JoinPath("index.txt").String() - resp, err := afp.client.Get(indexURL) + resp, err := afp.client.Get(changesURL) if err != nil { return nil, err } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("fetching %s failed. Status code %d (%s)", + changesURL, resp.StatusCode, resp.Status) + } + defer resp.Body.Close() var files []AdvisoryFile - - scanner := bufio.NewScanner(resp.Body) - - for line := 1; scanner.Scan(); line++ { - u := scanner.Text() - if _, err := url.Parse(u); err != nil { - lg("index.txt contains invalid URL %q in line %d", u, line) + c := csv.NewReader(resp.Body) + const ( + pathColumn = 0 + timeColumn = 1 + ) + for line := 1; ; line++ { + r, err := c.Read() + if err == io.EOF { + break + } + if err != nil { + return nil, err + } + if len(r) < 2 { + lg("%q has not enough columns in line %d", line) + continue + } + t, err := time.Parse(time.RFC3339, r[timeColumn]) + if err != nil { + lg("%q has an invalid time stamp in line %d: %v", changesURL, line, err) + continue + } + // Apply date range filtering. + if afp.AgeAccept != nil && !afp.AgeAccept(t) { + continue + } + path := r[pathColumn] + if _, err := url.Parse(path); err != nil { + lg("%q contains an invalid URL %q in line %d", changesURL, path, line) continue } files = append(files, - PlainAdvisoryFile(base.JoinPath(u).String())) - } - - if err := scanner.Err(); err != nil { - return nil, err + PlainAdvisoryFile(base.JoinPath(path).String())) } return files, nil }