all: add linting test
Lint all reports in main unit test.
Change-Id: Ib605dc109128581f8209a6a2088313bd0dbc4801
Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/337851
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/cmd/gendb/main.go b/cmd/gendb/main.go
index 98ab2a5..a38fc56 100644
--- a/cmd/gendb/main.go
+++ b/cmd/gendb/main.go
@@ -64,8 +64,12 @@
if err != nil {
fail(fmt.Sprintf("unable to unmarshal %q: %s", f.Name(), err))
}
- if err = vuln.Lint(); err != nil {
- fail(fmt.Sprintf("invalid vulnerability %q: %s", f.Name(), err))
+ if lints := vuln.Lint(); len(lints) > 0 {
+ fmt.Fprintf(os.Stderr, "invalid vulnerability file %q:\n", os.Args[1])
+ for _, lint := range lints {
+ fmt.Fprintf(os.Stderr, "\t%s\n", lint)
+ }
+ os.Exit(1)
}
name := strings.TrimSuffix(filepath.Base(f.Name()), filepath.Ext(f.Name()))
diff --git a/cmd/genhtml/main.go b/cmd/genhtml/main.go
index bad0bd3..d961fac 100644
--- a/cmd/genhtml/main.go
+++ b/cmd/genhtml/main.go
@@ -198,8 +198,12 @@
if err != nil {
fail(fmt.Sprintf("unable to unmarshal %q: %s", f.Name(), err))
}
- if err = vuln.Lint(); err != nil {
- fail(fmt.Sprintf("invalid vulnerability %q: %s", f.Name(), err))
+ if lints := vuln.Lint(); len(lints) > 0 {
+ fmt.Fprintf(os.Stderr, "invalid vulnerability file %q:\n", os.Args[1])
+ for _, lint := range lints {
+ fmt.Fprintf(os.Stderr, "\t%s\n", lint)
+ }
+ os.Exit(1)
}
name := strings.TrimSuffix(filepath.Base(f.Name()), filepath.Ext(f.Name()))
htmlVulns[name] = vuln
diff --git a/cmd/linter/main.go b/cmd/linter/main.go
index 6312b75..09e019a 100644
--- a/cmd/linter/main.go
+++ b/cmd/linter/main.go
@@ -32,8 +32,11 @@
os.Exit(1)
}
- if err = vuln.Lint(); err != nil {
- fmt.Fprintf(os.Stderr, "invalid vulnerability file %q: %s\n", os.Args[1], err)
+ if lints := vuln.Lint(); len(lints) > 0 {
+ fmt.Fprintf(os.Stderr, "invalid vulnerability file %q:\n", os.Args[1])
+ for _, lint := range lints {
+ fmt.Fprintf(os.Stderr, "\t%s\n", lint)
+ }
os.Exit(1)
}
}
diff --git a/lint_test.go b/lint_test.go
new file mode 100644
index 0000000..7c1f351
--- /dev/null
+++ b/lint_test.go
@@ -0,0 +1,43 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "io/ioutil"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "golang.org/x/vulndb/report"
+ "gopkg.in/yaml.v2"
+)
+
+func TestLintReports(t *testing.T) {
+ reports, err := ioutil.ReadDir("reports")
+ if err != nil {
+ t.Fatalf("unable to read reports/: %s", err)
+ }
+
+ for _, rf := range reports {
+ if rf.IsDir() {
+ continue
+ }
+ t.Run(rf.Name(), func(t *testing.T) {
+ b, err := ioutil.ReadFile(filepath.Join("reports", rf.Name()))
+ if err != nil {
+ t.Fatalf("unable to read %q: %s", rf.Name(), err)
+ }
+
+ var r report.Report
+ if err := yaml.UnmarshalStrict(b, &r); err != nil {
+ t.Fatalf("unable to parse report %q: %s", rf.Name(), err)
+ }
+
+ if lints := r.Lint(); len(lints) > 0 {
+ t.Errorf(strings.Join(lints, "\n"))
+ }
+ })
+ }
+}
diff --git a/report/lint.go b/report/lint.go
index 5cc8ccf..5bfdc08 100644
--- a/report/lint.go
+++ b/report/lint.go
@@ -9,6 +9,7 @@
"fmt"
"io/ioutil"
"net/http"
+ "os"
"regexp"
"strings"
@@ -20,7 +21,13 @@
// TODO: getting things from the proxy should all be cached so we
// aren't re-requesting the same stuff over and over.
-const proxyURL = "https://proxy.golang.org"
+var proxyURL = "https://proxy.golang.org"
+
+func init() {
+ if proxy, ok := os.LookupEnv("GOPROXY"); ok {
+ proxyURL = proxy
+ }
+}
func getModVersions(module string) (map[string]bool, error) {
resp, err := http.Get(fmt.Sprintf("%s/%s/@v/list", proxyURL, module))
@@ -131,44 +138,48 @@
// we aren't fixing one thing at a time. Similarly it might make sense to include
// warnings or informational things alongside errors, especially during for use
// during the triage process.
-func (vuln *Report) Lint() error {
+func (vuln *Report) Lint() []string {
+ var issues []string
+
var importPath string
if !vuln.Stdlib {
if vuln.Module == "" {
- return errors.New("missing module")
+ issues = append(issues, "missing module")
}
- if vuln.Package == vuln.Module {
- return errors.New("package is redundant and can be removed")
+ if vuln.Module != "" && vuln.Package == vuln.Module {
+ issues = append(issues, "package is redundant and can be removed")
}
if vuln.Package != "" && !strings.HasPrefix(vuln.Package, vuln.Module) {
- return errors.New("module must be a prefix of package")
+ issues = append(issues, "module must be a prefix of package")
}
if vuln.Package == "" {
importPath = vuln.Module
} else {
importPath = vuln.Package
}
- if err := checkModVersions(vuln.Module, vuln.Versions); err != nil {
- return err
- }
+ if vuln.Module != "" && importPath != "" {
+ if err := checkModVersions(vuln.Module, vuln.Versions); err != nil {
+ issues = append(issues, err.Error())
+ }
- if err := module.CheckImportPath(importPath); err != nil {
- return err
+ if err := module.CheckImportPath(importPath); err != nil {
+ issues = append(issues, err.Error())
+ }
}
} else if vuln.Package == "" {
- return errors.New("missing package")
+ issues = append(issues, "missing package")
}
for _, additionalPackage := range vuln.AdditionalPackages {
var additionalImportPath string
if additionalPackage.Module == "" {
- return errors.New("missing additional_package.module")
+ issues = append(issues, "missing additional_package.module")
}
if additionalPackage.Package == additionalPackage.Module {
- return errors.New("package is redundant and can be removed")
+ issues = append(issues, "package is redundant and can be removed")
}
if additionalPackage.Package != "" && !strings.HasPrefix(additionalPackage.Package, additionalPackage.Module) {
- return errors.New("additional_package.module must be a prefix of additional_package.package")
+ issues = append(issues, "additional_package.module must be a prefix of additional_package.package")
}
if additionalPackage.Package == "" {
additionalImportPath = additionalPackage.Module
@@ -176,44 +187,44 @@
additionalImportPath = additionalPackage.Package
}
if err := module.CheckImportPath(additionalImportPath); err != nil {
- return err
+ issues = append(issues, err.Error())
}
if !vuln.Stdlib {
if err := checkModVersions(additionalPackage.Module, additionalPackage.Versions); err != nil {
- return err
+ issues = append(issues, err.Error())
}
}
}
if vuln.Description == "" {
- return errors.New("missing description")
+ issues = append(issues, "missing description")
}
if vuln.Published.IsZero() {
- return errors.New("missing published")
+ issues = append(issues, "missing published")
}
if vuln.LastModified != nil && vuln.LastModified.Before(vuln.Published) {
- return errors.New("last_modified is before published")
+ issues = append(issues, "last_modified is before published")
}
if vuln.CVE != "" && vuln.CVEMetadata != nil && vuln.CVEMetadata.ID != "" {
// TODO: may just want to use one of these? :shrug:
- return errors.New("only one of cve and cve_metadata.id should be present")
+ issues = append(issues, "only one of cve and cve_metadata.id should be present")
}
if vuln.CVE != "" && !cveRegex.MatchString(vuln.CVE) {
- return fmt.Errorf("malformed cve: %s", vuln.CVE)
+ issues = append(issues, "malformed cve identifier")
}
if vuln.CVEMetadata != nil {
if vuln.CVEMetadata.ID == "" {
- return errors.New("cve_metadata.id is required")
+ issues = append(issues, "cve_metadata.id is required")
}
if !cveRegex.MatchString(vuln.CVEMetadata.ID) {
- return fmt.Errorf("malformed cve_metadata.id: %s", vuln.CVEMetadata.ID)
+ issues = append(issues, "malformed cve_metadata.id identifier")
}
}
- return nil
+ return issues
}