diff --git a/cmd/csaf_checker/main.go b/cmd/csaf_checker/main.go index 057778c..aeff022 100644 --- a/cmd/csaf_checker/main.go +++ b/cmd/csaf_checker/main.go @@ -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"}}, diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 54f3a06..45ae721 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -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) diff --git a/cmd/csaf_checker/reporters.go b/cmd/csaf_checker/reporters.go index e52b088..be79926 100644 --- a/cmd/csaf_checker/reporters.go +++ b/cmd/csaf_checker/reporters.go @@ -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) {