forked from golang/vulndb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
222 lines (194 loc) · 5.88 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
// Copyright 2023 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.
// Command inspect provides insights into the current contents of vulndb.
package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"sort"
"strconv"
"strings"
"text/tabwriter"
"time"
"golang.org/x/exp/maps"
"golang.org/x/vulndb/internal/genericosv"
"golang.org/x/vulndb/internal/ghsarepo"
"golang.org/x/vulndb/internal/gitrepo"
"golang.org/x/vulndb/internal/report"
"golang.org/x/vulndb/internal/stdlib"
)
var (
localGHSA = flag.String("local-ghsa", "", "path to local GHSA repo, instead of cloning remote")
detail = flag.Bool("detail", false, "if true, print more details on GHSAs not yet in vulndb")
)
func main() {
start := time.Now()
flag.Parse()
localRepo, err := gitrepo.Open(context.Background(), ".")
if err != nil {
log.Fatal(err)
}
byIssue, _, err := report.All(localRepo)
if err != nil {
log.Fatal(err)
}
reports := maps.Values(byIssue)
var c *ghsarepo.Client
if *localGHSA != "" {
repo, err := gitrepo.Open(context.Background(), *localGHSA)
if err != nil {
log.Fatal(err)
}
c, err = ghsarepo.NewClientFromRepo(repo)
if err != nil {
log.Fatal(err)
}
} else {
log.Println("cloning remote GHSA repo (use -local-ghsa to speed this up)...")
c, err = ghsarepo.NewClient()
if err != nil {
log.Fatal(err)
}
}
ghsas := c.List()
overall, byYear := summarize(ghsas, reports)
display(overall, byYear)
if *detail {
fmt.Println("\n=== GHSAs not yet in vulndb ===")
displayGHSAs(overall.ghsasNotInVDB)
}
fmt.Printf("\n%s\n", time.Since(start).Truncate(time.Millisecond))
}
func display(overall *summary, byYear map[int]*summary) {
tw := tabwriter.NewWriter(os.Stdout, 2, 4, 2, ' ', 0)
years := maps.Keys(byYear)
sort.Sort(sort.Reverse(sort.IntSlice(years))) // sort descending
headings := func() {
fmt.Fprintf(tw, "\t%s", "Total")
for _, year := range years {
fmt.Fprintf(tw, "\t%4d", year)
}
fmt.Fprintf(tw, "\n")
fmt.Fprintf(tw, "\t%s", "-----")
for range years {
fmt.Fprintf(tw, "\t%4s", "----")
}
fmt.Fprintf(tw, "\n")
}
data := func(desc string, indent int, getData func(s *summary) int) {
var indentS string
if indent > 0 {
indentS = strings.Repeat("--", indent) + " "
}
fmt.Fprintf(tw, "%s%s\t%4d", indentS, desc, getData(overall))
for _, year := range years {
fmt.Fprintf(tw, "\t%4d", getData(byYear[year]))
}
fmt.Fprintf(tw, "\n")
}
newline := func() {
fmt.Fprintf(tw, "%s\n", strings.Repeat("\t", len(years))) // preserve tab formatting
}
// Summary of Go reports by year.
headings()
data("Go reports", 0, func(s *summary) int { return s.reports })
data("Regular reports", 1, func(s *summary) int { return s.regular })
data("Excluded reports", 1, func(s *summary) int { return s.excluded })
for _, er := range report.ExcludedReasons {
data(string(er), 2, func(s *summary) int { return s.excludedByReason[er] })
}
data("Reports with no GHSA (+)", 1, func(s *summary) int { return s.noGHSA })
data("Stdlib, toolchain and x/ reports", 1, func(s *summary) int { return s.firstParty })
// Summary of GHSAs by year.
newline()
headings()
data("GHSAs affecting Go", 0, func(s *summary) int { return s.ghsas })
data("GHSAs not yet in vulndb (++)", 1, func(s *summary) int { return len(s.ghsasNotInVDB) })
// Additional context.
newline()
fmt.Fprintln(tw, "(+) \"Go reports with no GHSA\" are published third-party Go reports\nwith no corresponding GHSA. (This isn't a problem; it's informational only.)")
fmt.Fprintln(tw, "(++) \"GHSAs not yet in vulndb\" are published GHSAs with no corresponding\nGo report. There may already be an open issue on the tracker for these.")
tw.Flush()
}
func displayGHSAs(ghsas []string) {
for i, g := range ghsas {
fmt.Println()
fmt.Printf("%d) %s\n", i+1, g)
fmt.Printf("https://github.com/advisories/%s\n", g)
fmt.Printf("search issue tracker: https://github.com/golang/vulndb/issues?q=is%%3Aissue+%s\n", g)
}
}
type summary struct {
reports, regular, excluded, noGHSA, firstParty int
ghsas int
ghsasNotInVDB []string
excludedByReason map[report.ExcludedReason]int
}
func newSummary() *summary {
return &summary{
excludedByReason: make(map[report.ExcludedReason]int),
}
}
func summarize(ghsas []*genericosv.Entry, reports []*report.Report) (*summary, map[int]*summary) {
overall := newSummary()
byYear := make(map[int]*summary)
ghsasWithReport := make(map[string]bool)
for _, r := range reports {
year, err := strconv.Atoi(strings.Split(r.ID, "-")[1])
if err != nil {
panic(err)
}
if _, ok := byYear[year]; !ok {
byYear[year] = newSummary()
}
yearSummary := byYear[year]
overall.reports++
yearSummary.reports++
if isFirstParty(r) {
overall.firstParty++
yearSummary.firstParty++
}
if r.IsExcluded() {
overall.excluded++
overall.excludedByReason[r.Excluded]++
yearSummary.excluded++
yearSummary.excludedByReason[r.Excluded]++
} else {
overall.regular++
yearSummary.regular++
}
if len(r.GHSAs) == 0 && r.CVEMetadata == nil {
overall.noGHSA++
yearSummary.noGHSA++
}
for _, ghsa := range r.GHSAs {
ghsasWithReport[ghsa] = true
}
}
for _, ghsa := range ghsas {
year := ghsa.Published.Year()
if _, ok := byYear[year]; !ok {
byYear[year] = newSummary()
}
yearSummary := byYear[year]
overall.ghsas++
yearSummary.ghsas++
if _, ok := ghsasWithReport[ghsa.ID]; !ok {
overall.ghsasNotInVDB = append(overall.ghsasNotInVDB, ghsa.ID)
yearSummary.ghsasNotInVDB = append(yearSummary.ghsasNotInVDB, ghsa.ID)
}
}
return overall, byYear
}
func isFirstParty(r *report.Report) bool {
for _, m := range r.Modules {
if stdlib.IsStdModule(m.Module) || stdlib.IsCmdModule(m.Module) || stdlib.IsXModule(m.Module) {
return true
}
}
return false
}