mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 18:15:42 +01:00
Implemented security.txt check.
This commit is contained in:
parent
ffd43d510b
commit
fe09d0ea65
1 changed files with 97 additions and 3 deletions
|
|
@ -9,11 +9,16 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
|
@ -24,6 +29,7 @@ type processor struct {
|
||||||
opts *options
|
opts *options
|
||||||
redirects map[string]string
|
redirects map[string]string
|
||||||
noneTLS map[string]struct{}
|
noneTLS map[string]struct{}
|
||||||
|
pmd256 []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type check interface {
|
type check interface {
|
||||||
|
|
@ -47,6 +53,7 @@ func (p *processor) clean() {
|
||||||
for k := range p.noneTLS {
|
for k := range p.noneTLS {
|
||||||
delete(p.noneTLS, k)
|
delete(p.noneTLS, k)
|
||||||
}
|
}
|
||||||
|
p.pmd256 = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) run(checks []check, domains []string) (*Report, error) {
|
func (p *processor) run(checks []check, domains []string) (*Report, error) {
|
||||||
|
|
@ -259,8 +266,21 @@ func (pmdc *providerMetadataCheck) run(p *processor, domain string) error {
|
||||||
msg := fmt.Sprintf("Status: %d (%s).", res.StatusCode, res.Status)
|
msg := fmt.Sprintf("Status: %d (%s).", res.StatusCode, res.Status)
|
||||||
pmdc.add(msg)
|
pmdc.add(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate checksum for later comparison.
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := io.Copy(&buf, res.Body); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := buf.Bytes()
|
||||||
|
h := sha256.New()
|
||||||
|
if _, err := h.Write(data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
p.pmd256 = h.Sum(nil)
|
||||||
|
|
||||||
var doc interface{}
|
var doc interface{}
|
||||||
if err := json.NewDecoder(res.Body).Decode(&doc); err != nil {
|
if err := json.NewDecoder(bytes.NewReader(data)).Decode(&doc); err != nil {
|
||||||
msg := fmt.Sprintf("Decoding JSON failed: %s.", err.Error())
|
msg := fmt.Sprintf("Decoding JSON failed: %s.", err.Error())
|
||||||
pmdc.add(msg)
|
pmdc.add(msg)
|
||||||
}
|
}
|
||||||
|
|
@ -277,8 +297,82 @@ func (pmdc *providerMetadataCheck) run(p *processor, domain string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *securityCheck) run(*processor, string) error {
|
func (sc *securityCheck) run(p *processor, domain string) error {
|
||||||
// TODO: Implement me!
|
path := "https://" + domain + "/.well-known/security.txt"
|
||||||
|
client := p.httpClient()
|
||||||
|
req, err := http.NewRequest(http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
sc.add(fmt.Sprintf("Fetching security failed. Status code %d (%s)", res.StatusCode, res.Status))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
sc.add(fmt.Sprintf("Error while reading security.txt: %s", err.Error()))
|
||||||
|
}
|
||||||
|
if u == "" {
|
||||||
|
sc.add("No CSAF line found in security.txt.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load
|
||||||
|
up, err := url.Parse(u)
|
||||||
|
if err != nil {
|
||||||
|
sc.add(fmt.Sprintf("CSAF URL '%s' invalid: %s.", u, err.Error()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
base, err := url.Parse("https://" + domain + "/.well-known/")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ur := base.ResolveReference(up)
|
||||||
|
u = ur.String()
|
||||||
|
p.checkTLS(u)
|
||||||
|
if req, err = http.NewRequest(http.MethodGet, u, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if res, err = client.Do(req); err != nil {
|
||||||
|
sc.add(fmt.Sprintf("Cannot fetch %s from security.txt: %s", u, err.Error()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if res.StatusCode != http.StatusOK {
|
||||||
|
sc.add(fmt.Sprintf("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 {
|
||||||
|
sc.add(fmt.Sprintf("Reading %s failed: %s.", u, err.Error()))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(h.Sum(nil), p.pmd256) {
|
||||||
|
sc.add(fmt.Sprintf(
|
||||||
|
"Content of %s from security.txt is not identical to .well-known/csaf/provider-metadata.json", u))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sc.baseCheck.messages) == 0 {
|
||||||
|
sc.add("Valid CSAF line in security.txt found.")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue