|
| 1 | +diff --git a/README.md b/README.md |
| 2 | +index 0af935a..8db6157 100644 |
| 3 | +--- a/README.md |
| 4 | ++++ b/README.md |
| 5 | +@@ -1,5 +1,23 @@ |
| 6 | + # Go Vulnerability Management |
| 7 | + |
| 8 | ++This is a fork of the govulncheck tool which adds the ability to exclude specific |
| 9 | ++vulnerebalities. |
| 10 | ++ |
| 11 | ++It is configured through the `.govuln_exclude` file by default which takes the following format |
| 12 | ++``` |
| 13 | ++# Some comment about why the vulnerability is excluded |
| 14 | ++GO-2026-4880 |
| 15 | ++``` |
| 16 | ++The file should contain the ID of 1 excluded vulnerability per line. |
| 17 | ++Only lines starting with `GO-` are considered vulnerability identifiers. |
| 18 | ++Any other lines will be discarded. |
| 19 | ++ |
| 20 | ++If the file is missing or parsing otherwise fails, an empty excluded list will be used. |
| 21 | ++ |
| 22 | ++This should make the tool a drop-in replacement for govulncheck. |
| 23 | ++ |
| 24 | ++ |
| 25 | ++## Original README |
| 26 | + [](https://pkg.go.dev/golang.org/x/vuln) |
| 27 | + |
| 28 | + Go's support for vulnerability management includes tooling for analyzing your |
| 29 | +diff --git a/internal/govulncheck/govulncheck.go b/internal/govulncheck/govulncheck.go |
| 30 | +index 377a378..aca9f41 100644 |
| 31 | +--- a/internal/govulncheck/govulncheck.go |
| 32 | ++++ b/internal/govulncheck/govulncheck.go |
| 33 | +@@ -84,6 +84,10 @@ type Config struct { |
| 34 | + // what to do with it. Valid values are source, binary, query, |
| 35 | + // and extract. |
| 36 | + ScanMode ScanMode `json:"scan_mode,omitempty"` |
| 37 | ++ |
| 38 | ++ // ExcludedVulnerabilites contains a set of go vuln identifiers that will be |
| 39 | ++ // ignored if they are present in the output |
| 40 | ++ ExcludedVulnerabilites map[string]struct{} `json:"excluded_vulnerabilities,omitempty"` |
| 41 | + } |
| 42 | + |
| 43 | + // SBOM contains minimal information about the artifacts govulncheck is scanning. |
| 44 | +diff --git a/internal/govulncheck/jsonhandler.go b/internal/govulncheck/jsonhandler.go |
| 45 | +index b1586e0..51c2f02 100644 |
| 46 | +--- a/internal/govulncheck/jsonhandler.go |
| 47 | ++++ b/internal/govulncheck/jsonhandler.go |
| 48 | +@@ -13,6 +13,7 @@ import ( |
| 49 | + ) |
| 50 | + |
| 51 | + type jsonHandler struct { |
| 52 | ++ cfg *Config |
| 53 | + enc *json.Encoder |
| 54 | + } |
| 55 | + |
| 56 | +@@ -25,6 +26,7 @@ func NewJSONHandler(w io.Writer) Handler { |
| 57 | + |
| 58 | + // Config writes config block in JSON to the underlying writer. |
| 59 | + func (h *jsonHandler) Config(config *Config) error { |
| 60 | ++ h.cfg = config |
| 61 | + return h.enc.Encode(Message{Config: config}) |
| 62 | + } |
| 63 | + |
| 64 | +@@ -45,5 +47,9 @@ func (h *jsonHandler) OSV(entry *osv.Entry) error { |
| 65 | + |
| 66 | + // Finding writes a finding in JSON to the underlying writer. |
| 67 | + func (h *jsonHandler) Finding(finding *Finding) error { |
| 68 | ++ // Return early if the finding is excluded |
| 69 | ++ if _, ok := h.cfg.ExcludedVulnerabilites[finding.OSV]; ok { |
| 70 | ++ return nil |
| 71 | ++ } |
| 72 | + return h.enc.Encode(Message{Finding: finding}) |
| 73 | + } |
| 74 | +diff --git a/internal/openvex/handler.go b/internal/openvex/handler.go |
| 75 | +index a2d3282..5a5e80b 100644 |
| 76 | +--- a/internal/openvex/handler.go |
| 77 | ++++ b/internal/openvex/handler.go |
| 78 | +@@ -109,6 +109,10 @@ func moreSpecific(f1, f2 *govulncheck.Finding) int { |
| 79 | + } |
| 80 | + |
| 81 | + func (h *handler) Finding(f *govulncheck.Finding) error { |
| 82 | ++ // Return early if the finding is ignored |
| 83 | ++ if _, ok := h.cfg.ExcludedVulnerabilites[f.OSV]; ok { |
| 84 | ++ return nil |
| 85 | ++ } |
| 86 | + fs := h.findings[f.OSV] |
| 87 | + if len(fs) == 0 { |
| 88 | + fs = []*govulncheck.Finding{f} |
| 89 | +diff --git a/internal/sarif/handler.go b/internal/sarif/handler.go |
| 90 | +index d9e585b..b2d0dcd 100644 |
| 91 | +--- a/internal/sarif/handler.go |
| 92 | ++++ b/internal/sarif/handler.go |
| 93 | +@@ -87,6 +87,10 @@ func moreSpecific(f1, f2 *govulncheck.Finding) int { |
| 94 | + } |
| 95 | + |
| 96 | + func (h *handler) Finding(f *govulncheck.Finding) error { |
| 97 | ++ // Return early if the OSV is excluded |
| 98 | ++ if _, ok := h.cfg.ExcludedVulnerabilites[f.OSV]; ok { |
| 99 | ++ return nil |
| 100 | ++ } |
| 101 | + fs := h.findings[f.OSV] |
| 102 | + if len(fs) == 0 { |
| 103 | + fs = []*govulncheck.Finding{f} |
| 104 | +diff --git a/internal/scan/flags.go b/internal/scan/flags.go |
| 105 | +index e67c0a1..5c1871f 100644 |
| 106 | +--- a/internal/scan/flags.go |
| 107 | ++++ b/internal/scan/flags.go |
| 108 | +@@ -27,6 +27,7 @@ type config struct { |
| 109 | + format FormatFlag |
| 110 | + version bool |
| 111 | + env []string |
| 112 | ++ exclude string |
| 113 | + } |
| 114 | + |
| 115 | + func parseFlags(cfg *config, stderr io.Writer, args []string) error { |
| 116 | +@@ -46,6 +47,7 @@ func parseFlags(cfg *config, stderr io.Writer, args []string) error { |
| 117 | + flags.Var(&cfg.format, "format", "specify format output\nThe supported values are 'text', 'json', 'sarif', and 'openvex' (default 'text')") |
| 118 | + flags.BoolVar(&version, "version", false, "print the version information") |
| 119 | + flags.Var(&scanFlag, "scan", "set the scanning level desired, one of 'module', 'package', or 'symbol' (default 'symbol')") |
| 120 | ++ flags.StringVar(&cfg.exclude, "exclude", ".govuln_exclude", "path to a file containing vulnerabilities to exclude (default '.govuln_exclude')") |
| 121 | + |
| 122 | + // We don't want to print the whole usage message on each flags |
| 123 | + // error, so we set to a no-op and do the printing ourselves. |
| 124 | +diff --git a/internal/scan/run.go b/internal/scan/run.go |
| 125 | +index 5f6a641..1ae1e0e 100644 |
| 126 | +--- a/internal/scan/run.go |
| 127 | ++++ b/internal/scan/run.go |
| 128 | +@@ -5,9 +5,11 @@ |
| 129 | + package scan |
| 130 | + |
| 131 | + import ( |
| 132 | ++ "bufio" |
| 133 | + "context" |
| 134 | + "fmt" |
| 135 | + "io" |
| 136 | ++ "os" |
| 137 | + "os/exec" |
| 138 | + "path" |
| 139 | + "path/filepath" |
| 140 | +@@ -104,6 +106,30 @@ func prepareConfig(ctx context.Context, cfg *config, client *client.Client) { |
| 141 | + if mod, err := client.LastModifiedTime(ctx); err == nil { |
| 142 | + cfg.DBLastModified = &mod |
| 143 | + } |
| 144 | ++ |
| 145 | ++ cfg.ExcludedVulnerabilites = excludedVulns(cfg.exclude) |
| 146 | ++} |
| 147 | ++ |
| 148 | ++// excludedVulns loads a list of vulnerability IDs to ignore from the given filepath |
| 149 | ++// If the file is not present, or cannot be parsed, an empty set will be returned |
| 150 | ++func excludedVulns(file string) map[string]struct{} { |
| 151 | ++ output := make(map[string]struct{}) |
| 152 | ++ fd, err := os.Open(file) |
| 153 | ++ if err != nil { |
| 154 | ++ return output |
| 155 | ++ } |
| 156 | ++ scanner := bufio.NewScanner(fd) |
| 157 | ++ for scanner.Scan() { |
| 158 | ++ line := scanner.Text() |
| 159 | ++ line = strings.TrimSpace(line) |
| 160 | ++ if strings.HasPrefix(line, "GO-") { |
| 161 | ++ output[line] = struct{}{} |
| 162 | ++ } |
| 163 | ++ } |
| 164 | ++ if scanner.Err() != nil { |
| 165 | ++ return make(map[string]struct{}) |
| 166 | ++ } |
| 167 | ++ return output |
| 168 | + } |
| 169 | + |
| 170 | + // scannerVersion reconstructs the current version of |
| 171 | +diff --git a/internal/scan/text.go b/internal/scan/text.go |
| 172 | +index ab49bdd..6eb6270 100644 |
| 173 | +--- a/internal/scan/text.go |
| 174 | ++++ b/internal/scan/text.go |
| 175 | +@@ -40,6 +40,7 @@ type TextHandler struct { |
| 176 | + findings []*findingSummary |
| 177 | + scanLevel govulncheck.ScanLevel |
| 178 | + scanMode govulncheck.ScanMode |
| 179 | ++ excluded map[string]struct{} |
| 180 | + |
| 181 | + err error |
| 182 | + |
| 183 | +@@ -91,6 +92,7 @@ func (h *TextHandler) Flush() error { |
| 184 | + func (h *TextHandler) Config(config *govulncheck.Config) error { |
| 185 | + h.scanLevel = config.ScanLevel |
| 186 | + h.scanMode = config.ScanMode |
| 187 | ++ h.excluded = config.ExcludedVulnerabilites |
| 188 | + |
| 189 | + if !h.showVersion { |
| 190 | + return nil |
| 191 | +@@ -182,6 +184,9 @@ func (h *TextHandler) OSV(entry *osv.Entry) error { |
| 192 | + |
| 193 | + // Finding gathers vulnerability findings to be written. |
| 194 | + func (h *TextHandler) Finding(finding *govulncheck.Finding) error { |
| 195 | ++ if _, ok := h.excluded[finding.OSV]; ok { |
| 196 | ++ return nil |
| 197 | ++ } |
| 198 | + if err := validateFindings(finding); err != nil { |
| 199 | + return err |
| 200 | + } |
0 commit comments