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 }