1
0
Fork 0
mirror of https://github.com/gocsaf/csaf.git synced 2025-12-22 11:55:40 +01:00

Merge pull request #80 from csaf-poc/checker-documentation

Checker documentation
This commit is contained in:
Sascha L. Teichmann 2022-03-21 22:20:00 +01:00 committed by GitHub
commit 45ac434871
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 65 additions and 0 deletions

View file

@ -38,6 +38,8 @@ func errCheck(err error) {
}
}
// 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 {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
@ -48,6 +50,8 @@ func writeJSON(report *Report, w io.WriteCloser) error {
return err
}
// writeHTML writes the given report to the given writer, it uses the template
// in the "reportHTML" variable. It returns nil, otherwise an error.
func writeHTML(report *Report, w io.WriteCloser) error {
tmpl, err := template.New("Report HTML").Parse(reportHTML)
if err != nil {
@ -72,6 +76,8 @@ type nopCloser struct{ io.Writer }
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 {
var w io.WriteCloser
@ -98,6 +104,8 @@ func writeReport(report *Report, opts *options) error {
return writer(report, w)
}
// buildReporters initializes each report by assigning a number and description to it.
// It returns an array of the reporter interface type.
func buildReporters() []reporter {
return []reporter{
&tlsReporter{baseReporter{num: 3, description: "TLS"}},

View file

@ -58,6 +58,9 @@ type processor struct {
expr *util.PathEval
}
// reporter is implemented by any value that has a report method.
// The implementation of the report controls how to test
// the respective requirement and generate the report.
type reporter interface {
report(*processor, *Domain)
}
@ -102,6 +105,8 @@ func (wt whereType) String() string {
}
}
// 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 {
return &processor{
opts: opts,
@ -110,6 +115,7 @@ func newProcessor(opts *options) *processor {
}
}
// clean clears the fields values of the given processor.
func (p *processor) clean() {
p.redirects = nil
p.noneTLS = nil
@ -130,6 +136,9 @@ func (p *processor) clean() {
p.badChanges = nil
}
// run calls checkDomain function for each domain in the given "domains" parameter.
// Then it calls the report method on each report from the given "reporters" paramerter for each domain.
// It return a poiter to the report and nil, otherwise an error.
func (p *processor) run(reporters []reporter, domains []string) (*Report, error) {
var report Report
@ -173,6 +182,8 @@ func (p *processor) checkDomain(domain string) error {
return nil
}
// checkTLS parses the given URL to check its schema, as a result it sets
// the value of "noneTLS" field if it is not HTTPS.
func (p *processor) checkTLS(u string) {
if p.noneTLS == nil {
p.noneTLS = map[string]struct{}{}
@ -231,6 +242,7 @@ func (p *processor) httpClient() *http.Client {
return p.client
}
// use checks the given array and initializes an empty array if its nil.
func use(s *[]string) {
if *s == nil {
*s = []string{}
@ -241,34 +253,50 @@ func used(s []string) bool {
return s != nil
}
// badIntegrity appends a message to the value of "badIntegrity" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badIntegrity(format string, args ...interface{}) {
p.badIntegrities = append(p.badIntegrities, fmt.Sprintf(format, args...))
}
// badSignature appends a message to the value of "badSignature" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badSignature(format string, args ...interface{}) {
p.badSignatures = append(p.badSignatures, fmt.Sprintf(format, args...))
}
// badProviderMetadata appends a message to the value of "badProviderMetadatas" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badProviderMetadata(format string, args ...interface{}) {
p.badProviderMetadatas = append(p.badProviderMetadatas, fmt.Sprintf(format, args...))
}
// badPGP appends a message to the value of "badPGPs" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badPGP(format string, args ...interface{}) {
p.badPGPs = append(p.badPGPs, fmt.Sprintf(format, args...))
}
// badSecurity appends a message to the value of "badSecurity" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badSecurity(format string, args ...interface{}) {
p.badSecurities = append(p.badSecurities, fmt.Sprintf(format, args...))
}
// badIndex appends a message to the value of "badIndices" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badIndex(format string, args ...interface{}) {
p.badIndices = append(p.badIndices, fmt.Sprintf(format, args...))
}
// badChange appends a message to the value of "badChanges" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badChange(format string, args ...interface{}) {
p.badChanges = append(p.badChanges, fmt.Sprintf(format, args...))
}
// badFolder appends a message to the value of "badFolders" field of
// the "processor" struct according to the given format and parameters.
func (p *processor) badFolder(format string, args ...interface{}) {
p.badFolders = append(p.badFolders, fmt.Sprintf(format, args...))
}
@ -492,6 +520,9 @@ func (p *processor) processROLIEFeed(feed string) error {
return nil
}
// checkIndex fetches the "index.txt" and calls "checkTLS" method for HTTPS checks.
// It extracts the file names from the file and passes them to "integrity" function.
// It returns error if fetching/reading the file(s) fails, otherwise nil.
func (p *processor) checkIndex(base string, mask whereType) error {
client := p.httpClient()
index := base + "/index.txt"
@ -530,6 +561,10 @@ func (p *processor) checkIndex(base string, mask whereType) error {
return p.integrity(files, base, mask, p.badIndex)
}
// checkChanges fetches the "changes.csv" and calls the "checkTLS" method for HTTPs checks.
// It extracts the file content, tests the column number and the validity of the time format
// of the fields' values and if they are sorted properly. Then it passes the files to the
// "integrity" functions. It returns error if some test fails, otherwise nil.
func (p *processor) checkChanges(base string, mask whereType) error {
client := p.httpClient()
changes := base + "/changes.csv"
@ -786,6 +821,10 @@ func extractProviderURL(r io.Reader) (string, error) {
return "", nil
}
// checkProviderMetadata checks the provider-metatdata if exists, decodes,
// and validates against the JSON schema. According to the result the respective
// error messages are passed to the badProviderMetadatas method in case of errors.
// It returns nil if all checks are passed.
func (p *processor) checkProviderMetadata(domain string) error {
use(&p.badProviderMetadatas)
@ -829,6 +868,11 @@ func (p *processor) checkProviderMetadata(domain string) error {
return nil
}
// checkSecurity checks the security.txt file by making HTTP request to fetch it.
// It checks the existence of the CSAF field in the file content and tries to fetch
// the value of this field. As a result of these a respective error messages are
// passed to the badSecurity method in case of errors.
// It returns nil if all checks are passed.
func (p *processor) checkSecurity(domain string) error {
client := p.httpClient()
@ -907,6 +951,10 @@ func (p *processor) checkSecurity(domain string) error {
return nil
}
// checkPGPKeys checks if the OpenPGP keys are available and valid, fetches
// the the remotely keys and compares the fingerprints.
// As a result of these a respective error messages are passed to badPGP method
// in case of errors. It returns nil if all checks are passed.
func (p *processor) checkPGPKeys(domain string) error {
use(&p.badPGPs)

View file

@ -42,6 +42,9 @@ func (bc *baseReporter) requirement(domain *Domain) *Requirement {
return req
}
// report tests if the URLs are HTTPS and sets the "message" field value
// of the "Requirement" struct as a result of that.
// A list of non HTTPS URLs is included in the value of the "message" field.
func (r *tlsReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if p.noneTLS == nil {
@ -64,6 +67,8 @@ func (r *tlsReporter) report(p *processor, domain *Domain) {
req.message(urls...)
}
// report tests if redirects are used and sets the "message" field value
// of the "Requirement" struct as a result of that.
func (r *redirectsReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if len(p.redirects) == 0 {
@ -84,6 +89,8 @@ func (r *redirectsReporter) report(p *processor, domain *Domain) {
req.Messages = keys
}
// report tests if an provider-metatdata.json are available and sets the
// "message" field value of the "Requirement" struct as a result of that.
func (r *providerMetadataReport) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badProviderMetadatas) {
@ -97,6 +104,8 @@ func (r *providerMetadataReport) report(p *processor, domain *Domain) {
req.Messages = p.badProviderMetadatas
}
// report tests the "security.txt" file and sets the "message" field value
// of the "Requirement" struct as a result of that.
func (r *securityReporter) report(p *processor, domain *Domain) {
req := r.requirement(domain)
if !used(p.badSecurities) {