File: binary.go

package info (click to toggle)
golang-golang-x-vuln 1.0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,400 kB
  • sloc: sh: 161; asm: 40; makefile: 7
file content (103 lines) | stat: -rw-r--r-- 2,616 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 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.

//go:build go1.18
// +build go1.18

package scan

import (
	"context"
	"encoding/json"
	"errors"
	"io"
	"os"
	"runtime/debug"

	"golang.org/x/vuln/internal/buildinfo"
	"golang.org/x/vuln/internal/client"
	"golang.org/x/vuln/internal/derrors"
	"golang.org/x/vuln/internal/govulncheck"
	"golang.org/x/vuln/internal/vulncheck"
)

// runBinary detects presence of vulnerable symbols in an executable or its minimal blob representation.
func runBinary(ctx context.Context, handler govulncheck.Handler, cfg *config, client *client.Client) (err error) {
	defer derrors.Wrap(&err, "govulncheck")

	bin, err := createBin(cfg.patterns[0])
	if err != nil {
		return err
	}

	p := &govulncheck.Progress{Message: binaryProgressMessage}
	if err := handler.Progress(p); err != nil {
		return err
	}
	return vulncheck.Binary(ctx, handler, bin, &cfg.Config, client)
}

func createBin(path string) (*vulncheck.Bin, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	// First check if the path points to a Go binary. Otherwise, blob
	// parsing might json decode a Go binary which takes time.
	//
	// TODO(#64716): use fingerprinting to make this precise, clean, and fast.
	mods, packageSymbols, bi, err := buildinfo.ExtractPackagesAndSymbols(f)
	if err == nil {
		return &vulncheck.Bin{
			Modules:    mods,
			PkgSymbols: packageSymbols,
			GoVersion:  bi.GoVersion,
			GOOS:       findSetting("GOOS", bi),
			GOARCH:     findSetting("GOARCH", bi),
		}, nil
	}

	// Otherwise, see if the path points to a valid blob.
	bin := parseBlob(f)
	if bin != nil {
		return bin, nil
	}

	return nil, errors.New("unrecognized binary format")
}

// parseBlob extracts vulncheck.Bin from a valid blob. If it
// cannot recognize a valid blob, returns nil.
func parseBlob(from io.Reader) *vulncheck.Bin {
	dec := json.NewDecoder(from)

	var h header
	if err := dec.Decode(&h); err != nil {
		return nil // no header
	} else if h.Name != extractModeID || h.Version != extractModeVersion {
		return nil // invalid header
	}

	var b vulncheck.Bin
	if err := dec.Decode(&b); err != nil {
		return nil // no body
	}
	if dec.More() {
		return nil // we want just header and body, nothing else
	}
	return &b
}

// findSetting returns value of setting from bi if present.
// Otherwise, returns "".
func findSetting(setting string, bi *debug.BuildInfo) string {
	for _, s := range bi.Settings {
		if s.Key == setting {
			return s.Value
		}
	}
	return ""
}