src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

run.go (4353B)


      1 // Copyright 2022 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package scan
      6 
      7 import (
      8 	"context"
      9 	"fmt"
     10 	"io"
     11 	"os/exec"
     12 	"path"
     13 	"path/filepath"
     14 	"runtime/debug"
     15 	"strings"
     16 	"time"
     17 
     18 	"golang.org/x/telemetry/counter"
     19 	"golang.org/x/vuln/internal/client"
     20 	"golang.org/x/vuln/internal/govulncheck"
     21 	"golang.org/x/vuln/internal/openvex"
     22 	"golang.org/x/vuln/internal/sarif"
     23 )
     24 
     25 // RunGovulncheck performs main govulncheck functionality.
     26 // On failure, the returned error wraps an exit code error (see scan.Cmd.Wait).
     27 func RunGovulncheck(ctx context.Context, env []string, r io.Reader, stdout io.Writer, stderr io.Writer, args []string) error {
     28 	cfg := &config{env: env}
     29 	if err := parseFlags(cfg, stderr, args); err != nil {
     30 		return err
     31 	}
     32 
     33 	client, err := client.NewClient(cfg.db, nil)
     34 	if err != nil {
     35 		return fmt.Errorf("creating client: %w", err)
     36 	}
     37 
     38 	prepareConfig(ctx, cfg, client)
     39 	var handler govulncheck.Handler
     40 	switch cfg.format {
     41 	case formatJSON:
     42 		handler = govulncheck.NewJSONHandler(stdout)
     43 	case formatSarif:
     44 		handler = sarif.NewHandler(stdout)
     45 	case formatOpenVEX:
     46 		handler = openvex.NewHandler(stdout)
     47 	default:
     48 		th := NewTextHandler(stdout)
     49 		cfg.show.Update(th)
     50 		handler = th
     51 	}
     52 
     53 	if err := handler.Config(&cfg.Config); err != nil {
     54 		return err
     55 	}
     56 
     57 	if cfg.version {
     58 		// If the -version flag is passed, exit before doing anything else. This is different than
     59 		// passing -show which includes "version".
     60 		return nil
     61 	}
     62 
     63 	incTelemetryFlagCounters(cfg)
     64 
     65 	switch cfg.ScanMode {
     66 	case govulncheck.ScanModeSource:
     67 		dir := filepath.FromSlash(cfg.dir)
     68 		err = runSource(ctx, handler, cfg, client, dir)
     69 	case govulncheck.ScanModeBinary:
     70 		err = runBinary(ctx, handler, cfg, client)
     71 	case govulncheck.ScanModeExtract:
     72 		return runExtract(cfg, stdout)
     73 	case govulncheck.ScanModeQuery:
     74 		err = runQuery(ctx, handler, cfg, client)
     75 	case govulncheck.ScanModeConvert:
     76 		err = govulncheck.HandleJSON(r, handler)
     77 	}
     78 	if err != nil {
     79 		return err
     80 	}
     81 	return Flush(handler)
     82 }
     83 
     84 func prepareConfig(ctx context.Context, cfg *config, client *client.Client) {
     85 	cfg.ProtocolVersion = govulncheck.ProtocolVersion
     86 	cfg.DB = cfg.db
     87 	if cfg.ScanMode == govulncheck.ScanModeSource && cfg.GoVersion == "" {
     88 		const goverPrefix = "GOVERSION="
     89 		for _, env := range cfg.env {
     90 			if val := strings.TrimPrefix(env, goverPrefix); val != env {
     91 				cfg.GoVersion = val
     92 			}
     93 		}
     94 		if cfg.GoVersion == "" {
     95 			if out, err := exec.Command("go", "env", "GOVERSION").Output(); err == nil {
     96 				cfg.GoVersion = strings.TrimSpace(string(out))
     97 			}
     98 		}
     99 	}
    100 	if bi, ok := debug.ReadBuildInfo(); ok {
    101 		scannerVersion(cfg, bi)
    102 	}
    103 	if mod, err := client.LastModifiedTime(ctx); err == nil {
    104 		cfg.DBLastModified = &mod
    105 	}
    106 }
    107 
    108 // scannerVersion reconstructs the current version of
    109 // this binary used from the build info.
    110 func scannerVersion(cfg *config, bi *debug.BuildInfo) {
    111 	if bi.Path != "" {
    112 		cfg.ScannerName = strings.TrimSuffix(path.Base(bi.Path), ".test")
    113 	}
    114 	if bi.Main.Version != "" && bi.Main.Version != "(devel)" {
    115 		cfg.ScannerVersion = bi.Main.Version
    116 		return
    117 	}
    118 
    119 	// TODO(https://go.dev/issue/29228): we need to manually construct the
    120 	// version string when it is "(devel)" until #29228 is resolved.
    121 	var revision, at string
    122 	for _, s := range bi.Settings {
    123 		if s.Key == "vcs.revision" {
    124 			revision = s.Value
    125 		}
    126 		if s.Key == "vcs.time" {
    127 			at = s.Value
    128 		}
    129 	}
    130 	buf := strings.Builder{}
    131 	buf.WriteString("v0.0.0")
    132 	if revision != "" {
    133 		buf.WriteString("-")
    134 		buf.WriteString(revision[:12])
    135 	}
    136 	if at != "" {
    137 		// commit time is of the form 2023-01-25T19:57:54Z
    138 		p, err := time.Parse(time.RFC3339, at)
    139 		if err == nil {
    140 			buf.WriteString("-")
    141 			buf.WriteString(p.Format("20060102150405"))
    142 		}
    143 	}
    144 	cfg.ScannerVersion = buf.String()
    145 }
    146 
    147 func incTelemetryFlagCounters(cfg *config) {
    148 	counter.Inc(fmt.Sprintf("govulncheck/mode:%s", cfg.ScanMode))
    149 	counter.Inc(fmt.Sprintf("govulncheck/scan:%s", cfg.ScanLevel))
    150 	counter.Inc(fmt.Sprintf("govulncheck/format:%s", cfg.format))
    151 
    152 	if len(cfg.show) == 0 {
    153 		counter.Inc("govulncheck/show:none")
    154 	}
    155 	for _, s := range cfg.show {
    156 		counter.Inc(fmt.Sprintf("govulncheck/show:%s", s))
    157 	}
    158 }
    159 
    160 func Flush(h govulncheck.Handler) error {
    161 	if th, ok := h.(interface{ Flush() error }); ok {
    162 		return th.Flush()
    163 	}
    164 	return nil
    165 }