diff --git a/gopls/internal/server/vulncheck_prompt.go b/gopls/internal/server/vulncheck_prompt.go
index 32a5925..9edb727 100644
--- a/gopls/internal/server/vulncheck_prompt.go
+++ b/gopls/internal/server/vulncheck_prompt.go
@@ -14,12 +14,18 @@
"os"
"path/filepath"
"reflect"
+ "sort"
+ "strings"
"time"
"golang.org/x/mod/modfile"
+ "golang.org/x/tools/gopls/internal/cache"
"golang.org/x/tools/gopls/internal/filecache"
+ "golang.org/x/tools/gopls/internal/progress"
"golang.org/x/tools/gopls/internal/protocol"
"golang.org/x/tools/gopls/internal/settings"
+ "golang.org/x/tools/gopls/internal/vulncheck"
+ "golang.org/x/tools/gopls/internal/vulncheck/scan"
"golang.org/x/tools/internal/event"
)
@@ -125,40 +131,99 @@
govulncheckLink := "[govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck)"
message := fmt.Sprintf("Dependencies have changed in %s, would you like to run %s to check for vulnerabilities?", fileLink, govulncheckLink)
- preference, err := getVulncheckPreference()
+ action, err := getVulncheckPreference()
if err != nil {
event.Error(ctx, "reading vulncheck preference", err)
}
- if preference == "Always" || preference == "Never" {
- // TODO: Update this so that we run vulncheck when Always is set.
- return
- }
- action, err := showMessageRequest(ctx, s.client, protocol.Info, message, "Yes", "No", "Always", "Never")
- if err != nil {
- event.Error(ctx, "showing go.mod changed notification", err)
- return
- }
- if action == "Always" || action == "Never" {
- if err := setVulncheckPreference(action); err != nil {
- event.Error(ctx, "writing vulncheck preference", err)
- showMessage(ctx, s.client, protocol.Error, fmt.Sprintf("Failed to save vulncheck preference: %v", err))
+ if action == "" {
+ action, err = showMessageRequest(ctx, s.client, protocol.Info, message, "Yes", "No", "Always", "Never")
+ if err != nil {
+ event.Error(ctx, "showing go.mod changed notification", err)
+ return
+ }
+
+ if action == "Always" || action == "Never" {
+ if err := setVulncheckPreference(action); err != nil {
+ event.Error(ctx, "writing vulncheck preference", err)
+ showMessage(ctx, s.client, protocol.Error, fmt.Sprintf("Failed to save vulncheck preference: %v", err))
+ }
+ }
+
+ if action == "" {
+ // No user input gathered from prompt.
+ return
}
}
- // TODO: Implement the logic to run govulncheck when action is "Yes" or "Always".
- if action == "No" || action == "Never" || action == "" {
- return // Skip the check and don't update the hash.
+ if action == "Yes" || action == "Always" {
+ s.runVulncheck(ctx, change.URI)
}
if err := filecache.Set(goModHashKind, pathHash, []byte(newHash)); err != nil {
event.Error(ctx, "writing new go.mod hash to filecache", err)
- return
}
}
}()
}
+func (s *server) runVulncheck(ctx context.Context, uri protocol.DocumentURI) {
+ err := func() error {
+ snapshot, release, err := s.session.SnapshotOf(ctx, uri)
+ if err != nil {
+ return err
+ }
+ defer release()
+
+ work := s.progress.Start(ctx, GoVulncheckCommandTitle, "Running govulncheck...", nil, nil)
+ defer work.End(ctx, "Done.")
+ workDoneWriter := progress.NewWorkDoneWriter(ctx, work)
+
+ dir := uri.DirPath()
+ pattern := "./..."
+
+ result, err := scan.RunGovulncheck(ctx, pattern, snapshot, dir, workDoneWriter)
+ if err != nil {
+ return err
+ }
+
+ snapshot, release, err = s.session.InvalidateView(ctx, snapshot.View(), cache.StateChange{
+ Vulns: map[protocol.DocumentURI]*vulncheck.Result{uri: result},
+ })
+ if err != nil {
+ return err
+ }
+ defer release()
+
+ s.diagnoseSnapshot(snapshot.BackgroundContext(), snapshot, nil, 0)
+
+ affecting := make(map[string]bool, len(result.Entries))
+ for _, finding := range result.Findings {
+ if len(finding.Trace) > 1 {
+ affecting[finding.OSV] = true
+ }
+ }
+ if len(affecting) == 0 {
+ showMessage(ctx, s.client, protocol.Info, "No vulnerabilities found")
+ return nil
+ }
+ affectingOSVs := make([]string, 0, len(affecting))
+ for id := range affecting {
+ affectingOSVs = append(affectingOSVs, id)
+ }
+ sort.Strings(affectingOSVs)
+
+ showMessage(ctx, s.client, protocol.Warning, fmt.Sprintf("Found %v", strings.Join(affectingOSVs, ", ")))
+
+ return nil
+ }()
+
+ if err != nil {
+ event.Error(ctx, "running vulncheck", err)
+ showMessage(ctx, s.client, protocol.Error, fmt.Sprintf("Failed to run vulncheck: %v", err))
+ }
+}
+
type vulncheckConfig struct {
Vulncheck string `json:"vulncheck"`
}
@@ -193,7 +258,6 @@
return err
}
path := filepath.Join(goplsDir, "settings.json")
- fmt.Printf("Writing vulncheck preference to %s\n", path)
config := vulncheckConfig{Vulncheck: preference}
content, err := json.Marshal(config)
if err != nil {