gosumcheck: basic go.sum checker
Copied from golang.org/x/exp/sumdb/gosumcheck. For golang/go#31761. Change-Id: Ia2cf81372b8e2da9b879f3d737de014eb30ea34b Reviewed-on: https://go-review.googlesource.com/c/mod/+/176639 Run-TryBot: Russ Cox <rsc@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
This commit is contained in:
Родитель
a1091b641a
Коммит
d8296639b4
2
go.mod
2
go.mod
|
@ -2,4 +2,4 @@ module golang.org/x/mod
|
|||
|
||||
require golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529
|
||||
|
||||
go 1.13
|
||||
go 1.12
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Gosumcheck checks a go.sum file against a go.sum database server.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// gosumcheck [-h H] [-k key] [-u url] [-v] go.sum
|
||||
//
|
||||
// The -h flag changes the tile height (default 8).
|
||||
//
|
||||
// The -k flag changes the go.sum database server key.
|
||||
//
|
||||
// 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 (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/mod/sumdb"
|
||||
)
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: gosumcheck [-h H] [-k key] [-u url] [-v] go.sum...\n")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var (
|
||||
height = flag.Int("h", 8, "tile height")
|
||||
vkey = flag.String("k", "sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8", "key")
|
||||
url = flag.String("u", "", "url to server (overriding name)")
|
||||
vflag = flag.Bool("v", false, "enable verbose output")
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetPrefix("notecheck: ")
|
||||
log.SetFlags(0)
|
||||
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if flag.NArg() < 1 {
|
||||
usage()
|
||||
}
|
||||
|
||||
client := sumdb.NewClient(new(clientOps))
|
||||
|
||||
// Look in environment explicitly, so that if 'go env' is old and
|
||||
// doesn't know about GONOSUMDB, we at least get anything
|
||||
// set in the environment.
|
||||
env := os.Getenv("GONOSUMDB")
|
||||
if env == "" {
|
||||
out, err := exec.Command("go", "env", "GONOSUMDB").CombinedOutput()
|
||||
if err != nil {
|
||||
log.Fatalf("go env GONOSUMDB: %v\n%s", err, out)
|
||||
}
|
||||
env = strings.TrimSpace(string(out))
|
||||
}
|
||||
client.SetGONOSUMDB(env)
|
||||
|
||||
for _, arg := range flag.Args() {
|
||||
data, err := ioutil.ReadFile(arg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
checkGoSum(client, arg, data)
|
||||
}
|
||||
}
|
||||
|
||||
func checkGoSum(client *sumdb.Client, name string, data []byte) {
|
||||
lines := strings.Split(string(data), "\n")
|
||||
if lines[len(lines)-1] != "" {
|
||||
log.Printf("error: final line missing newline")
|
||||
return
|
||||
}
|
||||
lines = lines[:len(lines)-1]
|
||||
|
||||
errs := make([]string, len(lines))
|
||||
var wg sync.WaitGroup
|
||||
for i, line := range lines {
|
||||
wg.Add(1)
|
||||
go func(i int, line string) {
|
||||
defer wg.Done()
|
||||
f := strings.Fields(line)
|
||||
if len(f) != 3 {
|
||||
errs[i] = "invalid number of fields"
|
||||
return
|
||||
}
|
||||
|
||||
dbLines, err := client.Lookup(f[0], f[1])
|
||||
if err != nil {
|
||||
if err == sumdb.ErrGONOSUMDB {
|
||||
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]
|
||||
for _, dbLine := range dbLines {
|
||||
if dbLine == line {
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(dbLine, hashAlgPrefix) {
|
||||
errs[i] = fmt.Sprintf("%s@%s hash mismatch: have %s, want %s", f[0], f[1], line, dbLine)
|
||||
return
|
||||
}
|
||||
}
|
||||
errs[i] = fmt.Sprintf("%s@%s hash algorithm mismatch: have %s, want one of:\n\t%s", f[0], f[1], line, strings.Join(dbLines, "\n\t"))
|
||||
}(i, line)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for i, err := range errs {
|
||||
if err != "" {
|
||||
fmt.Printf("%s:%d: %s\n", name, i+1, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type clientOps struct{}
|
||||
|
||||
func (*clientOps) ReadConfig(file string) ([]byte, error) {
|
||||
if file == "key" {
|
||||
return []byte(*vkey), nil
|
||||
}
|
||||
if strings.HasSuffix(file, "/latest") {
|
||||
// Looking for cached latest tree head.
|
||||
// Empty result means empty tree.
|
||||
return []byte{}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown config %s", file)
|
||||
}
|
||||
|
||||
func (*clientOps) WriteConfig(file string, old, new []byte) error {
|
||||
// Ignore writes.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*clientOps) ReadCache(file string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("no cache")
|
||||
}
|
||||
|
||||
func (*clientOps) WriteCache(file string, data []byte) {
|
||||
// Ignore writes.
|
||||
}
|
||||
|
||||
func (*clientOps) Log(msg string) {
|
||||
log.Print(msg)
|
||||
}
|
||||
|
||||
func (*clientOps) SecurityError(msg string) {
|
||||
log.Fatal(msg)
|
||||
}
|
||||
|
||||
func init() {
|
||||
http.DefaultClient.Timeout = 1 * time.Minute
|
||||
}
|
||||
|
||||
func (*clientOps) ReadRemote(path string) ([]byte, error) {
|
||||
name := *vkey
|
||||
if i := strings.Index(name, "+"); i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
start := time.Now()
|
||||
target := "https://" + name + path
|
||||
if *url != "" {
|
||||
target = *url + path
|
||||
}
|
||||
resp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, fmt.Errorf("GET %v: %v", target, resp.Status)
|
||||
}
|
||||
data, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if *vflag {
|
||||
fmt.Fprintf(os.Stderr, "%.3fs %s\n", time.Since(start).Seconds(), target)
|
||||
}
|
||||
return data, nil
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
go build -o gosumcheck.exe
|
||||
export GONOSUMDB=*/text # rsc.io/text but not golang.org/x/text
|
||||
./gosumcheck.exe "$@" -v test.sum
|
||||
rm -f ./gosumcheck.exe
|
||||
echo PASS
|
|
@ -0,0 +1,6 @@
|
|||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
|
||||
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=
|
Загрузка…
Ссылка в новой задаче