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) | ||
|  | } |