|
|
@@ -0,0 +1,169 @@
|
|
|
+// 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)
|
|
|
+}
|