1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 18:15:42 +01:00

Add time range to checker.

This commit is contained in:
Sascha L. Teichmann 2023-07-26 13:34:15 +02:00
parent 125028773f
commit 5e5074fbf1
3 changed files with 85 additions and 28 deletions

View file

@ -13,7 +13,9 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"time"
"github.com/csaf-poc/csaf_distribution/v2/internal/models"
"github.com/csaf-poc/csaf_distribution/v2/internal/options" "github.com/csaf-poc/csaf_distribution/v2/internal/options"
) )
@ -35,6 +37,7 @@ type config struct {
Verbose bool `long:"verbose" short:"v" description:"Verbose output" toml:"verbose"` Verbose bool `long:"verbose" short:"v" description:"Verbose output" toml:"verbose"`
Rate *float64 `long:"rate" short:"r" description:"The average upper limit of https operations per second (defaults to unlimited)" toml:"rate"` Rate *float64 `long:"rate" short:"r" description:"The average upper limit of https operations per second (defaults to unlimited)" toml:"rate"`
Years *uint `long:"years" short:"y" description:"Number of years to look back from now" value-name:"YEARS" toml:"years"` Years *uint `long:"years" short:"y" description:"Number of years to look back from now" value-name:"YEARS" toml:"years"`
Range *models.TimeRange `long:"timerange" short:"t" description:"RANGE of time from which advisories to download" value-name:"RANGE" toml:"timerange"`
ExtraHeader http.Header `long:"header" short:"H" description:"One or more extra HTTP header fields" toml:"header"` ExtraHeader http.Header `long:"header" short:"H" description:"One or more extra HTTP header fields" toml:"header"`
RemoteValidator string `long:"validator" description:"URL to validate documents remotely" value-name:"URL" toml:"validator"` RemoteValidator string `long:"validator" description:"URL to validate documents remotely" value-name:"URL" toml:"validator"`
@ -44,6 +47,7 @@ type config struct {
Config string `short:"c" long:"config" description:"Path to config TOML file" value-name:"TOML-FILE" toml:"-"` Config string `short:"c" long:"config" description:"Path to config TOML file" value-name:"TOML-FILE" toml:"-"`
clientCerts []tls.Certificate clientCerts []tls.Certificate
ageAccept func(time.Time) bool
} }
// configPaths are the potential file locations of the config file. // configPaths are the potential file locations of the config file.
@ -102,6 +106,16 @@ func (cfg *config) protectedAccess() bool {
// prepare prepares internal state of a loaded configuration. // prepare prepares internal state of a loaded configuration.
func (cfg *config) prepare() error { func (cfg *config) prepare() error {
// Load client certs. // Load client certs.
if err := cfg.prepareCertificates(); err != nil {
return err
}
return cfg.prepareTimeRangeFilter()
}
// prepareCertificates loads the client side certificates used by the HTTP client.
func (cfg *config) prepareCertificates() error {
switch hasCert, hasKey := cfg.ClientCert != nil, cfg.ClientKey != nil; { switch hasCert, hasKey := cfg.ClientCert != nil, cfg.ClientKey != nil; {
case hasCert && !hasKey || !hasCert && hasKey: case hasCert && !hasKey || !hasCert && hasKey:
@ -116,3 +130,27 @@ func (cfg *config) prepare() error {
} }
return nil return nil
} }
// acceptYears returns a filter that accepts advisories from the last years.
func acceptYears(years uint) func(time.Time) bool {
good := time.Now().AddDate(-int(years), 0, 0)
return func(t time.Time) bool {
return !t.Before(good)
}
}
// prepareTimeRangeFilter sets up the filter in which time range
// advisory should be considered for checking.
func (cfg *config) prepareTimeRangeFilter() error {
switch {
case cfg.Years != nil && cfg.Range != nil:
return errors.New(`"timerange" and "years" are both configured: only one allowed`)
case cfg.Years != nil:
cfg.ageAccept = acceptYears(*cfg.Years)
case cfg.Range != nil:
cfg.ageAccept = cfg.Range.Contains
}
return nil
}

View file

@ -44,7 +44,6 @@ type processor struct {
validator csaf.RemoteValidator validator csaf.RemoteValidator
client util.Client client util.Client
unauthClient util.Client unauthClient util.Client
ageAccept func(time.Time) bool
redirects map[string][]string redirects map[string][]string
noneTLS util.Set[string] noneTLS util.Set[string]
@ -187,7 +186,6 @@ func newProcessor(cfg *config) (*processor, error) {
cfg: cfg, cfg: cfg,
alreadyChecked: map[string]whereType{}, alreadyChecked: map[string]whereType{},
expr: util.NewPathEval(), expr: util.NewPathEval(),
ageAccept: ageAccept(cfg),
validator: validator, validator: validator,
labelChecker: labelChecker{ labelChecker: labelChecker{
advisories: map[csaf.TLPLabel]util.Set[string]{}, advisories: map[csaf.TLPLabel]util.Set[string]{},
@ -204,16 +202,6 @@ func (p *processor) close() {
} }
} }
func ageAccept(cfg *config) func(time.Time) bool {
if cfg.Years == nil {
return nil
}
good := time.Now().AddDate(-int(*cfg.Years), 0, 0)
return func(t time.Time) bool {
return !t.Before(good)
}
}
// clean clears the fields values of the given processor. // clean clears the fields values of the given processor.
func (p *processor) clean() { func (p *processor) clean() {
p.redirects = nil p.redirects = nil
@ -557,8 +545,8 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) {
rfeed.Entries(func(entry *csaf.Entry) { rfeed.Entries(func(entry *csaf.Entry) {
// Filter if we have date checking. // Filter if we have date checking.
if p.ageAccept != nil { if p.cfg.ageAccept != nil {
if pub := time.Time(entry.Published); !pub.IsZero() && !p.ageAccept(pub) { if pub := time.Time(entry.Published); !pub.IsZero() && !p.cfg.ageAccept(pub) {
return return
} }
} }
@ -669,7 +657,7 @@ func (p *processor) integrity(
if m := yearFromURL.FindStringSubmatch(u); m != nil { if m := yearFromURL.FindStringSubmatch(u); m != nil {
year, _ := strconv.Atoi(m[1]) year, _ := strconv.Atoi(m[1])
// Check if we are in checking time interval. // Check if we are in checking time interval.
if p.ageAccept != nil && !p.ageAccept( if p.cfg.ageAccept != nil && !p.cfg.ageAccept(
time.Date( time.Date(
year, 12, 31, // Assume last day of year. year, 12, 31, // Assume last day of year.
23, 59, 59, 0, // 23:59:59 23, 59, 59, 0, // 23:59:59
@ -975,7 +963,7 @@ func (p *processor) checkChanges(base string, mask whereType) error {
return nil, nil, err return nil, nil, err
} }
// Apply date range filtering. // Apply date range filtering.
if p.ageAccept != nil && !p.ageAccept(t) { if p.cfg.ageAccept != nil && !p.cfg.ageAccept(t) {
continue continue
} }
path := r[pathColumn] path := r[pathColumn]
@ -992,7 +980,7 @@ func (p *processor) checkChanges(base string, mask whereType) error {
if len(files) == 0 { if len(files) == 0 {
var filtered string var filtered string
if p.ageAccept != nil { if p.cfg.ageAccept != nil {
filtered = " (maybe filtered out by time interval)" filtered = " (maybe filtered out by time interval)"
} }
p.badChanges.warn("no entries in changes.csv found" + filtered) p.badChanges.warn("no entries in changes.csv found" + filtered)

View file

@ -16,10 +16,11 @@ Application Options:
-v, --verbose Verbose output -v, --verbose Verbose output
-r, --rate= The average upper limit of https operations per second (defaults to unlimited) -r, --rate= The average upper limit of https operations per second (defaults to unlimited)
-y, --years=YEARS Number of years to look back from now -y, --years=YEARS Number of years to look back from now
-t, --timerange=RANGE RANGE of time from which advisories to download
-H, --header= One or more extra HTTP header fields -H, --header= One or more extra HTTP header fields
--validator=URL URL to validate documents remotely --validator=URL URL to validate documents remotely
--validatorcache=FILE FILE to cache remote validations --validatorcache=FILE FILE to cache remote validations
--validatorpreset= One or more presets to validate remotely (default: mandatory) --validatorpreset= One or more presets to validate remotely (default: [mandatory])
-c, --config=TOML-FILE Path to config TOML file -c, --config=TOML-FILE Path to config TOML file
Help Options: Help Options:
@ -48,6 +49,7 @@ insecure = false
verbose = false verbose = false
# rate # not set by default # rate # not set by default
# years # not set by default # years # not set by default
# timerange # not set by default
# header # not set by default # header # not set by default
# validator # not set by default # validator # not set by default
# validatorcache # not set by default # validatorcache # not set by default
@ -66,6 +68,35 @@ type 2: error
The checker result is a success if no checks resulted in type 2, and a failure otherwise. The checker result is a success if no checks resulted in type 2, and a failure otherwise.
`years` and `timerange` allows only checking advisories from a given time interval.
It is only allowed to specify one off them.
`years` looks number of years back from now. `timerange` values allow finer controls:
1. Relative. If the given string follows the rules of being a [Go duration](https://pkg.go.dev/time@go1.20.6#ParseDuration)
the time interval from now minus that duration till now is used.
E.g. `"3h"` means checking the advisories that have changed in the last three hours.
2. Absolute. If the given string is an RFC 3339 date timestamp the time interval between
this date and now is used.
E.g. `"2006-01-02"` means that all files between 2006 January 2nd and now going to be
checked.
Accepted patterns are:
- `"2006-01-02T15:04:05Z"`
- `"2006-01-02T15:04:05+07:00"`
- `"2006-01-02T15:04:05-07:00"`
- `"2006-01-02T15:04:05"`
- `"2006-01-02T15:04"`
- `"2006-01-02T15"`
- `"2006-01-02"`
- `"2006-01"`
- `"2006"`
Missing parts are set to the smallest value possible in that field.
3. Range. Same as 2 but separated by a `,` to span an interval. e.g `2019,2024`
spans an interval from 1st January 2019 to the 1st January of 2024.
All interval boundaries are inclusive.
### Remarks ### Remarks