Skip to content

Commit

Permalink
internal/lsp/command: add RunVulncheckExp
Browse files Browse the repository at this point in the history
This is a command that runs govulncheck-like analysis.
This is highly experimental and can change any time,
so we mark it with the "Exp" suffix. Once the interface
becomes stable, we will rename this command.

It returns VulncheckResult that can be encoded as
a JSON message. The result includes all potentially
affecting vulnerabilities, and sample traces.

This feature is currently available only when gopls
is compiled with go1.18. Otherwise, the command will
return an error.

Updates golang/go#50577
Updates golang/vscode-go#2096

Change-Id: Ia37b0555f7bf98760292c9f68e50fb70dd494522
Reviewed-on: https://go-review.googlesource.com/c/tools/+/395576
Trust: Hyang-Ah Hana Kim <[email protected]>
Run-TryBot: Hyang-Ah Hana Kim <[email protected]>
Reviewed-by: Jonathan Amsterdam <[email protected]>
gopls-CI: kokoro <[email protected]>
TryBot-Result: Gopher Robot <[email protected]>
  • Loading branch information
hyangah committed Mar 24, 2022
1 parent 4737f45 commit cd31eaa
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 4 deletions.
35 changes: 35 additions & 0 deletions gopls/doc/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,41 @@ Args:
}
```

### **Run vulncheck (experimental)**
Identifier: `gopls.run_vulncheck_exp`

Run vulnerability check (`govulncheck`).

Args:

```
{
// Dir is the directory from which vulncheck will run from.
"Dir": string,
// Package pattern. E.g. "", ".", "./...".
"Pattern": string,
}
```

Result:

```
{
"Vuln": []{
"id": string,
"details": string,
"aliases": []string,
"symbol": string,
"pkg_path": string,
"mod_path": string,
"url": string,
"current_version": string,
"fixed_version": string,
"call_stacks": [][]golang.org/x/tools/internal/lsp/command.StackEntry,
},
}
```

### **Start the gopls debug server**
Identifier: `gopls.start_debugging`

Expand Down
3 changes: 3 additions & 0 deletions gopls/internal/hooks/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ package hooks // import "golang.org/x/tools/gopls/internal/hooks"
import (
"context"

"golang.org/x/tools/gopls/internal/vulncheck"
"golang.org/x/tools/internal/lsp/source"
"mvdan.cc/gofumpt/format"
"mvdan.cc/xurls/v2"
Expand All @@ -28,4 +29,6 @@ func Options(options *source.Options) {
})
}
updateAnalyzers(options)

options.Govulncheck = vulncheck.Govulncheck
}
42 changes: 38 additions & 4 deletions gopls/internal/vulncheck/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,56 @@
//go:build go1.18
// +build go1.18

// Package vulncheck provides an analysis command
// that runs vulnerability analysis using data from
// golang.org/x/exp/vulncheck.
// This package requires go1.18 or newer.
package vulncheck

import (
"context"
"fmt"
"os"
"strings"

"golang.org/x/exp/vulncheck"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/lsp/command"
"golang.org/x/vuln/client"
)

func init() {
Govulncheck = govulncheck
}

func govulncheck(ctx context.Context, cfg *packages.Config, args command.VulncheckArgs) (res command.VulncheckResult, _ error) {
if args.Pattern == "" {
args.Pattern = "."
}

dbClient, err := client.NewClient(findGOVULNDB(cfg), client.Options{HTTPCache: defaultCache()})
if err != nil {
return res, err
}

c := cmd{Client: dbClient}
vulns, err := c.Run(ctx, cfg, args.Pattern)
if err != nil {
return res, err
}

res.Vuln = vulns
return res, err
}

func findGOVULNDB(cfg *packages.Config) []string {
for _, kv := range cfg.Env {
if strings.HasPrefix(kv, "GOVULNDB=") {
return strings.Split(kv[len("GOVULNDB="):], ",")
}
}
if GOVULNDB := os.Getenv("GOVULNDB"); GOVULNDB != "" {
return strings.Split(GOVULNDB, ",")
}
return []string{"https://storage.googleapis.com/go-vulndb"}
}

type Vuln = command.Vuln
type CallStack = command.CallStack
type StackEntry = command.StackEntry
Expand Down
23 changes: 23 additions & 0 deletions gopls/internal/vulncheck/vulncheck.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2022 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 vulncheck provides an analysis command
// that runs vulnerability analysis using data from
// golang.org/x/exp/vulncheck.
// This package requires go1.18 or newer.
package vulncheck

import (
"context"
"errors"

"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/lsp/command"
)

// Govulncheck runs the in-process govulncheck implementation.
// With go1.18+, this is swapped with the real implementation.
var Govulncheck = func(ctx context.Context, cfg *packages.Config, args command.VulncheckArgs) (res command.VulncheckResult, _ error) {
return res, errors.New("not implemented")
}
33 changes: 33 additions & 0 deletions internal/lsp/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"golang.org/x/mod/modfile"
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/gocommand"
"golang.org/x/tools/internal/lsp/command"
Expand Down Expand Up @@ -781,3 +782,35 @@ func (c *commandHandler) StartDebugging(ctx context.Context, args command.Debugg
result.URLs = []string{"http://" + listenedAddr}
return result, nil
}

func (c *commandHandler) RunVulncheckExp(ctx context.Context, args command.VulncheckArgs) (result command.VulncheckResult, _ error) {
err := c.run(ctx, commandConfig{
progress: "Running vulncheck",
requireSave: true,
forURI: args.Dir, // Will dir work?
}, func(ctx context.Context, deps commandDeps) error {
view := deps.snapshot.View()
opts := view.Options()
if opts == nil || opts.Hooks.Govulncheck == nil {
return errors.New("vulncheck feature is not available")
}

buildFlags := opts.BuildFlags // XXX: is session.Options equivalent to view.Options?
var viewEnv []string
if e := opts.EnvSlice(); e != nil {
viewEnv = append(os.Environ(), e...)
}
cfg := &packages.Config{
Context: ctx,
Tests: true, // TODO(hyangah): add a field in args.
BuildFlags: buildFlags,
Env: viewEnv,
Dir: view.Folder().Filename(),
// TODO(hyangah): configure overlay
}
var err error
result, err = opts.Hooks.Govulncheck(ctx, cfg, args)
return err
})
return result, err
}
20 changes: 20 additions & 0 deletions internal/lsp/command/command_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions internal/lsp/command/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ type Interface interface {
// Start the gopls debug server if it isn't running, and return the debug
// address.
StartDebugging(context.Context, DebuggingArgs) (DebuggingResult, error)

// RunVulncheckExp: Run vulncheck (experimental)
//
// Run vulnerability check (`govulncheck`).
RunVulncheckExp(context.Context, VulncheckArgs) (VulncheckResult, error)
}

type RunTestsArgs struct {
Expand Down
7 changes: 7 additions & 0 deletions internal/lsp/source/api_json.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions internal/lsp/source/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import (
"golang.org/x/tools/go/analysis/passes/unsafeptr"
"golang.org/x/tools/go/analysis/passes/unusedresult"
"golang.org/x/tools/go/analysis/passes/unusedwrite"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/lsp/analysis/fillreturns"
"golang.org/x/tools/internal/lsp/analysis/fillstruct"
"golang.org/x/tools/internal/lsp/analysis/infertypeargs"
Expand Down Expand Up @@ -476,6 +477,9 @@ type Hooks struct {
TypeErrorAnalyzers map[string]*Analyzer
ConvenienceAnalyzers map[string]*Analyzer
StaticcheckAnalyzers map[string]*Analyzer

// Govulncheck is the implementation of the Govulncheck gopls command.
Govulncheck func(context.Context, *packages.Config, command.VulncheckArgs) (command.VulncheckResult, error)
}

// InternalOptions contains settings that are not intended for use by the
Expand Down Expand Up @@ -703,6 +707,7 @@ func (o *Options) Clone() *Options {
ComputeEdits: o.ComputeEdits,
GofumptFormat: o.GofumptFormat,
URLRegexp: o.URLRegexp,
Govulncheck: o.Govulncheck,
},
ServerOptions: o.ServerOptions,
UserOptions: o.UserOptions,
Expand Down

0 comments on commit cd31eaa

Please sign in to comment.