From d8ccf9ff4101cd657b2196f270cd25b6842995fe Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Wed, 15 Dec 2021 17:44:13 +0100 Subject: [PATCH] Rfactoring to processor started. --- cmd/csaf_checker/checks.go | 33 +--------- cmd/csaf_checker/processor.go | 120 ++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 31 deletions(-) diff --git a/cmd/csaf_checker/checks.go b/cmd/csaf_checker/checks.go index a84df34..ff4e361 100644 --- a/cmd/csaf_checker/checks.go +++ b/cmd/csaf_checker/checks.go @@ -12,7 +12,6 @@ import ( "bufio" "bytes" "crypto/sha256" - "encoding/json" "fmt" "io" "net/http" @@ -155,38 +154,10 @@ func (rc *redirectsCheck) run(p *processor, domain string) error { } func (pmdc *providerMetadataCheck) run(p *processor, domain string) error { - url := "https://" + domain + "/.well-known/csaf/provider-metadata.json" - client := p.httpClient() - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { + + if err := p.checkProviderMetadata(domain, pmdc.sprintf); err != nil { return err } - res, err := client.Do(req) - if err != nil { - pmdc.sprintf("Fetching provider metadata failed: %s.", err.Error()) - return nil - } - defer res.Body.Close() - if res.StatusCode != http.StatusOK { - pmdc.sprintf("Status: %d (%s).", res.StatusCode, res.Status) - } - - // Calculate checksum for later comparison. - hash := sha256.New() - - if err := json.NewDecoder(io.TeeReader(res.Body, hash)).Decode(&p.pmd); err != nil { - pmdc.sprintf("Decoding JSON failed: %s.", err.Error()) - } - p.pmd256 = hash.Sum(nil) - - errors, err := csaf.ValidateProviderMetadata(p.pmd) - if err != nil { - return err - } - if len(errors) > 0 { - pmdc.add("Validating against JSON schema failed:") - pmdc.add(errors...) - } pmdc.ok("No problems with provider metadata.") return nil diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index b197fb7..f34dd4f 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -9,6 +9,7 @@ package main import ( + "bufio" "bytes" "context" "crypto/sha256" @@ -413,3 +414,122 @@ func (p *processor) checkCSAFs(domain string, lg func(string, ...interface{})) e return nil } + +func (p *processor) checkProviderMetadata(domain string, lg func(string, ...interface{})) error { + + client := p.httpClient() + + url := "https://" + domain + "/.well-known/csaf/provider-metadata.json" + + res, err := client.Get(url) + if err != nil { + lg("Fetching %s: %v.", url, err) + return err + } + + if res.StatusCode != http.StatusOK { + lg("Fetching %s failed. Status code: %d (%s).", + url, res.StatusCode, res.Status) + return errors.New("Cannot load provider-metadata.json") + } + + // Calculate checksum for later comparison. + hash := sha256.New() + + if err := func() error { + defer res.Body.Close() + tee := io.TeeReader(res.Body, hash) + return json.NewDecoder(tee).Decode(&p.pmd) + }(); err != nil { + lg("Decoding JSON failed: %v.", err) + return err + } + + p.pmd256 = hash.Sum(nil) + + errors, err := csaf.ValidateProviderMetadata(p.pmd) + if err != nil { + return err + } + if len(errors) > 0 { + lg("Validating against JSON schema failed:") + for _, msg := range errors { + lg(strings.ReplaceAll(msg, `%`, `%%`)) + } + } + return nil +} + +func (p *processor) checkSecurity(domain string, lg func(string, ...interface{})) error { + + client := p.httpClient() + + path := "https://" + domain + "/.well-known/security.txt" + res, err := client.Get(path) + if err != nil { + return err + } + + if res.StatusCode != http.StatusOK { + lg("Fetching %s failed. Status code %d (%s)", + path, res.StatusCode, res.Status) + return errors.New("fetching security.txt failed") + } + + u, err := func() (string, error) { + defer res.Body.Close() + lines := bufio.NewScanner(res.Body) + for lines.Scan() { + line := lines.Text() + if strings.HasPrefix(line, "CSAF:") { + return strings.TrimSpace(line[6:]), nil + } + } + return "", lines.Err() + }() + if err != nil { + lg("Error while reading security.txt: %v", err) + return err + } + if u == "" { + lg("No CSAF line found in security.txt.") + return errors.New("no CSAF line in security.txt") + } + + // Try to load + up, err := url.Parse(u) + if err != nil { + lg("CSAF URL '%s' invalid: %v.", u, err.Error()) + return err + } + + base, err := url.Parse("https://" + domain + "/.well-known/") + if err != nil { + return err + } + ur := base.ResolveReference(up) + u = ur.String() + p.checkTLS(u) + if res, err = client.Get(u); err != nil { + lg("Cannot fetch %s from security.txt: %v", u, err) + return nil + } + if res.StatusCode != http.StatusOK { + lg("Fetching %s failed. Status code %d (%s).", + u, res.StatusCode, res.Status) + return nil + } + defer res.Body.Close() + // Compare checksums to already read provider-metadata.json. + h := sha256.New() + if _, err := io.Copy(h, res.Body); err != nil { + lg("Reading %s failed: %v.", u, err) + return nil + } + + if !bytes.Equal(h.Sum(nil), p.pmd256) { + lg("Content of %s from security.txt is not identical to .well-known/csaf/provider-metadata.json", u) + } + + return nil +}