mirror of https://github.com/docker/docs.git
validation tests
Signed-off-by: David Lawrence <david.lawrence@docker.com> (github: endophage)
This commit is contained in:
parent
0ece438313
commit
6616bed616
|
@ -8,3 +8,4 @@ cross
|
|||
*.swp
|
||||
.idea
|
||||
*.iml
|
||||
coverage.out
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "code.google.com/p/gosqlite/sqlite3",
|
||||
"Comment": "null-16",
|
||||
"Rev": "74691fb6f83716190870cde1b658538dd4b18eb0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/BurntSushi/toml",
|
||||
"Rev": "bd2bdf7f18f849530ef7a1c29a4290217cab32a1"
|
||||
|
@ -95,7 +100,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/endophage/gotuf",
|
||||
"Rev": "78f263e5a0b2f74b8336ea9faef0e40eef1f439d"
|
||||
"Rev": "4c04df9067a595ead06309f38021ea445acc1d1c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-sql-driver/mysql",
|
||||
|
@ -106,6 +111,10 @@
|
|||
"ImportPath": "github.com/golang/protobuf/proto",
|
||||
"Rev": "655cdfa588ea190e901bc5590e65d5621688847c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/google/gofuzz",
|
||||
"Rev": "bbcb9da2d746f8bdbd6a936686a0a6067ada0ec5"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "14f550f51af52180c2eefed15e5fd18d63c0a64a"
|
||||
|
|
498
Godeps/_workspace/src/code.google.com/p/gosqlite/sqlite3/driver.go
generated
vendored
Normal file
498
Godeps/_workspace/src/code.google.com/p/gosqlite/sqlite3/driver.go
generated
vendored
Normal file
|
@ -0,0 +1,498 @@
|
|||
// Copyright 2010 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.
|
||||
|
||||
// Package sqlite3 provides access to the SQLite library, version 3.
|
||||
//
|
||||
// The package has no exported API.
|
||||
// It registers a driver for the standard Go database/sql package.
|
||||
//
|
||||
// import _ "code.google.com/p/gosqlite/sqlite3"
|
||||
//
|
||||
// (For an alternate, earlier API, see the code.google.com/p/gosqlite/sqlite package.)
|
||||
package sqlite
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -lsqlite3
|
||||
|
||||
#include <sqlite3.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// These wrappers are necessary because SQLITE_TRANSIENT
|
||||
// is a pointer constant, and cgo doesn't translate them correctly.
|
||||
// The definition in sqlite3.h is:
|
||||
//
|
||||
// typedef void (*sqlite3_destructor_type)(void*);
|
||||
// #define SQLITE_STATIC ((sqlite3_destructor_type)0)
|
||||
// #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1)
|
||||
|
||||
static int my_bind_text(sqlite3_stmt *stmt, int n, char *p, int np) {
|
||||
return sqlite3_bind_text(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
}
|
||||
static int my_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
|
||||
return sqlite3_bind_blob(stmt, n, p, np, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sql.Register("sqlite3", impl{})
|
||||
}
|
||||
|
||||
type errno int
|
||||
|
||||
func (e errno) Error() string {
|
||||
s := errText[e]
|
||||
if s == "" {
|
||||
return fmt.Sprintf("errno %d", int(e))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var (
|
||||
errError error = errno(1) // /* SQL error or missing database */
|
||||
errInternal error = errno(2) // /* Internal logic error in SQLite */
|
||||
errPerm error = errno(3) // /* Access permission denied */
|
||||
errAbort error = errno(4) // /* Callback routine requested an abort */
|
||||
errBusy error = errno(5) // /* The database file is locked */
|
||||
errLocked error = errno(6) // /* A table in the database is locked */
|
||||
errNoMem error = errno(7) // /* A malloc() failed */
|
||||
errReadOnly error = errno(8) // /* Attempt to write a readonly database */
|
||||
errInterrupt error = errno(9) // /* Operation terminated by sqlite3_interrupt()*/
|
||||
errIOErr error = errno(10) // /* Some kind of disk I/O error occurred */
|
||||
errCorrupt error = errno(11) // /* The database disk image is malformed */
|
||||
errFull error = errno(13) // /* Insertion failed because database is full */
|
||||
errCantOpen error = errno(14) // /* Unable to open the database file */
|
||||
errEmpty error = errno(16) // /* Database is empty */
|
||||
errSchema error = errno(17) // /* The database schema changed */
|
||||
errTooBig error = errno(18) // /* String or BLOB exceeds size limit */
|
||||
errConstraint error = errno(19) // /* Abort due to constraint violation */
|
||||
errMismatch error = errno(20) // /* Data type mismatch */
|
||||
errMisuse error = errno(21) // /* Library used incorrectly */
|
||||
errNolfs error = errno(22) // /* Uses OS features not supported on host */
|
||||
errAuth error = errno(23) // /* Authorization denied */
|
||||
errFormat error = errno(24) // /* Auxiliary database format error */
|
||||
errRange error = errno(25) // /* 2nd parameter to sqlite3_bind out of range */
|
||||
errNotDB error = errno(26) // /* File opened that is not a database file */
|
||||
stepRow = errno(100) // /* sqlite3_step() has another row ready */
|
||||
stepDone = errno(101) // /* sqlite3_step() has finished executing */
|
||||
)
|
||||
|
||||
var errText = map[errno]string{
|
||||
1: "SQL error or missing database",
|
||||
2: "Internal logic error in SQLite",
|
||||
3: "Access permission denied",
|
||||
4: "Callback routine requested an abort",
|
||||
5: "The database file is locked",
|
||||
6: "A table in the database is locked",
|
||||
7: "A malloc() failed",
|
||||
8: "Attempt to write a readonly database",
|
||||
9: "Operation terminated by sqlite3_interrupt()*/",
|
||||
10: "Some kind of disk I/O error occurred",
|
||||
11: "The database disk image is malformed",
|
||||
12: "NOT USED. Table or record not found",
|
||||
13: "Insertion failed because database is full",
|
||||
14: "Unable to open the database file",
|
||||
15: "NOT USED. Database lock protocol error",
|
||||
16: "Database is empty",
|
||||
17: "The database schema changed",
|
||||
18: "String or BLOB exceeds size limit",
|
||||
19: "Abort due to constraint violation",
|
||||
20: "Data type mismatch",
|
||||
21: "Library used incorrectly",
|
||||
22: "Uses OS features not supported on host",
|
||||
23: "Authorization denied",
|
||||
24: "Auxiliary database format error",
|
||||
25: "2nd parameter to sqlite3_bind out of range",
|
||||
26: "File opened that is not a database file",
|
||||
100: "sqlite3_step() has another row ready",
|
||||
101: "sqlite3_step() has finished executing",
|
||||
}
|
||||
|
||||
type impl struct{}
|
||||
|
||||
func (impl) Open(name string) (driver.Conn, error) {
|
||||
if C.sqlite3_threadsafe() == 0 {
|
||||
return nil, errors.New("sqlite library was not compiled for thread-safe operation")
|
||||
}
|
||||
|
||||
var db *C.sqlite3
|
||||
cname := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(cname))
|
||||
rv := C.sqlite3_open_v2(cname, &db,
|
||||
C.SQLITE_OPEN_FULLMUTEX|
|
||||
C.SQLITE_OPEN_READWRITE|
|
||||
C.SQLITE_OPEN_CREATE,
|
||||
nil)
|
||||
if rv != 0 {
|
||||
return nil, errno(rv)
|
||||
}
|
||||
if db == nil {
|
||||
return nil, errors.New("sqlite succeeded without returning a database")
|
||||
}
|
||||
return &conn{db: db}, nil
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
db *C.sqlite3
|
||||
closed bool
|
||||
tx bool
|
||||
}
|
||||
|
||||
func (c *conn) error(rv C.int) error {
|
||||
if rv == 0 {
|
||||
return nil
|
||||
}
|
||||
if rv == 21 || c.closed {
|
||||
return errno(rv)
|
||||
}
|
||||
return errors.New(errno(rv).Error() + ": " + C.GoString(C.sqlite3_errmsg(c.db)))
|
||||
}
|
||||
|
||||
func (c *conn) Prepare(cmd string) (driver.Stmt, error) {
|
||||
if c.closed {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Prepare after Close")
|
||||
}
|
||||
cmdstr := C.CString(cmd)
|
||||
defer C.free(unsafe.Pointer(cmdstr))
|
||||
var s *C.sqlite3_stmt
|
||||
var tail *C.char
|
||||
rv := C.sqlite3_prepare_v2(c.db, cmdstr, C.int(len(cmd)+1), &s, &tail)
|
||||
if rv != 0 {
|
||||
return nil, c.error(rv)
|
||||
}
|
||||
return &stmt{c: c, stmt: s, sql: cmd, t0: time.Now()}, nil
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
if c.closed {
|
||||
panic("database/sql/driver: misuse of sqlite driver: multiple Close")
|
||||
}
|
||||
c.closed = true
|
||||
rv := C.sqlite3_close(c.db)
|
||||
c.db = nil
|
||||
return c.error(rv)
|
||||
}
|
||||
|
||||
func (c *conn) exec(cmd string) error {
|
||||
cstring := C.CString(cmd)
|
||||
defer C.free(unsafe.Pointer(cstring))
|
||||
rv := C.sqlite3_exec(c.db, cstring, nil, nil, nil)
|
||||
return c.error(rv)
|
||||
}
|
||||
|
||||
func (c *conn) Begin() (driver.Tx, error) {
|
||||
if c.tx {
|
||||
panic("database/sql/driver: misuse of sqlite driver: multiple Tx")
|
||||
}
|
||||
if err := c.exec("BEGIN TRANSACTION"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.tx = true
|
||||
return &tx{c}, nil
|
||||
}
|
||||
|
||||
type tx struct {
|
||||
c *conn
|
||||
}
|
||||
|
||||
func (t *tx) Commit() error {
|
||||
if t.c == nil || !t.c.tx {
|
||||
panic("database/sql/driver: misuse of sqlite driver: extra Commit")
|
||||
}
|
||||
t.c.tx = false
|
||||
err := t.c.exec("COMMIT TRANSACTION")
|
||||
t.c = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *tx) Rollback() error {
|
||||
if t.c == nil || !t.c.tx {
|
||||
panic("database/sql/driver: misuse of sqlite driver: extra Rollback")
|
||||
}
|
||||
t.c.tx = false
|
||||
err := t.c.exec("ROLLBACK")
|
||||
t.c = nil
|
||||
return err
|
||||
}
|
||||
|
||||
type stmt struct {
|
||||
c *conn
|
||||
stmt *C.sqlite3_stmt
|
||||
err error
|
||||
t0 time.Time
|
||||
sql string
|
||||
args string
|
||||
closed bool
|
||||
rows bool
|
||||
colnames []string
|
||||
coltypes []string
|
||||
}
|
||||
|
||||
func (s *stmt) Close() error {
|
||||
if s.rows {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Close with active Rows")
|
||||
}
|
||||
if s.closed {
|
||||
panic("database/sql/driver: misuse of sqlite driver: double Close of Stmt")
|
||||
}
|
||||
s.closed = true
|
||||
rv := C.sqlite3_finalize(s.stmt)
|
||||
if rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stmt) NumInput() int {
|
||||
if s.closed {
|
||||
panic("database/sql/driver: misuse of sqlite driver: NumInput after Close")
|
||||
}
|
||||
return int(C.sqlite3_bind_parameter_count(s.stmt))
|
||||
}
|
||||
|
||||
func (s *stmt) reset() error {
|
||||
return s.c.error(C.sqlite3_reset(s.stmt))
|
||||
}
|
||||
|
||||
func (s *stmt) start(args []driver.Value) error {
|
||||
if err := s.reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n := int(C.sqlite3_bind_parameter_count(s.stmt))
|
||||
if n != len(args) {
|
||||
return fmt.Errorf("incorrect argument count for command: have %d want %d", len(args), n)
|
||||
}
|
||||
|
||||
for i, v := range args {
|
||||
var str string
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
if rv := C.sqlite3_bind_null(s.stmt, C.int(i+1)); rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
continue
|
||||
|
||||
case float64:
|
||||
if rv := C.sqlite3_bind_double(s.stmt, C.int(i+1), C.double(v)); rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
continue
|
||||
|
||||
case int64:
|
||||
if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(v)); rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
continue
|
||||
|
||||
case []byte:
|
||||
var p *byte
|
||||
if len(v) > 0 {
|
||||
p = &v[0]
|
||||
}
|
||||
if rv := C.my_bind_blob(s.stmt, C.int(i+1), unsafe.Pointer(p), C.int(len(v))); rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
continue
|
||||
|
||||
case bool:
|
||||
var vi int64
|
||||
if v {
|
||||
vi = 1
|
||||
}
|
||||
if rv := C.sqlite3_bind_int64(s.stmt, C.int(i+1), C.sqlite3_int64(vi)); rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
continue
|
||||
|
||||
case time.Time:
|
||||
str = v.UTC().Format(timefmt[0])
|
||||
|
||||
case string:
|
||||
str = v
|
||||
|
||||
default:
|
||||
str = fmt.Sprint(v)
|
||||
}
|
||||
|
||||
cstr := C.CString(str)
|
||||
rv := C.my_bind_text(s.stmt, C.int(i+1), cstr, C.int(len(str)))
|
||||
C.free(unsafe.Pointer(cstr))
|
||||
if rv != 0 {
|
||||
return s.c.error(rv)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if s.closed {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Exec after Close")
|
||||
}
|
||||
if s.rows {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Exec with active Rows")
|
||||
}
|
||||
|
||||
err := s.start(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rv := C.sqlite3_step(s.stmt)
|
||||
if errno(rv) != stepDone {
|
||||
if rv == 0 {
|
||||
rv = 21 // errMisuse
|
||||
}
|
||||
return nil, s.c.error(rv)
|
||||
}
|
||||
|
||||
id := int64(C.sqlite3_last_insert_rowid(s.c.db))
|
||||
rows := int64(C.sqlite3_changes(s.c.db))
|
||||
return &result{id, rows}, nil
|
||||
}
|
||||
|
||||
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
if s.closed {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Query after Close")
|
||||
}
|
||||
if s.rows {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Query with active Rows")
|
||||
}
|
||||
|
||||
err := s.start(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.rows = true
|
||||
if s.colnames == nil {
|
||||
n := int64(C.sqlite3_column_count(s.stmt))
|
||||
s.colnames = make([]string, n)
|
||||
s.coltypes = make([]string, n)
|
||||
for i := range s.colnames {
|
||||
s.colnames[i] = C.GoString(C.sqlite3_column_name(s.stmt, C.int(i)))
|
||||
s.coltypes[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(s.stmt, C.int(i))))
|
||||
}
|
||||
}
|
||||
return &rows{s}, nil
|
||||
}
|
||||
|
||||
type rows struct {
|
||||
s *stmt
|
||||
}
|
||||
|
||||
func (r *rows) Columns() []string {
|
||||
if r.s == nil {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Columns of closed Rows")
|
||||
}
|
||||
return r.s.colnames
|
||||
}
|
||||
|
||||
const maxslice = 1<<31 - 1
|
||||
|
||||
var timefmt = []string{
|
||||
"2006-01-02 15:04:05.999999999",
|
||||
"2006-01-02T15:04:05.999999999",
|
||||
"2006-01-02 15:04:05",
|
||||
"2006-01-02T15:04:05",
|
||||
"2006-01-02 15:04",
|
||||
"2006-01-02T15:04",
|
||||
"2006-01-02",
|
||||
}
|
||||
|
||||
func (r *rows) Next(dst []driver.Value) error {
|
||||
if r.s == nil {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Next of closed Rows")
|
||||
}
|
||||
|
||||
rv := C.sqlite3_step(r.s.stmt)
|
||||
if errno(rv) != stepRow {
|
||||
if errno(rv) == stepDone {
|
||||
return io.EOF
|
||||
}
|
||||
if rv == 0 {
|
||||
rv = 21
|
||||
}
|
||||
return r.s.c.error(rv)
|
||||
}
|
||||
|
||||
for i := range dst {
|
||||
switch typ := C.sqlite3_column_type(r.s.stmt, C.int(i)); typ {
|
||||
default:
|
||||
return fmt.Errorf("unexpected sqlite3 column type %d", typ)
|
||||
case C.SQLITE_INTEGER:
|
||||
val := int64(C.sqlite3_column_int64(r.s.stmt, C.int(i)))
|
||||
switch r.s.coltypes[i] {
|
||||
case "timestamp", "datetime":
|
||||
dst[i] = time.Unix(val, 0).UTC()
|
||||
case "boolean":
|
||||
dst[i] = val > 0
|
||||
default:
|
||||
dst[i] = val
|
||||
}
|
||||
|
||||
case C.SQLITE_FLOAT:
|
||||
dst[i] = float64(C.sqlite3_column_double(r.s.stmt, C.int(i)))
|
||||
|
||||
case C.SQLITE_BLOB, C.SQLITE_TEXT:
|
||||
n := int(C.sqlite3_column_bytes(r.s.stmt, C.int(i)))
|
||||
var b []byte
|
||||
if n > 0 {
|
||||
p := C.sqlite3_column_blob(r.s.stmt, C.int(i))
|
||||
b = (*[maxslice]byte)(unsafe.Pointer(p))[:n]
|
||||
}
|
||||
dst[i] = b
|
||||
switch r.s.coltypes[i] {
|
||||
case "timestamp", "datetime":
|
||||
dst[i] = time.Time{}
|
||||
s := string(b)
|
||||
for _, f := range timefmt {
|
||||
if t, err := time.Parse(f, s); err == nil {
|
||||
dst[i] = t
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case C.SQLITE_NULL:
|
||||
dst[i] = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rows) Close() error {
|
||||
if r.s == nil {
|
||||
panic("database/sql/driver: misuse of sqlite driver: Close of closed Rows")
|
||||
}
|
||||
r.s.rows = false
|
||||
r.s = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type result struct {
|
||||
id int64
|
||||
rows int64
|
||||
}
|
||||
|
||||
func (r *result) LastInsertId() (int64, error) {
|
||||
return r.id, nil
|
||||
}
|
||||
|
||||
func (r *result) RowsAffected() (int64, error) {
|
||||
return r.rows, nil
|
||||
}
|
99
Godeps/_workspace/src/github.com/endophage/gotuf/testutils/repo.go
generated
vendored
Normal file
99
Godeps/_workspace/src/github.com/endophage/gotuf/testutils/repo.go
generated
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
package testutils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/endophage/gotuf/data"
|
||||
"github.com/endophage/gotuf/utils"
|
||||
fuzz "github.com/google/gofuzz"
|
||||
|
||||
tuf "github.com/endophage/gotuf"
|
||||
"github.com/endophage/gotuf/keys"
|
||||
"github.com/endophage/gotuf/signed"
|
||||
)
|
||||
|
||||
// EmptyRepo creates an in memory key database, crypto service
|
||||
// and initializes a repo with no targets or delegations.
|
||||
func EmptyRepo() (*keys.KeyDB, *tuf.TufRepo, signed.CryptoService) {
|
||||
c := signed.NewEd25519()
|
||||
kdb := keys.NewDB()
|
||||
r := tuf.NewTufRepo(kdb, c)
|
||||
|
||||
for _, role := range []string{"root", "targets", "snapshot", "timestamp"} {
|
||||
key, _ := c.Create(role, data.ED25519Key)
|
||||
role, _ := data.NewRole(role, 1, []string{key.ID()}, nil, nil)
|
||||
kdb.AddKey(key)
|
||||
kdb.AddRole(role)
|
||||
}
|
||||
|
||||
r.InitRepo(false)
|
||||
return kdb, r, c
|
||||
}
|
||||
|
||||
func AddTarget(role string, r *tuf.TufRepo) (name string, meta data.FileMeta, content []byte, err error) {
|
||||
randness := fuzz.Continue{}
|
||||
content = RandomByteSlice(1024)
|
||||
name = randness.RandString()
|
||||
t := data.FileMeta{
|
||||
Length: int64(len(content)),
|
||||
Hashes: data.Hashes{
|
||||
"sha256": utils.DoHash("sha256", content),
|
||||
"sha512": utils.DoHash("sha512", content),
|
||||
},
|
||||
}
|
||||
files := data.Files{name: t}
|
||||
_, err = r.AddTargets(role, files)
|
||||
return
|
||||
}
|
||||
|
||||
func RandomByteSlice(maxSize int) []byte {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
contentSize := r.Intn(maxSize)
|
||||
content := make([]byte, contentSize)
|
||||
for i := range content {
|
||||
content[i] = byte(r.Int63() & 0xff)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
func Sign(repo *tuf.TufRepo) (root, targets, snapshot, timestamp *data.Signed, err error) {
|
||||
root, err = repo.SignRoot(data.DefaultExpires("root"), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
targets, err = repo.SignTargets("targets", data.DefaultExpires("targets"), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
snapshot, err = repo.SignSnapshot(data.DefaultExpires("snapshot"), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
timestamp, err = repo.SignTimestamp(data.DefaultExpires("timestamp"), nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Serialize(sRoot, sTargets, sSnapshot, sTimestamp *data.Signed) (root, targets, snapshot, timestamp []byte, err error) {
|
||||
root, err = json.Marshal(sRoot)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
targets, err = json.Marshal(sTargets)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
snapshot, err = json.Marshal(sSnapshot)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
timestamp, err = json.Marshal(sTimestamp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.4
|
||||
- 1.3
|
||||
- 1.2
|
||||
- tip
|
||||
|
||||
install:
|
||||
- if ! go get code.google.com/p/go.tools/cmd/cover; then go get golang.org/x/tools/cmd/cover; fi
|
||||
|
||||
script:
|
||||
- go test -cover
|
|
@ -0,0 +1,67 @@
|
|||
# How to contribute #
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
a just a few small guidelines you need to follow.
|
||||
|
||||
|
||||
## Contributor License Agreement ##
|
||||
|
||||
Contributions to any Google project must be accompanied by a Contributor
|
||||
License Agreement. This is not a copyright **assignment**, it simply gives
|
||||
Google permission to use and redistribute your contributions as part of the
|
||||
project.
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual
|
||||
CLA][].
|
||||
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA][].
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted
|
||||
one (even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
[individual CLA]: https://developers.google.com/open-source/cla/individual
|
||||
[corporate CLA]: https://developers.google.com/open-source/cla/corporate
|
||||
|
||||
|
||||
## Submitting a patch ##
|
||||
|
||||
1. It's generally best to start by opening a new issue describing the bug or
|
||||
feature you're intending to fix. Even if you think it's relatively minor,
|
||||
it's helpful to know what people are working on. Mention in the initial
|
||||
issue that you are planning to work on that bug or feature so that it can
|
||||
be assigned to you.
|
||||
|
||||
1. Follow the normal process of [forking][] the project, and setup a new
|
||||
branch to work in. It's important that each group of changes be done in
|
||||
separate branches in order to ensure that a pull request only includes the
|
||||
commits related to that bug or feature.
|
||||
|
||||
1. Go makes it very simple to ensure properly formatted code, so always run
|
||||
`go fmt` on your code before committing it. You should also run
|
||||
[golint][] over your code. As noted in the [golint readme][], it's not
|
||||
strictly necessary that your code be completely "lint-free", but this will
|
||||
help you find common style issues.
|
||||
|
||||
1. Any significant changes should almost always be accompanied by tests. The
|
||||
project already has good test coverage, so look at some of the existing
|
||||
tests if you're unsure how to go about it. [gocov][] and [gocov-html][]
|
||||
are invaluable tools for seeing which parts of your code aren't being
|
||||
exercised by your tests.
|
||||
|
||||
1. Do your best to have [well-formed commit messages][] for each change.
|
||||
This provides consistency throughout the project, and ensures that commit
|
||||
messages are able to be formatted properly by various git tools.
|
||||
|
||||
1. Finally, push the commits to your fork and submit a [pull request][].
|
||||
|
||||
[forking]: https://help.github.com/articles/fork-a-repo
|
||||
[golint]: https://github.com/golang/lint
|
||||
[golint readme]: https://github.com/golang/lint/blob/master/README
|
||||
[gocov]: https://github.com/axw/gocov
|
||||
[gocov-html]: https://github.com/matm/gocov-html
|
||||
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
[squash]: http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits
|
||||
[pull request]: https://help.github.com/articles/creating-a-pull-request
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,71 @@
|
|||
gofuzz
|
||||
======
|
||||
|
||||
gofuzz is a library for populating go objects with random values.
|
||||
|
||||
[](https://godoc.org/github.com/google/gofuzz)
|
||||
[](https://travis-ci.org/google/gofuzz)
|
||||
|
||||
This is useful for testing:
|
||||
|
||||
* Do your project's objects really serialize/unserialize correctly in all cases?
|
||||
* Is there an incorrectly formatted object that will cause your project to panic?
|
||||
|
||||
Import with ```import "github.com/google/gofuzz"```
|
||||
|
||||
You can use it on single variables:
|
||||
```
|
||||
f := fuzz.New()
|
||||
var myInt int
|
||||
f.Fuzz(&myInt) // myInt gets a random value.
|
||||
```
|
||||
|
||||
You can use it on maps:
|
||||
```
|
||||
f := fuzz.New().NilChance(0).NumElements(1, 1)
|
||||
var myMap map[ComplexKeyType]string
|
||||
f.Fuzz(&myMap) // myMap will have exactly one element.
|
||||
```
|
||||
|
||||
Customize the chance of getting a nil pointer:
|
||||
```
|
||||
f := fuzz.New().NilChance(.5)
|
||||
var fancyStruct struct {
|
||||
A, B, C, D *string
|
||||
}
|
||||
f.Fuzz(&fancyStruct) // About half the pointers should be set.
|
||||
```
|
||||
|
||||
You can even customize the randomization completely if needed:
|
||||
```
|
||||
type MyEnum string
|
||||
const (
|
||||
A MyEnum = "A"
|
||||
B MyEnum = "B"
|
||||
)
|
||||
type MyInfo struct {
|
||||
Type MyEnum
|
||||
AInfo *string
|
||||
BInfo *string
|
||||
}
|
||||
|
||||
f := fuzz.New().NilChance(0).Funcs(
|
||||
func(e *MyInfo, c fuzz.Continue) {
|
||||
switch c.Intn(2) {
|
||||
case 0:
|
||||
e.Type = A
|
||||
c.Fuzz(&e.AInfo)
|
||||
case 1:
|
||||
e.Type = B
|
||||
c.Fuzz(&e.BInfo)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
var myObject MyInfo
|
||||
f.Fuzz(&myObject) // Type will correspond to whether A or B info is set.
|
||||
```
|
||||
|
||||
See more examples in ```example_test.go```.
|
||||
|
||||
Happy testing!
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package fuzz is a library for populating go objects with random values.
|
||||
package fuzz
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fuzz_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/google/gofuzz"
|
||||
)
|
||||
|
||||
func ExampleSimple() {
|
||||
type MyType struct {
|
||||
A string
|
||||
B string
|
||||
C int
|
||||
D struct {
|
||||
E float64
|
||||
}
|
||||
}
|
||||
|
||||
f := fuzz.New()
|
||||
object := MyType{}
|
||||
|
||||
uniqueObjects := map[MyType]int{}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
f.Fuzz(&object)
|
||||
uniqueObjects[object]++
|
||||
}
|
||||
fmt.Printf("Got %v unique objects.\n", len(uniqueObjects))
|
||||
// Output:
|
||||
// Got 1000 unique objects.
|
||||
}
|
||||
|
||||
func ExampleCustom() {
|
||||
type MyType struct {
|
||||
A int
|
||||
B string
|
||||
}
|
||||
|
||||
counter := 0
|
||||
f := fuzz.New().Funcs(
|
||||
func(i *int, c fuzz.Continue) {
|
||||
*i = counter
|
||||
counter++
|
||||
},
|
||||
)
|
||||
object := MyType{}
|
||||
|
||||
uniqueObjects := map[MyType]int{}
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
f.Fuzz(&object)
|
||||
if object.A != i {
|
||||
fmt.Printf("Unexpected value: %#v\n", object)
|
||||
}
|
||||
uniqueObjects[object]++
|
||||
}
|
||||
fmt.Printf("Got %v unique objects.\n", len(uniqueObjects))
|
||||
// Output:
|
||||
// Got 100 unique objects.
|
||||
}
|
||||
|
||||
func ExampleComplex() {
|
||||
type OtherType struct {
|
||||
A string
|
||||
B string
|
||||
}
|
||||
type MyType struct {
|
||||
Pointer *OtherType
|
||||
Map map[string]OtherType
|
||||
PointerMap *map[string]OtherType
|
||||
Slice []OtherType
|
||||
SlicePointer []*OtherType
|
||||
PointerSlicePointer *[]*OtherType
|
||||
}
|
||||
|
||||
f := fuzz.New().RandSource(rand.NewSource(0)).NilChance(0).NumElements(1, 1).Funcs(
|
||||
func(o *OtherType, c fuzz.Continue) {
|
||||
o.A = "Foo"
|
||||
o.B = "Bar"
|
||||
},
|
||||
func(op **OtherType, c fuzz.Continue) {
|
||||
*op = &OtherType{"A", "B"}
|
||||
},
|
||||
func(m map[string]OtherType, c fuzz.Continue) {
|
||||
m["Works Because"] = OtherType{
|
||||
"Fuzzer",
|
||||
"Preallocated",
|
||||
}
|
||||
},
|
||||
)
|
||||
object := MyType{}
|
||||
f.Fuzz(&object)
|
||||
bytes, err := json.MarshalIndent(&object, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("error: %v\n", err)
|
||||
}
|
||||
fmt.Printf("%s\n", string(bytes))
|
||||
// Output:
|
||||
// {
|
||||
// "Pointer": {
|
||||
// "A": "A",
|
||||
// "B": "B"
|
||||
// },
|
||||
// "Map": {
|
||||
// "Works Because": {
|
||||
// "A": "Fuzzer",
|
||||
// "B": "Preallocated"
|
||||
// }
|
||||
// },
|
||||
// "PointerMap": {
|
||||
// "Works Because": {
|
||||
// "A": "Fuzzer",
|
||||
// "B": "Preallocated"
|
||||
// }
|
||||
// },
|
||||
// "Slice": [
|
||||
// {
|
||||
// "A": "Foo",
|
||||
// "B": "Bar"
|
||||
// }
|
||||
// ],
|
||||
// "SlicePointer": [
|
||||
// {
|
||||
// "A": "A",
|
||||
// "B": "B"
|
||||
// }
|
||||
// ],
|
||||
// "PointerSlicePointer": [
|
||||
// {
|
||||
// "A": "A",
|
||||
// "B": "B"
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
}
|
||||
|
||||
func ExampleMap() {
|
||||
f := fuzz.New().NilChance(0).NumElements(1, 1)
|
||||
var myMap map[struct{ A, B, C int }]string
|
||||
f.Fuzz(&myMap)
|
||||
fmt.Printf("myMap has %v element(s).\n", len(myMap))
|
||||
// Output:
|
||||
// myMap has 1 element(s).
|
||||
}
|
||||
|
||||
func ExampleSingle() {
|
||||
f := fuzz.New()
|
||||
var i int
|
||||
f.Fuzz(&i)
|
||||
|
||||
// Technically, we'd expect this to fail one out of 2 billion attempts...
|
||||
fmt.Printf("(i == 0) == %v", i == 0)
|
||||
// Output:
|
||||
// (i == 0) == false
|
||||
}
|
||||
|
||||
func ExampleEnum() {
|
||||
type MyEnum string
|
||||
const (
|
||||
A MyEnum = "A"
|
||||
B MyEnum = "B"
|
||||
)
|
||||
type MyInfo struct {
|
||||
Type MyEnum
|
||||
AInfo *string
|
||||
BInfo *string
|
||||
}
|
||||
|
||||
f := fuzz.New().NilChance(0).Funcs(
|
||||
func(e *MyInfo, c fuzz.Continue) {
|
||||
// Note c's embedded Rand allows for direct use.
|
||||
// We could also use c.RandBool() here.
|
||||
switch c.Intn(2) {
|
||||
case 0:
|
||||
e.Type = A
|
||||
c.Fuzz(&e.AInfo)
|
||||
case 1:
|
||||
e.Type = B
|
||||
c.Fuzz(&e.BInfo)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
var myObject MyInfo
|
||||
f.Fuzz(&myObject)
|
||||
switch myObject.Type {
|
||||
case A:
|
||||
if myObject.AInfo == nil {
|
||||
fmt.Println("AInfo should have been set!")
|
||||
}
|
||||
if myObject.BInfo != nil {
|
||||
fmt.Println("BInfo should NOT have been set!")
|
||||
}
|
||||
case B:
|
||||
if myObject.BInfo == nil {
|
||||
fmt.Println("BInfo should have been set!")
|
||||
}
|
||||
if myObject.AInfo != nil {
|
||||
fmt.Println("AInfo should NOT have been set!")
|
||||
}
|
||||
default:
|
||||
fmt.Println("Invalid enum value!")
|
||||
}
|
||||
}
|
||||
// Output:
|
||||
}
|
|
@ -0,0 +1,446 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// fuzzFuncMap is a map from a type to a fuzzFunc that handles that type.
|
||||
type fuzzFuncMap map[reflect.Type]reflect.Value
|
||||
|
||||
// Fuzzer knows how to fill any object with random fields.
|
||||
type Fuzzer struct {
|
||||
fuzzFuncs fuzzFuncMap
|
||||
defaultFuzzFuncs fuzzFuncMap
|
||||
r *rand.Rand
|
||||
nilChance float64
|
||||
minElements int
|
||||
maxElements int
|
||||
}
|
||||
|
||||
// New returns a new Fuzzer. Customize your Fuzzer further by calling Funcs,
|
||||
// RandSource, NilChance, or NumElements in any order.
|
||||
func New() *Fuzzer {
|
||||
f := &Fuzzer{
|
||||
defaultFuzzFuncs: fuzzFuncMap{
|
||||
reflect.TypeOf(&time.Time{}): reflect.ValueOf(fuzzTime),
|
||||
},
|
||||
|
||||
fuzzFuncs: fuzzFuncMap{},
|
||||
r: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
nilChance: .2,
|
||||
minElements: 1,
|
||||
maxElements: 10,
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// Funcs adds each entry in fuzzFuncs as a custom fuzzing function.
|
||||
//
|
||||
// Each entry in fuzzFuncs must be a function taking two parameters.
|
||||
// The first parameter must be a pointer or map. It is the variable that
|
||||
// function will fill with random data. The second parameter must be a
|
||||
// fuzz.Continue, which will provide a source of randomness and a way
|
||||
// to automatically continue fuzzing smaller pieces of the first parameter.
|
||||
//
|
||||
// These functions are called sensibly, e.g., if you wanted custom string
|
||||
// fuzzing, the function `func(s *string, c fuzz.Continue)` would get
|
||||
// called and passed the address of strings. Maps and pointers will always
|
||||
// be made/new'd for you, ignoring the NilChange option. For slices, it
|
||||
// doesn't make much sense to pre-create them--Fuzzer doesn't know how
|
||||
// long you want your slice--so take a pointer to a slice, and make it
|
||||
// yourself. (If you don't want your map/pointer type pre-made, take a
|
||||
// pointer to it, and make it yourself.) See the examples for a range of
|
||||
// custom functions.
|
||||
func (f *Fuzzer) Funcs(fuzzFuncs ...interface{}) *Fuzzer {
|
||||
for i := range fuzzFuncs {
|
||||
v := reflect.ValueOf(fuzzFuncs[i])
|
||||
if v.Kind() != reflect.Func {
|
||||
panic("Need only funcs!")
|
||||
}
|
||||
t := v.Type()
|
||||
if t.NumIn() != 2 || t.NumOut() != 0 {
|
||||
panic("Need 2 in and 0 out params!")
|
||||
}
|
||||
argT := t.In(0)
|
||||
switch argT.Kind() {
|
||||
case reflect.Ptr, reflect.Map:
|
||||
default:
|
||||
panic("fuzzFunc must take pointer or map type")
|
||||
}
|
||||
if t.In(1) != reflect.TypeOf(Continue{}) {
|
||||
panic("fuzzFunc's second parameter must be type fuzz.Continue")
|
||||
}
|
||||
f.fuzzFuncs[argT] = v
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// RandSource causes f to get values from the given source of randomness.
|
||||
// Use if you want deterministic fuzzing.
|
||||
func (f *Fuzzer) RandSource(s rand.Source) *Fuzzer {
|
||||
f.r = rand.New(s)
|
||||
return f
|
||||
}
|
||||
|
||||
// NilChance sets the probability of creating a nil pointer, map, or slice to
|
||||
// 'p'. 'p' should be between 0 (no nils) and 1 (all nils), inclusive.
|
||||
func (f *Fuzzer) NilChance(p float64) *Fuzzer {
|
||||
if p < 0 || p > 1 {
|
||||
panic("p should be between 0 and 1, inclusive.")
|
||||
}
|
||||
f.nilChance = p
|
||||
return f
|
||||
}
|
||||
|
||||
// NumElements sets the minimum and maximum number of elements that will be
|
||||
// added to a non-nil map or slice.
|
||||
func (f *Fuzzer) NumElements(atLeast, atMost int) *Fuzzer {
|
||||
if atLeast > atMost {
|
||||
panic("atLeast must be <= atMost")
|
||||
}
|
||||
if atLeast < 0 {
|
||||
panic("atLeast must be >= 0")
|
||||
}
|
||||
f.minElements = atLeast
|
||||
f.maxElements = atMost
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *Fuzzer) genElementCount() int {
|
||||
if f.minElements == f.maxElements {
|
||||
return f.minElements
|
||||
}
|
||||
return f.minElements + f.r.Intn(f.maxElements-f.minElements)
|
||||
}
|
||||
|
||||
func (f *Fuzzer) genShouldFill() bool {
|
||||
return f.r.Float64() > f.nilChance
|
||||
}
|
||||
|
||||
// Fuzz recursively fills all of obj's fields with something random. First
|
||||
// this tries to find a custom fuzz function (see Funcs). If there is no
|
||||
// custom function this tests whether the object implements fuzz.Interface and,
|
||||
// if so, calls Fuzz on it to fuzz itself. If that fails, this will see if
|
||||
// there is a default fuzz function provided by this package. If all of that
|
||||
// fails, this will generate random values for all primitive fields and then
|
||||
// recurse for all non-primitives.
|
||||
//
|
||||
// Not safe for cyclic or tree-like structs!
|
||||
//
|
||||
// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ )
|
||||
// Intended for tests, so will panic on bad input or unimplemented fields.
|
||||
func (f *Fuzzer) Fuzz(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
f.doFuzz(v, 0)
|
||||
}
|
||||
|
||||
// FuzzNoCustom is just like Fuzz, except that any custom fuzz function for
|
||||
// obj's type will not be called and obj will not be tested for fuzz.Interface
|
||||
// conformance. This applies only to obj and not other instances of obj's
|
||||
// type.
|
||||
// Not safe for cyclic or tree-like structs!
|
||||
// obj must be a pointer. Only exported (public) fields can be set (thanks, golang :/ )
|
||||
// Intended for tests, so will panic on bad input or unimplemented fields.
|
||||
func (f *Fuzzer) FuzzNoCustom(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
f.doFuzz(v, flagNoCustomFuzz)
|
||||
}
|
||||
|
||||
const (
|
||||
// Do not try to find a custom fuzz function. Does not apply recursively.
|
||||
flagNoCustomFuzz uint64 = 1 << iota
|
||||
)
|
||||
|
||||
func (f *Fuzzer) doFuzz(v reflect.Value, flags uint64) {
|
||||
if !v.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if flags&flagNoCustomFuzz == 0 {
|
||||
// Check for both pointer and non-pointer custom functions.
|
||||
if v.CanAddr() && f.tryCustom(v.Addr()) {
|
||||
return
|
||||
}
|
||||
if f.tryCustom(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if fn, ok := fillFuncMap[v.Kind()]; ok {
|
||||
fn(v, f.r)
|
||||
return
|
||||
}
|
||||
switch v.Kind() {
|
||||
case reflect.Map:
|
||||
if f.genShouldFill() {
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
n := f.genElementCount()
|
||||
for i := 0; i < n; i++ {
|
||||
key := reflect.New(v.Type().Key()).Elem()
|
||||
f.doFuzz(key, 0)
|
||||
val := reflect.New(v.Type().Elem()).Elem()
|
||||
f.doFuzz(val, 0)
|
||||
v.SetMapIndex(key, val)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Ptr:
|
||||
if f.genShouldFill() {
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
f.doFuzz(v.Elem(), 0)
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Slice:
|
||||
if f.genShouldFill() {
|
||||
n := f.genElementCount()
|
||||
v.Set(reflect.MakeSlice(v.Type(), n, n))
|
||||
for i := 0; i < n; i++ {
|
||||
f.doFuzz(v.Index(i), 0)
|
||||
}
|
||||
return
|
||||
}
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
f.doFuzz(v.Field(i), 0)
|
||||
}
|
||||
case reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Chan:
|
||||
fallthrough
|
||||
case reflect.Func:
|
||||
fallthrough
|
||||
case reflect.Interface:
|
||||
fallthrough
|
||||
default:
|
||||
panic(fmt.Sprintf("Can't handle %#v", v.Interface()))
|
||||
}
|
||||
}
|
||||
|
||||
// tryCustom searches for custom handlers, and returns true iff it finds a match
|
||||
// and successfully randomizes v.
|
||||
func (f *Fuzzer) tryCustom(v reflect.Value) bool {
|
||||
// First: see if we have a fuzz function for it.
|
||||
doCustom, ok := f.fuzzFuncs[v.Type()]
|
||||
if !ok {
|
||||
// Second: see if it can fuzz itself.
|
||||
if v.CanInterface() {
|
||||
intf := v.Interface()
|
||||
if fuzzable, ok := intf.(Interface); ok {
|
||||
fuzzable.Fuzz(Continue{f: f, Rand: f.r})
|
||||
return true
|
||||
}
|
||||
}
|
||||
// Finally: see if there is a default fuzz function.
|
||||
doCustom, ok = f.defaultFuzzFuncs[v.Type()]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
switch v.Kind() {
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return false
|
||||
}
|
||||
v.Set(reflect.New(v.Type().Elem()))
|
||||
}
|
||||
case reflect.Map:
|
||||
if v.IsNil() {
|
||||
if !v.CanSet() {
|
||||
return false
|
||||
}
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
doCustom.Call([]reflect.Value{v, reflect.ValueOf(Continue{
|
||||
f: f,
|
||||
Rand: f.r,
|
||||
})})
|
||||
return true
|
||||
}
|
||||
|
||||
// Interface represents an object that knows how to fuzz itself. Any time we
|
||||
// find a type that implements this interface we will delegate the act of
|
||||
// fuzzing itself.
|
||||
type Interface interface {
|
||||
Fuzz(c Continue)
|
||||
}
|
||||
|
||||
// Continue can be passed to custom fuzzing functions to allow them to use
|
||||
// the correct source of randomness and to continue fuzzing their members.
|
||||
type Continue struct {
|
||||
f *Fuzzer
|
||||
|
||||
// For convenience, Continue implements rand.Rand via embedding.
|
||||
// Use this for generating any randomness if you want your fuzzing
|
||||
// to be repeatable for a given seed.
|
||||
*rand.Rand
|
||||
}
|
||||
|
||||
// Fuzz continues fuzzing obj. obj must be a pointer.
|
||||
func (c Continue) Fuzz(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
c.f.doFuzz(v, 0)
|
||||
}
|
||||
|
||||
// FuzzNoCustom continues fuzzing obj, except that any custom fuzz function for
|
||||
// obj's type will not be called and obj will not be tested for fuzz.Interface
|
||||
// conformance. This applies only to obj and not other instances of obj's
|
||||
// type.
|
||||
func (c Continue) FuzzNoCustom(obj interface{}) {
|
||||
v := reflect.ValueOf(obj)
|
||||
if v.Kind() != reflect.Ptr {
|
||||
panic("needed ptr!")
|
||||
}
|
||||
v = v.Elem()
|
||||
c.f.doFuzz(v, flagNoCustomFuzz)
|
||||
}
|
||||
|
||||
// RandString makes a random string up to 20 characters long. The returned string
|
||||
// may include a variety of (valid) UTF-8 encodings.
|
||||
func (c Continue) RandString() string {
|
||||
return randString(c.Rand)
|
||||
}
|
||||
|
||||
// RandUint64 makes random 64 bit numbers.
|
||||
// Weirdly, rand doesn't have a function that gives you 64 random bits.
|
||||
func (c Continue) RandUint64() uint64 {
|
||||
return randUint64(c.Rand)
|
||||
}
|
||||
|
||||
// RandBool returns true or false randomly.
|
||||
func (c Continue) RandBool() bool {
|
||||
return randBool(c.Rand)
|
||||
}
|
||||
|
||||
func fuzzInt(v reflect.Value, r *rand.Rand) {
|
||||
v.SetInt(int64(randUint64(r)))
|
||||
}
|
||||
|
||||
func fuzzUint(v reflect.Value, r *rand.Rand) {
|
||||
v.SetUint(randUint64(r))
|
||||
}
|
||||
|
||||
func fuzzTime(t *time.Time, c Continue) {
|
||||
var sec, nsec int64
|
||||
// Allow for about 1000 years of random time values, which keeps things
|
||||
// like JSON parsing reasonably happy.
|
||||
sec = c.Rand.Int63n(1000 * 365 * 24 * 60 * 60)
|
||||
c.Fuzz(&nsec)
|
||||
*t = time.Unix(sec, nsec)
|
||||
}
|
||||
|
||||
var fillFuncMap = map[reflect.Kind]func(reflect.Value, *rand.Rand){
|
||||
reflect.Bool: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetBool(randBool(r))
|
||||
},
|
||||
reflect.Int: fuzzInt,
|
||||
reflect.Int8: fuzzInt,
|
||||
reflect.Int16: fuzzInt,
|
||||
reflect.Int32: fuzzInt,
|
||||
reflect.Int64: fuzzInt,
|
||||
reflect.Uint: fuzzUint,
|
||||
reflect.Uint8: fuzzUint,
|
||||
reflect.Uint16: fuzzUint,
|
||||
reflect.Uint32: fuzzUint,
|
||||
reflect.Uint64: fuzzUint,
|
||||
reflect.Uintptr: fuzzUint,
|
||||
reflect.Float32: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetFloat(float64(r.Float32()))
|
||||
},
|
||||
reflect.Float64: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetFloat(r.Float64())
|
||||
},
|
||||
reflect.Complex64: func(v reflect.Value, r *rand.Rand) {
|
||||
panic("unimplemented")
|
||||
},
|
||||
reflect.Complex128: func(v reflect.Value, r *rand.Rand) {
|
||||
panic("unimplemented")
|
||||
},
|
||||
reflect.String: func(v reflect.Value, r *rand.Rand) {
|
||||
v.SetString(randString(r))
|
||||
},
|
||||
reflect.UnsafePointer: func(v reflect.Value, r *rand.Rand) {
|
||||
panic("unimplemented")
|
||||
},
|
||||
}
|
||||
|
||||
// randBool returns true or false randomly.
|
||||
func randBool(r *rand.Rand) bool {
|
||||
if r.Int()&1 == 1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type charRange struct {
|
||||
first, last rune
|
||||
}
|
||||
|
||||
// choose returns a random unicode character from the given range, using the
|
||||
// given randomness source.
|
||||
func (r *charRange) choose(rand *rand.Rand) rune {
|
||||
count := int64(r.last - r.first)
|
||||
return r.first + rune(rand.Int63n(count))
|
||||
}
|
||||
|
||||
var unicodeRanges = []charRange{
|
||||
{' ', '~'}, // ASCII characters
|
||||
{'\u00a0', '\u02af'}, // Multi-byte encoded characters
|
||||
{'\u4e00', '\u9fff'}, // Common CJK (even longer encodings)
|
||||
}
|
||||
|
||||
// randString makes a random string up to 20 characters long. The returned string
|
||||
// may include a variety of (valid) UTF-8 encodings.
|
||||
func randString(r *rand.Rand) string {
|
||||
n := r.Intn(20)
|
||||
runes := make([]rune, n)
|
||||
for i := range runes {
|
||||
runes[i] = unicodeRanges[r.Intn(len(unicodeRanges))].choose(r)
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
// randUint64 makes random 64 bit numbers.
|
||||
// Weirdly, rand doesn't have a function that gives you 64 random bits.
|
||||
func randUint64(r *rand.Rand) uint64 {
|
||||
return uint64(r.Uint32())<<32 | uint64(r.Uint32())
|
||||
}
|
|
@ -0,0 +1,384 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFuzz_basic(t *testing.T) {
|
||||
obj := &struct {
|
||||
I int
|
||||
I8 int8
|
||||
I16 int16
|
||||
I32 int32
|
||||
I64 int64
|
||||
U uint
|
||||
U8 uint8
|
||||
U16 uint16
|
||||
U32 uint32
|
||||
U64 uint64
|
||||
Uptr uintptr
|
||||
S string
|
||||
B bool
|
||||
T time.Time
|
||||
}{}
|
||||
|
||||
failed := map[string]int{}
|
||||
for i := 0; i < 10; i++ {
|
||||
New().Fuzz(obj)
|
||||
|
||||
if n, v := "i", obj.I; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "i8", obj.I8; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "i16", obj.I16; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "i32", obj.I32; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "i64", obj.I64; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "u", obj.U; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "u8", obj.U8; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "u16", obj.U16; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "u32", obj.U32; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "u64", obj.U64; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "uptr", obj.Uptr; v == 0 {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "s", obj.S; v == "" {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "b", obj.B; v == false {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "t", obj.T; v.IsZero() {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
}
|
||||
checkFailed(t, failed)
|
||||
}
|
||||
|
||||
func checkFailed(t *testing.T, failed map[string]int) {
|
||||
for k, v := range failed {
|
||||
if v > 8 {
|
||||
t.Errorf("%v seems to not be getting set, was zero value %v times", k, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuzz_structptr(t *testing.T) {
|
||||
obj := &struct {
|
||||
A *struct {
|
||||
S string
|
||||
}
|
||||
}{}
|
||||
|
||||
f := New().NilChance(.5)
|
||||
failed := map[string]int{}
|
||||
for i := 0; i < 10; i++ {
|
||||
f.Fuzz(obj)
|
||||
|
||||
if n, v := "a not nil", obj.A; v == nil {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "a nil", obj.A; v != nil {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
if n, v := "as", obj.A; v == nil || v.S == "" {
|
||||
failed[n] = failed[n] + 1
|
||||
}
|
||||
}
|
||||
checkFailed(t, failed)
|
||||
}
|
||||
|
||||
// tryFuzz tries fuzzing up to 20 times. Fail if check() never passes, report the highest
|
||||
// stage it ever got to.
|
||||
func tryFuzz(t *testing.T, f *Fuzzer, obj interface{}, check func() (stage int, passed bool)) {
|
||||
maxStage := 0
|
||||
for i := 0; i < 20; i++ {
|
||||
f.Fuzz(obj)
|
||||
stage, passed := check()
|
||||
if stage > maxStage {
|
||||
maxStage = stage
|
||||
}
|
||||
if passed {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("Only ever got to stage %v", maxStage)
|
||||
}
|
||||
|
||||
func TestFuzz_structmap(t *testing.T) {
|
||||
obj := &struct {
|
||||
A map[struct {
|
||||
S string
|
||||
}]struct {
|
||||
S2 string
|
||||
}
|
||||
B map[string]string
|
||||
}{}
|
||||
|
||||
tryFuzz(t, New(), obj, func() (int, bool) {
|
||||
if obj.A == nil {
|
||||
return 1, false
|
||||
}
|
||||
if len(obj.A) == 0 {
|
||||
return 2, false
|
||||
}
|
||||
for k, v := range obj.A {
|
||||
if k.S == "" {
|
||||
return 3, false
|
||||
}
|
||||
if v.S2 == "" {
|
||||
return 4, false
|
||||
}
|
||||
}
|
||||
|
||||
if obj.B == nil {
|
||||
return 5, false
|
||||
}
|
||||
if len(obj.B) == 0 {
|
||||
return 6, false
|
||||
}
|
||||
for k, v := range obj.B {
|
||||
if k == "" {
|
||||
return 7, false
|
||||
}
|
||||
if v == "" {
|
||||
return 8, false
|
||||
}
|
||||
}
|
||||
return 9, true
|
||||
})
|
||||
}
|
||||
|
||||
func TestFuzz_structslice(t *testing.T) {
|
||||
obj := &struct {
|
||||
A []struct {
|
||||
S string
|
||||
}
|
||||
B []string
|
||||
}{}
|
||||
|
||||
tryFuzz(t, New(), obj, func() (int, bool) {
|
||||
if obj.A == nil {
|
||||
return 1, false
|
||||
}
|
||||
if len(obj.A) == 0 {
|
||||
return 2, false
|
||||
}
|
||||
for _, v := range obj.A {
|
||||
if v.S == "" {
|
||||
return 3, false
|
||||
}
|
||||
}
|
||||
|
||||
if obj.B == nil {
|
||||
return 4, false
|
||||
}
|
||||
if len(obj.B) == 0 {
|
||||
return 5, false
|
||||
}
|
||||
for _, v := range obj.B {
|
||||
if v == "" {
|
||||
return 6, false
|
||||
}
|
||||
}
|
||||
return 7, true
|
||||
})
|
||||
}
|
||||
|
||||
func TestFuzz_custom(t *testing.T) {
|
||||
obj := &struct {
|
||||
A string
|
||||
B *string
|
||||
C map[string]string
|
||||
D *map[string]string
|
||||
}{}
|
||||
|
||||
testPhrase := "gotcalled"
|
||||
testMap := map[string]string{"C": "D"}
|
||||
f := New().Funcs(
|
||||
func(s *string, c Continue) {
|
||||
*s = testPhrase
|
||||
},
|
||||
func(m map[string]string, c Continue) {
|
||||
m["C"] = "D"
|
||||
},
|
||||
)
|
||||
|
||||
tryFuzz(t, f, obj, func() (int, bool) {
|
||||
if obj.A != testPhrase {
|
||||
return 1, false
|
||||
}
|
||||
if obj.B == nil {
|
||||
return 2, false
|
||||
}
|
||||
if *obj.B != testPhrase {
|
||||
return 3, false
|
||||
}
|
||||
if e, a := testMap, obj.C; !reflect.DeepEqual(e, a) {
|
||||
return 4, false
|
||||
}
|
||||
if obj.D == nil {
|
||||
return 5, false
|
||||
}
|
||||
if e, a := testMap, *obj.D; !reflect.DeepEqual(e, a) {
|
||||
return 6, false
|
||||
}
|
||||
return 7, true
|
||||
})
|
||||
}
|
||||
|
||||
type SelfFuzzer string
|
||||
|
||||
// Implement fuzz.Interface.
|
||||
func (sf *SelfFuzzer) Fuzz(c Continue) {
|
||||
*sf = selfFuzzerTestPhrase
|
||||
}
|
||||
|
||||
const selfFuzzerTestPhrase = "was fuzzed"
|
||||
|
||||
func TestFuzz_interface(t *testing.T) {
|
||||
f := New()
|
||||
|
||||
var obj1 SelfFuzzer
|
||||
tryFuzz(t, f, &obj1, func() (int, bool) {
|
||||
if obj1 != selfFuzzerTestPhrase {
|
||||
return 1, false
|
||||
}
|
||||
return 1, true
|
||||
})
|
||||
|
||||
var obj2 map[int]SelfFuzzer
|
||||
tryFuzz(t, f, &obj2, func() (int, bool) {
|
||||
for _, v := range obj2 {
|
||||
if v != selfFuzzerTestPhrase {
|
||||
return 1, false
|
||||
}
|
||||
}
|
||||
return 1, true
|
||||
})
|
||||
}
|
||||
|
||||
func TestFuzz_interfaceAndFunc(t *testing.T) {
|
||||
const privateTestPhrase = "private phrase"
|
||||
f := New().Funcs(
|
||||
// This should take precedence over SelfFuzzer.Fuzz().
|
||||
func(s *SelfFuzzer, c Continue) {
|
||||
*s = privateTestPhrase
|
||||
},
|
||||
)
|
||||
|
||||
var obj1 SelfFuzzer
|
||||
tryFuzz(t, f, &obj1, func() (int, bool) {
|
||||
if obj1 != privateTestPhrase {
|
||||
return 1, false
|
||||
}
|
||||
return 1, true
|
||||
})
|
||||
|
||||
var obj2 map[int]SelfFuzzer
|
||||
tryFuzz(t, f, &obj2, func() (int, bool) {
|
||||
for _, v := range obj2 {
|
||||
if v != privateTestPhrase {
|
||||
return 1, false
|
||||
}
|
||||
}
|
||||
return 1, true
|
||||
})
|
||||
}
|
||||
|
||||
func TestFuzz_noCustom(t *testing.T) {
|
||||
type Inner struct {
|
||||
Str string
|
||||
}
|
||||
type Outer struct {
|
||||
Str string
|
||||
In Inner
|
||||
}
|
||||
|
||||
testPhrase := "gotcalled"
|
||||
f := New().Funcs(
|
||||
func(outer *Outer, c Continue) {
|
||||
outer.Str = testPhrase
|
||||
c.Fuzz(&outer.In)
|
||||
},
|
||||
func(inner *Inner, c Continue) {
|
||||
inner.Str = testPhrase
|
||||
},
|
||||
)
|
||||
c := Continue{f: f, Rand: f.r}
|
||||
|
||||
// Fuzzer.Fuzz()
|
||||
obj1 := Outer{}
|
||||
f.Fuzz(&obj1)
|
||||
if obj1.Str != testPhrase {
|
||||
t.Errorf("expected Outer custom function to have been called")
|
||||
}
|
||||
if obj1.In.Str != testPhrase {
|
||||
t.Errorf("expected Inner custom function to have been called")
|
||||
}
|
||||
|
||||
// Continue.Fuzz()
|
||||
obj2 := Outer{}
|
||||
c.Fuzz(&obj2)
|
||||
if obj2.Str != testPhrase {
|
||||
t.Errorf("expected Outer custom function to have been called")
|
||||
}
|
||||
if obj2.In.Str != testPhrase {
|
||||
t.Errorf("expected Inner custom function to have been called")
|
||||
}
|
||||
|
||||
// Fuzzer.FuzzNoCustom()
|
||||
obj3 := Outer{}
|
||||
f.FuzzNoCustom(&obj3)
|
||||
if obj3.Str == testPhrase {
|
||||
t.Errorf("expected Outer custom function to not have been called")
|
||||
}
|
||||
if obj3.In.Str != testPhrase {
|
||||
t.Errorf("expected Inner custom function to have been called")
|
||||
}
|
||||
|
||||
// Continue.FuzzNoCustom()
|
||||
obj4 := Outer{}
|
||||
c.FuzzNoCustom(&obj4)
|
||||
if obj4.Str == testPhrase {
|
||||
t.Errorf("expected Outer custom function to not have been called")
|
||||
}
|
||||
if obj4.In.Str != testPhrase {
|
||||
t.Errorf("expected Inner custom function to have been called")
|
||||
}
|
||||
}
|
|
@ -25,15 +25,15 @@ func (err ErrValidation) Error() string {
|
|||
return fmt.Sprintf("An error occurred during validation: %s", err.msg)
|
||||
}
|
||||
|
||||
// ErrBadHeirarchy represents a missing snapshot at this current time.
|
||||
// ErrBadHierarchy represents a missing snapshot at this current time.
|
||||
// When delegations are implemented it will also represent a missing
|
||||
// delegation parent
|
||||
type ErrBadHeirarchy struct {
|
||||
type ErrBadHierarchy struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (err ErrBadHeirarchy) Error() string {
|
||||
return fmt.Sprintf("Heirarchy of updates in incorrect: %s", err.msg)
|
||||
func (err ErrBadHierarchy) Error() string {
|
||||
return fmt.Sprintf("Hierarchy of updates in incorrect: %s", err.msg)
|
||||
}
|
||||
|
||||
// ErrBadRoot represents a failure validating the root
|
||||
|
@ -77,14 +77,15 @@ func validateUpdate(gun string, updates []storage.MetaUpdate, store storage.Meta
|
|||
for _, v := range updates {
|
||||
roles[v.Role] = v
|
||||
}
|
||||
if err := heirarchyOK(roles); err != nil {
|
||||
logrus.Error("ErrBadHeirarchy: ", err.Error())
|
||||
return ErrBadHeirarchy{msg: err.Error()}
|
||||
if err := hierarchyOK(roles); err != nil {
|
||||
logrus.Error("ErrBadHierarchy: ", err.Error())
|
||||
return ErrBadHierarchy{msg: err.Error()}
|
||||
}
|
||||
logrus.Debug("Successfully validated hierarchy")
|
||||
|
||||
var root *data.SignedRoot
|
||||
oldRootJSON, err := store.GetCurrent(gun, rootRole)
|
||||
if _, ok := err.(*storage.ErrNotFound); !ok {
|
||||
if _, ok := err.(*storage.ErrNotFound); err != nil && !ok {
|
||||
// problem with storage. No expectation we can
|
||||
// write if we can't read so bail.
|
||||
logrus.Error("error reading previous root: ", err.Error())
|
||||
|
@ -102,10 +103,19 @@ func validateUpdate(gun string, updates []storage.MetaUpdate, store storage.Meta
|
|||
logrus.Error("ErrValidation: ", err.Error())
|
||||
return ErrValidation{msg: err.Error()}
|
||||
}
|
||||
logrus.Debug("Successfully validated root")
|
||||
} else {
|
||||
if oldRootJSON == nil {
|
||||
return ErrValidation{msg: "no pre-existing root and no root provided in update."}
|
||||
}
|
||||
parsedOldRoot := &data.SignedRoot{}
|
||||
if err := json.Unmarshal(oldRootJSON, parsedOldRoot); err != nil {
|
||||
return ErrValidation{msg: "pre-existing root is corrupted and no root provided in update."}
|
||||
}
|
||||
if err = repo.SetRoot(parsedOldRoot); err != nil {
|
||||
logrus.Error("ErrValidation: ", err.Error())
|
||||
return ErrValidation{msg: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: validate delegated targets roles.
|
||||
|
@ -117,10 +127,11 @@ func validateUpdate(gun string, updates []storage.MetaUpdate, store storage.Meta
|
|||
}
|
||||
repo.SetTargets(targetsRole, t)
|
||||
}
|
||||
logrus.Debug("Successfully validated targets")
|
||||
|
||||
var oldSnap *data.SignedSnapshot
|
||||
oldSnapJSON, err := store.GetCurrent(gun, snapshotRole)
|
||||
if _, ok := err.(*storage.ErrNotFound); !ok {
|
||||
if _, ok := err.(*storage.ErrNotFound); err != nil && !ok {
|
||||
// problem with storage. No expectation we can
|
||||
// write if we can't read so bail.
|
||||
logrus.Error("error reading previous snapshot: ", err.Error())
|
||||
|
@ -136,6 +147,7 @@ func validateUpdate(gun string, updates []storage.MetaUpdate, store storage.Meta
|
|||
logrus.Error("ErrBadSnapshot: ", err.Error())
|
||||
return ErrBadSnapshot{msg: err.Error()}
|
||||
}
|
||||
logrus.Debug("Successfully validated snapshot")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -151,21 +163,27 @@ func validateSnapshot(role string, oldSnap *data.SignedSnapshot, snapUpdate stor
|
|||
return err
|
||||
}
|
||||
|
||||
if oldSnap != nil {
|
||||
snap, err := data.SnapshotFromSigned(s)
|
||||
if err != nil {
|
||||
return errors.New("could not parse snapshot")
|
||||
}
|
||||
err = checkSnapshotEntries(role, oldSnap, snap, roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snap, err := data.SnapshotFromSigned(s)
|
||||
if err != nil {
|
||||
return errors.New("could not parse snapshot")
|
||||
}
|
||||
if !data.ValidTUFType(snap.Signed.Type, data.CanonicalSnapshotRole) {
|
||||
return errors.New("snapshot has wrong type")
|
||||
}
|
||||
err = checkSnapshotEntries(role, oldSnap, snap, roles)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSnapshotEntries(role string, oldSnap, snap *data.SignedSnapshot, roles map[string]storage.MetaUpdate) error {
|
||||
snapshotRole := data.RoleName(data.CanonicalSnapshotRole)
|
||||
timestampRole := data.RoleName(data.CanonicalTimestampRole) // just in case
|
||||
for r, update := range roles {
|
||||
if r == snapshotRole || r == timestampRole {
|
||||
continue
|
||||
}
|
||||
m, ok := snap.Signed.Meta[r]
|
||||
if !ok {
|
||||
return fmt.Errorf("snapshot missing metadata for %s", r)
|
||||
|
@ -204,10 +222,17 @@ func validateTargets(role string, roles map[string]storage.MetaUpdate, kdb *keys
|
|||
if err := signed.Verify(s, role, 0, kdb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.TargetsFromSigned(s)
|
||||
t, err := data.TargetsFromSigned(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !data.ValidTUFType(t.Signed.Type, data.CanonicalTargetsRole) {
|
||||
return nil, fmt.Errorf("%s has wrong type", role)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// check the snapshot is present. If it is, the heirarchy
|
||||
// check the snapshot is present. If it is, the hierarchy
|
||||
// of the update is OK. This seems like a simplistic check
|
||||
// but is completely sufficient for all possible use cases:
|
||||
// 1. the user is updating only the snapshot.
|
||||
|
@ -219,7 +244,7 @@ func validateTargets(role string, roles map[string]storage.MetaUpdate, kdb *keys
|
|||
// timestamp, the server will replace it on next
|
||||
// GET timestamp.jsonshould it detect the current
|
||||
// snapshot has a different hash to the one in the timestamp.
|
||||
func heirarchyOK(roles map[string]storage.MetaUpdate) error {
|
||||
func hierarchyOK(roles map[string]storage.MetaUpdate) error {
|
||||
snapshotRole := data.RoleName(data.CanonicalSnapshotRole)
|
||||
if _, ok := roles[snapshotRole]; !ok {
|
||||
return errors.New("snapshot missing from update")
|
||||
|
@ -253,6 +278,9 @@ func validateRoot(gun string, oldRoot, newRoot []byte) (*data.SignedRoot, error)
|
|||
// correct old key signatures.
|
||||
return nil, err
|
||||
}
|
||||
if !data.ValidTUFType(parsedNewRoot.Signed.Type, data.CanonicalRootRole) {
|
||||
return nil, fmt.Errorf("root has wrong type")
|
||||
}
|
||||
return parsedNewRoot, nil
|
||||
}
|
||||
|
||||
|
@ -261,6 +289,10 @@ func validateRoot(gun string, oldRoot, newRoot []byte) (*data.SignedRoot, error)
|
|||
// are valid.
|
||||
func checkRoot(oldRoot, newRoot *data.SignedRoot) error {
|
||||
rootRole := data.RoleName(data.CanonicalRootRole)
|
||||
targetsRole := data.RoleName(data.CanonicalTargetsRole)
|
||||
snapshotRole := data.RoleName(data.CanonicalSnapshotRole)
|
||||
timestampRole := data.RoleName(data.CanonicalTimestampRole)
|
||||
|
||||
var oldRootRole *data.RootRole
|
||||
newRootRole, ok := newRoot.Signed.Roles[rootRole]
|
||||
if !ok {
|
||||
|
@ -311,14 +343,35 @@ func checkRoot(oldRoot, newRoot *data.SignedRoot) error {
|
|||
}
|
||||
}
|
||||
newSigned, err := newRoot.ToSigned()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rotation {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = signed.VerifyRoot(newSigned, oldThreshold, oldKeys)
|
||||
if err != nil {
|
||||
return fmt.Errorf("rotation detected and new root was not signed with at least %d old keys", oldThreshold)
|
||||
}
|
||||
}
|
||||
return signed.VerifyRoot(newSigned, newRootRole.Threshold, newKeys)
|
||||
err = signed.VerifyRoot(newSigned, newRootRole.Threshold, newKeys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
root, err := data.RootFromSigned(newSigned)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// at a minimum, check the 4 required roles are present
|
||||
for _, r := range []string{rootRole, targetsRole, snapshotRole, timestampRole} {
|
||||
role, ok := root.Signed.Roles[r]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing required %s role from root", r)
|
||||
}
|
||||
if role.Threshold < 1 {
|
||||
return fmt.Errorf("%s role has invalid threshold", r)
|
||||
}
|
||||
if len(role.KeyIDs) < role.Threshold {
|
||||
return fmt.Errorf("%s role has insufficient number of keys", r)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,893 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/endophage/gotuf/data"
|
||||
"github.com/endophage/gotuf/signed"
|
||||
"github.com/endophage/gotuf/testutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/docker/notary/server/storage"
|
||||
)
|
||||
|
||||
func TestValidateEmptyNew(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateNoNewRoot(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
store.UpdateCurrent(
|
||||
"testGUN",
|
||||
storage.MetaUpdate{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateNoNewTargets(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
store.UpdateCurrent(
|
||||
"testGUN",
|
||||
storage.MetaUpdate{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateOnlySnapshot(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, _, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
store.UpdateCurrent(
|
||||
"testGUN",
|
||||
storage.MetaUpdate{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
)
|
||||
store.UpdateCurrent(
|
||||
"testGUN",
|
||||
storage.MetaUpdate{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateOldRoot(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
store.UpdateCurrent(
|
||||
"testGUN",
|
||||
storage.MetaUpdate{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateRootRotation(t *testing.T) {
|
||||
_, repo, crypto := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
store.UpdateCurrent(
|
||||
"testGUN",
|
||||
storage.MetaUpdate{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
)
|
||||
|
||||
oldRootRole := repo.Root.Signed.Roles["root"]
|
||||
oldRootKey := repo.Root.Signed.Keys[oldRootRole.KeyIDs[0]]
|
||||
|
||||
rootKey, err := crypto.Create("root", data.ED25519Key)
|
||||
assert.NoError(t, err)
|
||||
rootRole, err := data.NewRole("root", 1, []string{rootKey.ID()}, nil, nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
delete(repo.Root.Signed.Keys, oldRootRole.KeyIDs[0])
|
||||
|
||||
repo.Root.Signed.Roles["root"] = &rootRole.RootRole
|
||||
repo.Root.Signed.Keys[rootKey.ID()] = data.NewPrivateKey(rootKey.Algorithm(), rootKey.Public(), nil)
|
||||
|
||||
r, err = repo.SignRoot(data.DefaultExpires(data.CanonicalRootRole), nil)
|
||||
assert.NoError(t, err)
|
||||
err = signed.Sign(crypto, r, rootKey, oldRootKey)
|
||||
assert.NoError(t, err)
|
||||
|
||||
rt, err := data.RootFromSigned(r)
|
||||
assert.NoError(t, err)
|
||||
repo.SetRoot(rt)
|
||||
|
||||
sn, err = repo.SignSnapshot(data.DefaultExpires(data.CanonicalSnapshotRole), nil)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err = testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestValidateNoRoot(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
_, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrValidation{}, err)
|
||||
}
|
||||
|
||||
func TestValidateSnapshotMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, _, _, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadHierarchy{}, err)
|
||||
}
|
||||
|
||||
// ### Role missing negative tests ###
|
||||
// These tests remove a role from the Root file and
|
||||
// check for a ErrBadRoot
|
||||
func TestValidateRootRoleMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
delete(repo.Root.Signed.Roles, "root")
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadRoot{}, err)
|
||||
}
|
||||
|
||||
func TestValidateTargetsRoleMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
delete(repo.Root.Signed.Roles, "targets")
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadRoot{}, err)
|
||||
}
|
||||
|
||||
func TestValidateSnapshotRoleMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
delete(repo.Root.Signed.Roles, "snapshot")
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadRoot{}, err)
|
||||
}
|
||||
|
||||
// ### End role missing negative tests ###
|
||||
|
||||
// ### Signature missing negative tests ###
|
||||
func TestValidateRootSigMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
delete(repo.Root.Signed.Roles, "snapshot")
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
r.Signatures = nil
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadRoot{}, err)
|
||||
}
|
||||
|
||||
func TestValidateTargetsSigMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tg.Signatures = nil
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadTargets{}, err)
|
||||
}
|
||||
|
||||
func TestValidateSnapshotSigMissing(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
sn.Signatures = nil
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadSnapshot{}, err)
|
||||
}
|
||||
|
||||
// ### End signature missing negative tests ###
|
||||
|
||||
// ### Corrupted metadata negative tests ###
|
||||
func TestValidateRootCorrupt(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// flip all the bits in the first byte
|
||||
root[0] = root[0] ^ 0xff
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadRoot{}, err)
|
||||
}
|
||||
|
||||
func TestValidateTargetsCorrupt(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// flip all the bits in the first byte
|
||||
targets[0] = targets[0] ^ 0xff
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadTargets{}, err)
|
||||
}
|
||||
|
||||
func TestValidateSnapshotCorrupt(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// flip all the bits in the first byte
|
||||
snapshot[0] = snapshot[0] ^ 0xff
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadSnapshot{}, err)
|
||||
}
|
||||
|
||||
// ### End corrupted metadata negative tests ###
|
||||
|
||||
// ### Snapshot size mismatch negative tests ###
|
||||
func TestValidateRootModifiedSize(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// add another copy of the signature so the hash is different
|
||||
r.Signatures = append(r.Signatures, r.Signatures[0])
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// flip all the bits in the first byte
|
||||
root[0] = root[0] ^ 0xff
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadRoot{}, err)
|
||||
}
|
||||
|
||||
func TestValidateTargetsModifiedSize(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// add another copy of the signature so the hash is different
|
||||
tg.Signatures = append(tg.Signatures, tg.Signatures[0])
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadSnapshot{}, err)
|
||||
}
|
||||
|
||||
// ### End snapshot size mismatch negative tests ###
|
||||
|
||||
// ### Snapshot hash mismatch negative tests ###
|
||||
func TestValidateRootModifiedHash(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
snap, err := data.SnapshotFromSigned(sn)
|
||||
assert.NoError(t, err)
|
||||
snap.Signed.Meta["root"].Hashes["sha256"][0] = snap.Signed.Meta["root"].Hashes["sha256"][0] ^ 0xff
|
||||
|
||||
sn, err = snap.ToSigned()
|
||||
assert.NoError(t, err)
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadSnapshot{}, err)
|
||||
}
|
||||
|
||||
func TestValidateTargetsModifiedHash(t *testing.T) {
|
||||
_, repo, _ := testutils.EmptyRepo()
|
||||
store := storage.NewMemStorage()
|
||||
|
||||
r, tg, sn, ts, err := testutils.Sign(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
snap, err := data.SnapshotFromSigned(sn)
|
||||
assert.NoError(t, err)
|
||||
snap.Signed.Meta["targets"].Hashes["sha256"][0] = snap.Signed.Meta["targets"].Hashes["sha256"][0] ^ 0xff
|
||||
|
||||
sn, err = snap.ToSigned()
|
||||
assert.NoError(t, err)
|
||||
|
||||
root, targets, snapshot, timestamp, err := testutils.Serialize(r, tg, sn, ts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
updates := []storage.MetaUpdate{
|
||||
{
|
||||
Role: "root",
|
||||
Version: 1,
|
||||
Data: root,
|
||||
},
|
||||
{
|
||||
Role: "targets",
|
||||
Version: 1,
|
||||
Data: targets,
|
||||
},
|
||||
{
|
||||
Role: "snapshot",
|
||||
Version: 1,
|
||||
Data: snapshot,
|
||||
},
|
||||
{
|
||||
Role: "timestamp",
|
||||
Version: 1,
|
||||
Data: timestamp,
|
||||
},
|
||||
}
|
||||
|
||||
err = validateUpdate("testGUN", updates, store)
|
||||
assert.Error(t, err)
|
||||
assert.IsType(t, ErrBadSnapshot{}, err)
|
||||
}
|
||||
|
||||
// ### End snapshot hash mismatch negative tests ###
|
Loading…
Reference in New Issue