diff --git a/cmd/csaf_checker/config.go b/cmd/csaf_checker/config.go index 6ed2691..77f7fb1 100644 --- a/cmd/csaf_checker/config.go +++ b/cmd/csaf_checker/config.go @@ -10,10 +10,11 @@ package main import ( "crypto/tls" + "errors" "net/http" ) -type options struct { +type config struct { Output string `short:"o" long:"output" description:"File name of the generated report" value-name:"REPORT-FILE"` Format string `short:"f" long:"format" choice:"json" choice:"html" description:"Format of report" default:"json"` Insecure bool `long:"insecure" description:"Do not check TLS certificates from provider"` @@ -31,3 +32,28 @@ type options struct { clientCerts []tls.Certificate } + +// protectedAccess returns true if we have client certificates or +// extra http headers configured. +// This may be a wrong assumption, because the certs are not checked +// for their domain and custom headers may have other purposes. +func (cfg *config) protectedAccess() bool { + return len(cfg.clientCerts) > 0 || len(cfg.ExtraHeader) > 0 +} + +func (cfg *config) prepare() error { + // Load client certs. + switch hasCert, hasKey := cfg.ClientCert != nil, cfg.ClientKey != nil; { + + case hasCert && !hasKey || !hasCert && hasKey: + return errors.New("both client-key and client-cert options must be set for the authentication") + + case hasCert: + cert, err := tls.LoadX509KeyPair(*cfg.ClientCert, *cfg.ClientKey) + if err != nil { + return err + } + cfg.clientCerts = []tls.Certificate{cert} + } + return nil +} diff --git a/cmd/csaf_checker/main.go b/cmd/csaf_checker/main.go index d614f0b..222f6de 100644 --- a/cmd/csaf_checker/main.go +++ b/cmd/csaf_checker/main.go @@ -11,10 +11,8 @@ package main import ( "bufio" - "crypto/tls" _ "embed" // Used for embedding. "encoding/json" - "errors" "fmt" "html/template" "io" @@ -37,31 +35,6 @@ func errCheck(err error) { } } -func (o *options) prepare() error { - // Load client certs. - switch hasCert, hasKey := o.ClientCert != nil, o.ClientKey != nil; { - - case hasCert && !hasKey || !hasCert && hasKey: - return errors.New("both client-key and client-cert options must be set for the authentication") - - case hasCert: - cert, err := tls.LoadX509KeyPair(*o.ClientCert, *o.ClientKey) - if err != nil { - return err - } - o.clientCerts = []tls.Certificate{cert} - } - return nil -} - -// protectedAccess returns true if we have client certificates or -// extra http headers configured. -// This may be a wrong assumption, because the certs are not checked -// for their domain and custom headers may have other purposes. -func (o *options) protectedAccess() bool { - return len(o.clientCerts) > 0 || len(o.ExtraHeader) > 0 -} - // writeJSON writes the JSON encoding of the given report to the given stream. // It returns nil, otherwise an error. func writeJSON(report *Report, w io.WriteCloser) error { @@ -102,14 +75,14 @@ func (nc *nopCloser) Close() error { return nil } // writeReport defines where to write the report according to the "output" flag option. // It calls also the "writeJSON" or "writeHTML" function according to the "format" flag option. -func writeReport(report *Report, opts *options) error { +func writeReport(report *Report, cfg *config) error { var w io.WriteCloser - if opts.Output == "" { + if cfg.Output == "" { w = &nopCloser{os.Stdout} } else { - f, err := os.Create(opts.Output) + f, err := os.Create(cfg.Output) if err != nil { return err } @@ -118,7 +91,7 @@ func writeReport(report *Report, opts *options) error { var writer func(*Report, io.WriteCloser) error - switch opts.Format { + switch cfg.Format { case "json": writer = writeJSON default: @@ -130,8 +103,8 @@ func writeReport(report *Report, opts *options) error { // run uses a processor to check all the given domains or direct urls // and generates a report. -func run(opts *options, domains []string) (*Report, error) { - p, err := newProcessor(opts) +func run(cfg *config, domains []string) (*Report, error) { + p, err := newProcessor(cfg) if err != nil { return nil, err } @@ -140,27 +113,27 @@ func run(opts *options, domains []string) (*Report, error) { } func main() { - opts := new(options) + cfg := new(config) - parser := flags.NewParser(opts, flags.Default) + parser := flags.NewParser(cfg, flags.Default) parser.Usage = "[OPTIONS] domain..." domains, err := parser.Parse() errCheck(err) - if opts.Version { + if cfg.Version { fmt.Println(util.SemVersion) return } - errCheck(opts.prepare()) + errCheck(cfg.prepare()) if len(domains) == 0 { log.Println("No domain or direct url given.") return } - report, err := run(opts, domains) + report, err := run(cfg, domains) errCheck(err) - errCheck(writeReport(report, opts)) + errCheck(writeReport(report, cfg)) } diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 510ad7c..fb678ac 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -40,7 +40,7 @@ import ( type topicMessages []Message type processor struct { - opts *options + cfg *config validator csaf.RemoteValidator client util.Client unauthClient util.Client @@ -165,17 +165,16 @@ func (m *topicMessages) hasErrors() bool { return false } -// newProcessor returns a processor structure after assigning the given options to the opts attribute -// and initializing the "alreadyChecked" and "expr" fields. -func newProcessor(opts *options) (*processor, error) { +// newProcessor returns an initilaized processor. +func newProcessor(cfg *config) (*processor, error) { var validator csaf.RemoteValidator - if opts.RemoteValidator != "" { + if cfg.RemoteValidator != "" { validatorOptions := csaf.RemoteValidatorOptions{ - URL: opts.RemoteValidator, - Presets: opts.RemoteValidatorPresets, - Cache: opts.RemoteValidatorCache, + URL: cfg.RemoteValidator, + Presets: cfg.RemoteValidatorPresets, + Cache: cfg.RemoteValidatorCache, } var err error if validator, err = validatorOptions.Open(); err != nil { @@ -185,10 +184,10 @@ func newProcessor(opts *options) (*processor, error) { } return &processor{ - opts: opts, + cfg: cfg, alreadyChecked: map[string]whereType{}, expr: util.NewPathEval(), - ageAccept: ageAccept(opts), + ageAccept: ageAccept(cfg), validator: validator, labelChecker: labelChecker{ advisories: map[csaf.TLPLabel]util.Set[string]{}, @@ -205,11 +204,11 @@ func (p *processor) close() { } } -func ageAccept(opts *options) func(time.Time) bool { - if opts.Years == nil { +func ageAccept(cfg *config) func(time.Time) bool { + if cfg.Years == nil { return nil } - good := time.Now().AddDate(-int(*opts.Years), 0, 0) + good := time.Now().AddDate(-int(*cfg.Years), 0, 0) return func(t time.Time) bool { return !t.Before(good) } @@ -431,12 +430,12 @@ func (p *processor) fullClient() util.Client { hClient.CheckRedirect = p.checkRedirect var tlsConfig tls.Config - if p.opts.Insecure { + if p.cfg.Insecure { tlsConfig.InsecureSkipVerify = true } - if len(p.opts.clientCerts) != 0 { - tlsConfig.Certificates = p.opts.clientCerts + if len(p.cfg.clientCerts) != 0 { + tlsConfig.Certificates = p.cfg.clientCerts } hClient.Transport = &http.Transport{ @@ -446,23 +445,23 @@ func (p *processor) fullClient() util.Client { client := util.Client(&hClient) // Add extra headers. - if len(p.opts.ExtraHeader) > 0 { + if len(p.cfg.ExtraHeader) > 0 { client = &util.HeaderClient{ Client: client, - Header: p.opts.ExtraHeader, + Header: p.cfg.ExtraHeader, } } // Add optional URL logging. - if p.opts.Verbose { + if p.cfg.Verbose { client = &util.LoggingClient{Client: client} } // Add optional rate limiting. - if p.opts.Rate != nil { + if p.cfg.Rate != nil { client = &util.LimitingClient{ Client: client, - Limiter: rate.NewLimiter(rate.Limit(*p.opts.Rate), 1), + Limiter: rate.NewLimiter(rate.Limit(*p.cfg.Rate), 1), } } return client @@ -470,7 +469,7 @@ func (p *processor) fullClient() util.Client { // basicClient returns a http Client w/o certs and headers. func (p *processor) basicClient() *http.Client { - if p.opts.Insecure { + if p.cfg.Insecure { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } @@ -500,7 +499,7 @@ func (p *processor) unauthorizedClient() util.Client { // usedAuthorizedClient tells if an authorized client is used // for downloading. func (p *processor) usedAuthorizedClient() bool { - return p.opts.protectedAccess() + return p.cfg.protectedAccess() } // rolieFeedEntries loads the references to the advisory files for a given feed. diff --git a/cmd/csaf_checker/reporters.go b/cmd/csaf_checker/reporters.go index 9a8357b..762cfc1 100644 --- a/cmd/csaf_checker/reporters.go +++ b/cmd/csaf_checker/reporters.go @@ -103,7 +103,7 @@ func (r *validReporter) report(p *processor, domain *Domain) { case !p.invalidAdvisories.used(): req.message(InfoType, "No validations performed") case len(p.invalidAdvisories) == 0: - if p.validator != nil && containsAny(p.opts.RemoteValidatorPresets, + if p.validator != nil && containsAny(p.cfg.RemoteValidatorPresets, "basic", "mandatory", "extended", "full") { req.message(InfoType, "All advisories validated fine.") } else {