src

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

binary.go (2814B)


      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 	"encoding/json"
     10 	"errors"
     11 	"os"
     12 	"runtime/debug"
     13 
     14 	"golang.org/x/tools/go/packages"
     15 	"golang.org/x/vuln/internal/buildinfo"
     16 	"golang.org/x/vuln/internal/client"
     17 	"golang.org/x/vuln/internal/derrors"
     18 	"golang.org/x/vuln/internal/govulncheck"
     19 	"golang.org/x/vuln/internal/vulncheck"
     20 )
     21 
     22 // runBinary detects presence of vulnerable symbols in an executable or its minimal blob representation.
     23 func runBinary(ctx context.Context, handler govulncheck.Handler, cfg *config, client *client.Client) (err error) {
     24 	defer derrors.Wrap(&err, "govulncheck")
     25 
     26 	bin, err := createBin(cfg.patterns[0])
     27 	if err != nil {
     28 		return err
     29 	}
     30 
     31 	p := &govulncheck.Progress{Message: binaryProgressMessage}
     32 	if err := handler.Progress(p); err != nil {
     33 		return err
     34 	}
     35 	return vulncheck.Binary(ctx, handler, bin, &cfg.Config, client)
     36 }
     37 
     38 func createBin(path string) (*vulncheck.Bin, error) {
     39 	// First check if the path points to a Go binary. Otherwise, blob
     40 	// parsing might json decode a Go binary which takes time.
     41 	//
     42 	// TODO(#64716): use fingerprinting to make this precise, clean, and fast.
     43 	mods, packageSymbols, bi, err := buildinfo.ExtractPackagesAndSymbols(path)
     44 	if err == nil {
     45 		var main *packages.Module
     46 		if bi.Main.Path != "" {
     47 			main = &packages.Module{
     48 				Path:    bi.Main.Path,
     49 				Version: bi.Main.Version,
     50 			}
     51 		}
     52 
     53 		return &vulncheck.Bin{
     54 			Path:       bi.Path,
     55 			Main:       main,
     56 			Modules:    mods,
     57 			PkgSymbols: packageSymbols,
     58 			GoVersion:  bi.GoVersion,
     59 			GOOS:       findSetting("GOOS", bi),
     60 			GOARCH:     findSetting("GOARCH", bi),
     61 		}, nil
     62 	}
     63 
     64 	// Otherwise, see if the path points to a valid blob.
     65 	bin := parseBlob(path)
     66 	if bin != nil {
     67 		return bin, nil
     68 	}
     69 
     70 	return nil, errors.New("unrecognized binary format")
     71 }
     72 
     73 // parseBlob extracts vulncheck.Bin from a valid blob at path.
     74 // If it cannot recognize a valid blob, returns nil.
     75 func parseBlob(path string) *vulncheck.Bin {
     76 	from, err := os.Open(path)
     77 	if err != nil {
     78 		return nil
     79 	}
     80 	defer from.Close()
     81 
     82 	dec := json.NewDecoder(from)
     83 
     84 	var h header
     85 	if err := dec.Decode(&h); err != nil {
     86 		return nil // no header
     87 	} else if h.Name != extractModeID || h.Version != extractModeVersion {
     88 		return nil // invalid header
     89 	}
     90 
     91 	var b vulncheck.Bin
     92 	if err := dec.Decode(&b); err != nil {
     93 		return nil // no body
     94 	}
     95 	if dec.More() {
     96 		return nil // we want just header and body, nothing else
     97 	}
     98 	return &b
     99 }
    100 
    101 // findSetting returns value of setting from bi if present.
    102 // Otherwise, returns "".
    103 func findSetting(setting string, bi *debug.BuildInfo) string {
    104 	for _, s := range bi.Settings {
    105 		if s.Key == setting {
    106 			return s.Value
    107 		}
    108 	}
    109 	return ""
    110 }