@@ -11,12 +11,15 @@ import (
11
11
"context"
12
12
"log"
13
13
"os"
14
+ "sort"
14
15
"strings"
15
16
16
17
"golang.org/x/tools/go/packages"
17
18
gvc "golang.org/x/tools/gopls/internal/govulncheck"
18
19
"golang.org/x/tools/internal/lsp/command"
19
20
"golang.org/x/vuln/client"
21
+ "golang.org/x/vuln/osv"
22
+ "golang.org/x/vuln/vulncheck"
20
23
)
21
24
22
25
func init () {
@@ -79,29 +82,84 @@ func (c *cmd) Run(ctx context.Context, cfg *packages.Config, patterns ...string)
79
82
}
80
83
log .Printf ("loaded %d packages\n " , len (loadedPkgs ))
81
84
82
- r , err := gvc .Source (ctx , loadedPkgs , c .Client )
85
+ log .Printf ("analyzing %d packages...\n " , len (loadedPkgs ))
86
+
87
+ r , err := vulncheck .Source (ctx , loadedPkgs , & vulncheck.Config {Client : c .Client })
83
88
if err != nil {
84
89
return nil , err
85
90
}
91
+ unaffectedMods := filterUnaffected (r .Vulns )
92
+ r .Vulns = filterCalled (r )
93
+
86
94
callInfo := gvc .GetCallInfo (r , loadedPkgs )
87
- return toVulns (callInfo )
95
+ return toVulns (callInfo , unaffectedMods )
88
96
// TODO: add import graphs.
89
97
}
90
98
91
- func toVulns (ci * gvc.CallInfo ) ([]Vuln , error ) {
99
+ // filterCalled returns vulnerabilities where the symbols are actually called.
100
+ func filterCalled (r * vulncheck.Result ) []* vulncheck.Vuln {
101
+ var vulns []* vulncheck.Vuln
102
+ for _ , v := range r .Vulns {
103
+ if v .CallSink != 0 {
104
+ vulns = append (vulns , v )
105
+ }
106
+ }
107
+ return vulns
108
+ }
109
+
110
+ // filterUnaffected returns vulnerabilities where no symbols are called,
111
+ // grouped by module.
112
+ func filterUnaffected (vulns []* vulncheck.Vuln ) map [string ][]* osv.Entry {
113
+ // It is possible that the same vuln.OSV.ID has vuln.CallSink != 0
114
+ // for one symbol, but vuln.CallSink == 0 for a different one, so
115
+ // we need to filter out ones that have been called.
116
+ called := map [string ]bool {}
117
+ for _ , vuln := range vulns {
118
+ if vuln .CallSink != 0 {
119
+ called [vuln .OSV .ID ] = true
120
+ }
121
+ }
122
+
123
+ modToIDs := map [string ]map [string ]* osv.Entry {}
124
+ for _ , vuln := range vulns {
125
+ if ! called [vuln .OSV .ID ] {
126
+ if _ , ok := modToIDs [vuln .ModPath ]; ! ok {
127
+ modToIDs [vuln .ModPath ] = map [string ]* osv.Entry {}
128
+ }
129
+ // keep only one vuln.OSV instance for the same ID.
130
+ modToIDs [vuln .ModPath ][vuln .OSV .ID ] = vuln .OSV
131
+ }
132
+ }
133
+ output := map [string ][]* osv.Entry {}
134
+ for m , vulnSet := range modToIDs {
135
+ var vulns []* osv.Entry
136
+ for _ , vuln := range vulnSet {
137
+ vulns = append (vulns , vuln )
138
+ }
139
+ sort .Slice (vulns , func (i , j int ) bool { return vulns [i ].ID < vulns [j ].ID })
140
+ output [m ] = vulns
141
+ }
142
+ return output
143
+ }
144
+
145
+ func fixed (v * osv.Entry ) string {
146
+ lf := gvc .LatestFixed (v .Affected )
147
+ if lf != "" && lf [0 ] != 'v' {
148
+ lf = "v" + lf
149
+ }
150
+ return lf
151
+ }
152
+
153
+ func toVulns (ci * gvc.CallInfo , unaffectedMods map [string ][]* osv.Entry ) ([]Vuln , error ) {
92
154
var vulns []Vuln
93
155
94
156
for _ , vg := range ci .VulnGroups {
95
157
v0 := vg [0 ]
96
- lf := gvc .LatestFixed (v0 .OSV .Affected )
97
- if lf != "" && lf [0 ] != 'v' {
98
- lf = "v" + lf
99
- }
100
158
vuln := Vuln {
101
159
ID : v0 .OSV .ID ,
102
160
PkgPath : v0 .PkgPath ,
103
161
CurrentVersion : ci .ModuleVersions [v0 .ModPath ],
104
- FixedVersion : lf ,
162
+ FixedVersion : fixed ( v0 . OSV ) ,
105
163
Details : v0 .OSV .Details ,
106
164
107
165
Aliases : v0 .OSV .Aliases ,
@@ -119,5 +177,19 @@ func toVulns(ci *gvc.CallInfo) ([]Vuln, error) {
119
177
}
120
178
vulns = append (vulns , vuln )
121
179
}
180
+ for m , vg := range unaffectedMods {
181
+ for _ , v0 := range vg {
182
+ vuln := Vuln {
183
+ ID : v0 .ID ,
184
+ Details : v0 .Details ,
185
+ Aliases : v0 .Aliases ,
186
+ ModPath : m ,
187
+ URL : href (v0 ),
188
+ CurrentVersion : "" ,
189
+ FixedVersion : fixed (v0 ),
190
+ }
191
+ vulns = append (vulns , vuln )
192
+ }
193
+ }
122
194
return vulns , nil
123
195
}
0 commit comments