170 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			170 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2020 The go-ethereum Authors
							 | 
						||
| 
								 | 
							
								// This file is part of go-ethereum.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// go-ethereum is free software: you can redistribute it and/or modify
							 | 
						||
| 
								 | 
							
								// it under the terms of the GNU General Public License as published by
							 | 
						||
| 
								 | 
							
								// the Free Software Foundation, either version 3 of the License, or
							 | 
						||
| 
								 | 
							
								// (at your option) any later version.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// go-ethereum is distributed in the hope that it will be useful,
							 | 
						||
| 
								 | 
							
								// but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
							 | 
						||
| 
								 | 
							
								// GNU General Public License for more details.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// You should have received a copy of the GNU General Public License
							 | 
						||
| 
								 | 
							
								// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package main
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"encoding/json"
							 | 
						||
| 
								 | 
							
									"errors"
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"io/ioutil"
							 | 
						||
| 
								 | 
							
									"net/http"
							 | 
						||
| 
								 | 
							
									"regexp"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/ethereum/go-ethereum/log"
							 | 
						||
| 
								 | 
							
									"github.com/jedisct1/go-minisign"
							 | 
						||
| 
								 | 
							
									"gopkg.in/urfave/cli.v1"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var gethPubKeys []string = []string{
							 | 
						||
| 
								 | 
							
									//@holiman, minisign public key FB1D084D39BAEC24
							 | 
						||
| 
								 | 
							
									"RWQk7Lo5TQgd+wxBNZM+Zoy+7UhhMHaWKzqoes9tvSbFLJYZhNTbrIjx",
							 | 
						||
| 
								 | 
							
									//minisign public key 138B1CA303E51687
							 | 
						||
| 
								 | 
							
									"RWSHFuUDoxyLEzjszuWZI1xStS66QTyXFFZG18uDfO26CuCsbckX1e9J",
							 | 
						||
| 
								 | 
							
									//minisign public key FD9813B2D2098484
							 | 
						||
| 
								 | 
							
									"RWSEhAnSshOY/b+GmaiDkObbCWefsAoavjoLcPjBo1xn71yuOH5I+Lts",
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type vulnJson struct {
							 | 
						||
| 
								 | 
							
									Name        string
							 | 
						||
| 
								 | 
							
									Uid         string
							 | 
						||
| 
								 | 
							
									Summary     string
							 | 
						||
| 
								 | 
							
									Description string
							 | 
						||
| 
								 | 
							
									Links       []string
							 | 
						||
| 
								 | 
							
									Introduced  string
							 | 
						||
| 
								 | 
							
									Fixed       string
							 | 
						||
| 
								 | 
							
									Published   string
							 | 
						||
| 
								 | 
							
									Severity    string
							 | 
						||
| 
								 | 
							
									Check       string
							 | 
						||
| 
								 | 
							
									CVE         string
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func versionCheck(ctx *cli.Context) error {
							 | 
						||
| 
								 | 
							
									url := ctx.String(VersionCheckUrlFlag.Name)
							 | 
						||
| 
								 | 
							
									version := ctx.String(VersionCheckVersionFlag.Name)
							 | 
						||
| 
								 | 
							
									log.Info("Checking vulnerabilities", "version", version, "url", url)
							 | 
						||
| 
								 | 
							
									return checkCurrent(url, version)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func checkCurrent(url, current string) error {
							 | 
						||
| 
								 | 
							
									var (
							 | 
						||
| 
								 | 
							
										data []byte
							 | 
						||
| 
								 | 
							
										sig  []byte
							 | 
						||
| 
								 | 
							
										err  error
							 | 
						||
| 
								 | 
							
									)
							 | 
						||
| 
								 | 
							
									if data, err = fetch(url); err != nil {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("could not retrieve data: %w", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if sig, err = fetch(fmt.Sprintf("%v.minisig", url)); err != nil {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("could not retrieve signature: %w", err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if err = verifySignature(gethPubKeys, data, sig); err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									var vulns []vulnJson
							 | 
						||
| 
								 | 
							
									if err = json.Unmarshal(data, &vulns); err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									allOk := true
							 | 
						||
| 
								 | 
							
									for _, vuln := range vulns {
							 | 
						||
| 
								 | 
							
										r, err := regexp.Compile(vuln.Check)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if r.MatchString(current) {
							 | 
						||
| 
								 | 
							
											allOk = false
							 | 
						||
| 
								 | 
							
											fmt.Printf("## Vulnerable to %v (%v)\n\n", vuln.Uid, vuln.Name)
							 | 
						||
| 
								 | 
							
											fmt.Printf("Severity: %v\n", vuln.Severity)
							 | 
						||
| 
								 | 
							
											fmt.Printf("Summary : %v\n", vuln.Summary)
							 | 
						||
| 
								 | 
							
											fmt.Printf("Fixed in: %v\n", vuln.Fixed)
							 | 
						||
| 
								 | 
							
											if len(vuln.CVE) > 0 {
							 | 
						||
| 
								 | 
							
												fmt.Printf("CVE: %v\n", vuln.CVE)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if len(vuln.Links) > 0 {
							 | 
						||
| 
								 | 
							
												fmt.Printf("References:\n")
							 | 
						||
| 
								 | 
							
												for _, ref := range vuln.Links {
							 | 
						||
| 
								 | 
							
													fmt.Printf("\t- %v\n", ref)
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											fmt.Println()
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if allOk {
							 | 
						||
| 
								 | 
							
										fmt.Println("No vulnerabilities found")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// fetch makes an HTTP request to the given url and returns the response body
							 | 
						||
| 
								 | 
							
								func fetch(url string) ([]byte, error) {
							 | 
						||
| 
								 | 
							
									if filep := strings.TrimPrefix(url, "file://"); filep != url {
							 | 
						||
| 
								 | 
							
										return ioutil.ReadFile(filep)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									res, err := http.Get(url)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									defer res.Body.Close()
							 | 
						||
| 
								 | 
							
									body, err := ioutil.ReadAll(res.Body)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return body, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// verifySignature checks that the sigData is a valid signature of the given
							 | 
						||
| 
								 | 
							
								// data, for pubkey GethPubkey
							 | 
						||
| 
								 | 
							
								func verifySignature(pubkeys []string, data, sigdata []byte) error {
							 | 
						||
| 
								 | 
							
									sig, err := minisign.DecodeSignature(string(sigdata))
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									// find the used key
							 | 
						||
| 
								 | 
							
									var key *minisign.PublicKey
							 | 
						||
| 
								 | 
							
									for _, pubkey := range pubkeys {
							 | 
						||
| 
								 | 
							
										pub, err := minisign.NewPublicKey(pubkey)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											// our pubkeys should be parseable
							 | 
						||
| 
								 | 
							
											return err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if pub.KeyId != sig.KeyId {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										key = &pub
							 | 
						||
| 
								 | 
							
										break
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if key == nil {
							 | 
						||
| 
								 | 
							
										log.Info("Signing key not trusted", "keyid", keyID(sig.KeyId), "error", err)
							 | 
						||
| 
								 | 
							
										return errors.New("signature could not be verified")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if ok, err := key.Verify(data, sig); !ok || err != nil {
							 | 
						||
| 
								 | 
							
										log.Info("Verification failed error", "keyid", keyID(key.KeyId), "error", err)
							 | 
						||
| 
								 | 
							
										return errors.New("signature could not be verified")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// keyID turns a binary minisign key ID into a hex string.
							 | 
						||
| 
								 | 
							
								// Note: key IDs are printed in reverse byte order.
							 | 
						||
| 
								 | 
							
								func keyID(id [8]byte) string {
							 | 
						||
| 
								 | 
							
									var rev [8]byte
							 | 
						||
| 
								 | 
							
									for i := range id {
							 | 
						||
| 
								 | 
							
										rev[len(rev)-1-i] = id[i]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return fmt.Sprintf("%X", rev)
							 | 
						||
| 
								 | 
							
								}
							 |