notary/internal/sumweb: add GONOVERIFY support
This CL implements respect for the $GONOVERIFY environment
variable as described in golang.org/design/25530-notary.
It also updates gosumcheck to use it.
Change-Id: I937058222eca374a5616feaa83f388f062bb64a2
Reviewed-on: https://go-review.googlesource.com/c/exp/+/172966
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Katie Hockman <katie@golang.org>
diff --git a/notary/gosumcheck/main.go b/notary/gosumcheck/main.go
index dbb0b9e..f6accb1 100644
--- a/notary/gosumcheck/main.go
+++ b/notary/gosumcheck/main.go
@@ -4,12 +4,6 @@
// Gosumcheck checks a go.sum file against a go.sum database server.
//
-// WARNING! This program is meant as a proof of concept demo and
-// should not be used in production scripts.
-// It does not set an exit status to report whether the
-// checksums matched, and it does not filter the go.sum
-// according to the $GONOVERIFY environment variable.
-//
// Usage:
//
// gosumcheck [-h H] [-k key] [-u url] [-v] go.sum
@@ -18,12 +12,23 @@
//
// The -k flag changes the go.sum database server key.
//
-// The -u flag overrides the URL of the server.
+// The -u flag overrides the URL of the server (usually set from the key name).
//
// The -v flag enables verbose output.
// In particular, it causes gosumcheck to report
// the URL and elapsed time for each server request.
//
+// WARNING! WARNING! WARNING!
+//
+// Gosumcheck is meant as a proof of concept demo and should not be
+// used in production scripts or continuous integration testing.
+// It does not cache any downloaded information from run to run,
+// making it expensive and also keeping it from detecting server
+// misbehavior or successful HTTPS man-in-the-middle timeline forks.
+//
+// To discourage misuse in automated settings, gosumcheck does not
+// set any exit status to report whether any problems were found.
+//
package main
import (
@@ -34,6 +39,7 @@
"log"
"net/http"
"os"
+ "os/exec"
"strings"
"sync"
"time"
@@ -65,7 +71,20 @@
conn := sumweb.NewConn(new(client))
- for _, arg := range flag.Args()[1:] {
+ // Look in environment explicitly, so that if 'go env' is old and
+ // doesn't know about GONOVERIFY, we at least get anything
+ // set in the environment.
+ env := os.Getenv("GONOVERIFY")
+ if env == "" {
+ out, err := exec.Command("go", "env", "GONOVERIFY").CombinedOutput()
+ if err != nil {
+ log.Fatalf("go env GONOVERIFY: %v\n%s", err, out)
+ }
+ env = strings.TrimSpace(string(out))
+ }
+ conn.SetGONOVERIFY(env)
+
+ for _, arg := range flag.Args() {
data, err := ioutil.ReadFile(arg)
if err != nil {
log.Fatal(err)
@@ -96,7 +115,12 @@
dbLines, err := conn.Lookup(f[0], f[1])
if err != nil {
- errs[i] = err.Error()
+ if err == sumweb.ErrGONOVERIFY {
+ errs[i] = fmt.Sprintf("%s@%s: %v", f[0], f[1], err)
+ } else {
+ // Otherwise Lookup properly adds the prefix itself.
+ errs[i] = err.Error()
+ }
return
}
hashAlgPrefix := f[0] + " " + f[1] + " " + f[2][:strings.Index(f[2], ":")+1]
diff --git a/notary/gosumcheck/test.bash b/notary/gosumcheck/test.bash
index bff14b0..4557b03 100755
--- a/notary/gosumcheck/test.bash
+++ b/notary/gosumcheck/test.bash
@@ -2,5 +2,6 @@
set -e
go build -o gosumcheck.exe
+export GONOVERIFY=*/text # rsc.io/text but not golang.org/x/text
./gosumcheck.exe "$@" -v test.sum
rm -f ./gosumcheck.exe
diff --git a/notary/gosumcheck/test.sum b/notary/gosumcheck/test.sum
index c7105a0..1b252ff 100644
--- a/notary/gosumcheck/test.sum
+++ b/notary/gosumcheck/test.sum
@@ -2,4 +2,5 @@
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
+rsc.io/text v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/notary/internal/sumweb/client.go b/notary/internal/sumweb/client.go
index 8c535b2..cf6891f 100644
--- a/notary/internal/sumweb/client.go
+++ b/notary/internal/sumweb/client.go
@@ -8,6 +8,7 @@
"bytes"
"errors"
"fmt"
+ "path"
"strings"
"sync"
@@ -82,6 +83,7 @@
verifiers note.Verifiers // accepted verifiers (just one, but Verifiers for note.Open)
tileReader tileReader
tileHeight int
+ noverify []string
record parCache // cache of record lookup, keyed by path@vers
tileCache parCache // cache of tile from client.ReadCache, keyed by tile
@@ -159,8 +161,59 @@
c.tileHeight = height
}
+// SetGONOVERIFY sets the list of comma-separated GONOVERIFY patterns for the Conn.
+// For any module path matching one of the patterns,
+// Lookup will return ErrGONOVERIFY.
+// Any call to SetGONOVERIFY must happen before the first call to Lookup.
+func (c *Conn) SetGONOVERIFY(list string) {
+ c.noverify = nil
+ for _, glob := range strings.Split(list, ",") {
+ if glob != "" {
+ c.noverify = append(c.noverify, glob)
+ }
+ }
+}
+
+// ErrGONOVERIFY is returned by Lookup for paths that match
+// a pattern listed in the GONOVERIFY list (set by SetGONOVERIFY,
+// usually from the environment variable).
+var ErrGONOVERIFY = errors.New("skipped (listed in GONOVERIFY)")
+
+func (c *Conn) skip(target string) bool {
+ for _, glob := range c.noverify {
+ // A glob with N+1 path elements (N slashes) needs to be matched
+ // against the first N+1 path elements of target,
+ // which end just before the N+1'th slash.
+ n := strings.Count(glob, "/")
+ prefix := target
+ // Walk target, counting slashes, truncating at the N+1'th slash.
+ for i := 0; i < len(target); i++ {
+ if target[i] == '/' {
+ if n == 0 {
+ prefix = target[:i]
+ break
+ }
+ n--
+ }
+ }
+ if n > 0 {
+ // Not enough prefix elements.
+ continue
+ }
+ matched, _ := path.Match(glob, prefix)
+ if matched {
+ return true
+ }
+ }
+ return false
+}
+
// Lookup returns the go.sum lines for the given module path and version.
func (c *Conn) Lookup(path, vers string) (lines []string, err error) {
+ if c.skip(path) {
+ return nil, ErrGONOVERIFY
+ }
+
defer func() {
if err != nil {
err = fmt.Errorf("%s@%s: %v", path, vers, err)
diff --git a/notary/internal/sumweb/client_test.go b/notary/internal/sumweb/client_test.go
index 47bc2f5..e500ef4 100644
--- a/notary/internal/sumweb/client_test.go
+++ b/notary/internal/sumweb/client_test.go
@@ -154,6 +154,40 @@
}
}
+func TestConnGONOVERIFY(t *testing.T) {
+ tc := newTestClient(t)
+ tc.conn.Lookup("rsc.io/sampler", "v1.3.0") // initialize before we turn off network
+ tc.getOK = false
+ tc.conn.SetGONOVERIFY("p,*/q")
+
+ ok := []string{
+ "abc",
+ "a/p",
+ "pq",
+ "q",
+ "n/o/p/q",
+ }
+ skip := []string{
+ "p",
+ "p/x",
+ "x/q",
+ "x/q/z",
+ }
+
+ for _, path := range ok {
+ _, err := tc.conn.Lookup(path, "v1.0.0")
+ if err == ErrGONOVERIFY {
+ t.Errorf("Lookup(%q): ErrGONOVERIFY, wanted failed actual lookup", path)
+ }
+ }
+ for _, path := range skip {
+ _, err := tc.conn.Lookup(path, "v1.0.0")
+ if err != ErrGONOVERIFY {
+ t.Errorf("Lookup(%q): %v, wanted ErrGONOVERIFY", path, err)
+ }
+ }
+}
+
// A testClient is a self-contained client-side testing environment.
type testClient struct {
t *testing.T // active test