github.com/bcnmy/go-ethereum@v1.10.27/cmd/geth/version_check.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of go-ethereum.
     3  //
     4  // go-ethereum is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // go-ethereum is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU General Public License
    15  // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"os"
    26  	"regexp"
    27  	"strings"
    28  
    29  	"github.com/ethereum/go-ethereum/log"
    30  	"github.com/jedisct1/go-minisign"
    31  	"github.com/urfave/cli/v2"
    32  )
    33  
    34  var gethPubKeys []string = []string{
    35  	//@holiman, minisign public key FB1D084D39BAEC24
    36  	"RWQk7Lo5TQgd+wxBNZM+Zoy+7UhhMHaWKzqoes9tvSbFLJYZhNTbrIjx",
    37  	//minisign public key 138B1CA303E51687
    38  	"RWSHFuUDoxyLEzjszuWZI1xStS66QTyXFFZG18uDfO26CuCsbckX1e9J",
    39  	//minisign public key FD9813B2D2098484
    40  	"RWSEhAnSshOY/b+GmaiDkObbCWefsAoavjoLcPjBo1xn71yuOH5I+Lts",
    41  }
    42  
    43  type vulnJson struct {
    44  	Name        string
    45  	Uid         string
    46  	Summary     string
    47  	Description string
    48  	Links       []string
    49  	Introduced  string
    50  	Fixed       string
    51  	Published   string
    52  	Severity    string
    53  	Check       string
    54  	CVE         string
    55  }
    56  
    57  func versionCheck(ctx *cli.Context) error {
    58  	url := ctx.String(VersionCheckUrlFlag.Name)
    59  	version := ctx.String(VersionCheckVersionFlag.Name)
    60  	log.Info("Checking vulnerabilities", "version", version, "url", url)
    61  	return checkCurrent(url, version)
    62  }
    63  
    64  func checkCurrent(url, current string) error {
    65  	var (
    66  		data []byte
    67  		sig  []byte
    68  		err  error
    69  	)
    70  	if data, err = fetch(url); err != nil {
    71  		return fmt.Errorf("could not retrieve data: %w", err)
    72  	}
    73  	if sig, err = fetch(fmt.Sprintf("%v.minisig", url)); err != nil {
    74  		return fmt.Errorf("could not retrieve signature: %w", err)
    75  	}
    76  	if err = verifySignature(gethPubKeys, data, sig); err != nil {
    77  		return err
    78  	}
    79  	var vulns []vulnJson
    80  	if err = json.Unmarshal(data, &vulns); err != nil {
    81  		return err
    82  	}
    83  	allOk := true
    84  	for _, vuln := range vulns {
    85  		r, err := regexp.Compile(vuln.Check)
    86  		if err != nil {
    87  			return err
    88  		}
    89  		if r.MatchString(current) {
    90  			allOk = false
    91  			fmt.Printf("## Vulnerable to %v (%v)\n\n", vuln.Uid, vuln.Name)
    92  			fmt.Printf("Severity: %v\n", vuln.Severity)
    93  			fmt.Printf("Summary : %v\n", vuln.Summary)
    94  			fmt.Printf("Fixed in: %v\n", vuln.Fixed)
    95  			if len(vuln.CVE) > 0 {
    96  				fmt.Printf("CVE: %v\n", vuln.CVE)
    97  			}
    98  			if len(vuln.Links) > 0 {
    99  				fmt.Printf("References:\n")
   100  				for _, ref := range vuln.Links {
   101  					fmt.Printf("\t- %v\n", ref)
   102  				}
   103  			}
   104  			fmt.Println()
   105  		}
   106  	}
   107  	if allOk {
   108  		fmt.Println("No vulnerabilities found")
   109  	}
   110  	return nil
   111  }
   112  
   113  // fetch makes an HTTP request to the given url and returns the response body
   114  func fetch(url string) ([]byte, error) {
   115  	if filep := strings.TrimPrefix(url, "file://"); filep != url {
   116  		return os.ReadFile(filep)
   117  	}
   118  	res, err := http.Get(url)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  	defer res.Body.Close()
   123  	body, err := io.ReadAll(res.Body)
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  	return body, nil
   128  }
   129  
   130  // verifySignature checks that the sigData is a valid signature of the given
   131  // data, for pubkey GethPubkey
   132  func verifySignature(pubkeys []string, data, sigdata []byte) error {
   133  	sig, err := minisign.DecodeSignature(string(sigdata))
   134  	if err != nil {
   135  		return err
   136  	}
   137  	// find the used key
   138  	var key *minisign.PublicKey
   139  	for _, pubkey := range pubkeys {
   140  		pub, err := minisign.NewPublicKey(pubkey)
   141  		if err != nil {
   142  			// our pubkeys should be parseable
   143  			return err
   144  		}
   145  		if pub.KeyId != sig.KeyId {
   146  			continue
   147  		}
   148  		key = &pub
   149  		break
   150  	}
   151  	if key == nil {
   152  		log.Info("Signing key not trusted", "keyid", keyID(sig.KeyId), "error", err)
   153  		return errors.New("signature could not be verified")
   154  	}
   155  	if ok, err := key.Verify(data, sig); !ok || err != nil {
   156  		log.Info("Verification failed error", "keyid", keyID(key.KeyId), "error", err)
   157  		return errors.New("signature could not be verified")
   158  	}
   159  	return nil
   160  }
   161  
   162  // keyID turns a binary minisign key ID into a hex string.
   163  // Note: key IDs are printed in reverse byte order.
   164  func keyID(id [8]byte) string {
   165  	var rev [8]byte
   166  	for i := range id {
   167  		rev[len(rev)-1-i] = id[i]
   168  	}
   169  	return fmt.Sprintf("%X", rev)
   170  }