From 8dcd664e0eda8e368af3c53e0c89918783eedbd2 Mon Sep 17 00:00:00 2001 From: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> Date: Sun, 1 Jan 2023 13:52:18 -0600 Subject: [PATCH] Refactor e2e - Refactor e2e Signed-off-by: naveensrinivasan <172697+naveensrinivasan@users.noreply.github.com> --- cmd/rekor-server/e2e_test.go | 278 +++++++++++++++++++++++++++ pkg/util/util.go | 120 ++++++++++++ tests/harness_test.go | 351 ----------------------------------- tests/util.go | 40 +--- 4 files changed, 403 insertions(+), 386 deletions(-) delete mode 100644 tests/harness_test.go diff --git a/cmd/rekor-server/e2e_test.go b/cmd/rekor-server/e2e_test.go index 7fac3aae8..a17d09537 100644 --- a/cmd/rekor-server/e2e_test.go +++ b/cmd/rekor-server/e2e_test.go @@ -20,9 +20,15 @@ package main import ( "bufio" "bytes" + "crypto" + "crypto/ecdsa" "crypto/sha256" + "crypto/x509" + "encoding/base64" "encoding/hex" "encoding/json" + "encoding/pem" + "errors" "fmt" "io/ioutil" "net/http" @@ -33,6 +39,15 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" + "github.com/in-toto/in-toto-golang/in_toto" + slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" + "github.com/secure-systems-lab/go-securesystemslib/dsse" + "github.com/sigstore/rekor/pkg/generated/models" + sigx509 "github.com/sigstore/rekor/pkg/pki/x509" + "github.com/sigstore/rekor/pkg/types" + "github.com/sigstore/sigstore/pkg/signature" + "github.com/sigstore/rekor/pkg/sharding" "github.com/sigstore/rekor/pkg/util" @@ -358,3 +373,266 @@ func TestSearchQueryMalformedEntry(t *testing.T) { t.Fatalf("expected status 400, got %d instead", resp.StatusCode) } } +func entryID(t *testing.T, uuid string) string { + t.Helper() + if sharding.ValidateEntryID(uuid) == nil { + return uuid + } + treeID, err := strconv.Atoi(os.Getenv("TREE_ID")) + if err != nil { + t.Fatal(err) + } + tid := strconv.FormatInt(int64(treeID), 16) + ts, err := sharding.PadToTreeIDLen(tid) + if err != nil { + t.Fatal(err) + } + return ts + uuid +} +func TestHarnessGetAllEntriesUUID(t *testing.T) { + if util.RekorCLIIncompatible() { + t.Skipf("Skipping getting entries by UUID, old rekor-cli version %s is incompatible with server version %s", os.Getenv("CLI_VERSION"), os.Getenv("SERVER_VERSION")) + } + + treeSize := util.ActiveTreeSize(t) + if treeSize == 0 { + t.Fatal("There are 0 entries in the log, there should be at least 2") + } + _, entries := util.GetEntries(t) + + for _, e := range entries { + outUUID := util.RunCli(t, "get", "--uuid", e.UUID, "--format", "json") + outEntryID := util.RunCli(t, "get", "--uuid", entryID(t, e.UUID), "--format", "json") + + if outUUID != outEntryID { + t.Fatalf("Getting by uuid %s and entryID %s gave different outputs:\nuuid: %v\nentryID:%v\n", e.UUID, entryID(t, e.UUID), outUUID, outEntryID) + } + + if !strings.Contains(outUUID, "IntotoObj") { + continue + } + var intotoObj struct { + Attestation string + } + if err := json.Unmarshal([]byte(outUUID), &intotoObj); err != nil { + t.Fatal(err) + } + if intotoObj.Attestation != e.Attestation { + t.Fatalf("attestations don't match, got %v expected %v", intotoObj.Attestation, e.Attestation) + } + } +} + +// Make sure we can get and verify all entries +// For attestations, make sure we can see the attestation +// Older versions of the CLI may not be able to parse the retrieved entry. +func TestHarnessGetAllEntriesLogIndex(t *testing.T) { + if util.RekorCLIIncompatible() { + t.Skipf("Skipping getting entries by UUID, old rekor-cli version %s is incompatible with server version %s", os.Getenv("CLI_VERSION"), os.Getenv("SERVER_VERSION")) + } + + treeSize := util.ActiveTreeSize(t) + if treeSize == 0 { + t.Fatal("There are 0 entries in the log, there should be at least 2") + } + for i := 0; i < treeSize; i++ { + out := util.RunCli(t, "get", "--log-index", fmt.Sprintf("%d", i), "--format", "json") + if !strings.Contains(out, "IntotoObj") { + continue + } + var intotoObj struct { + Attestation string + } + if err := json.Unmarshal([]byte(out), &intotoObj); err != nil { + t.Fatal(err) + } + util.CompareAttestation(t, i, intotoObj.Attestation) + t.Log("IntotoObj matches stored attestation") + } +} + +type StoredEntry struct { + Attestation string + UUID string +} + +// Make sure we can add an entry +func TestHarnessAddEntry(t *testing.T) { + // Create a random artifact and sign it. + artifactPath := filepath.Join(t.TempDir(), "artifact") + sigPath := filepath.Join(t.TempDir(), "signature.asc") + + sigx509.CreatedX509SignedArtifact(t, artifactPath, sigPath) + dataBytes, _ := ioutil.ReadFile(artifactPath) + h := sha256.Sum256(dataBytes) + dataSHA := hex.EncodeToString(h[:]) + + // Write the public key to a file + pubPath := filepath.Join(t.TempDir(), "pubKey.asc") + if err := ioutil.WriteFile(pubPath, []byte(sigx509.RSACert), 0644); err != nil { + t.Fatal(err) + } + + // Verify should fail initially + util.RunCliErr(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) + + // It should upload successfully. + out := util.RunCli(t, "upload", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Created entry at") + uuid := util.GetUUIDFromUploadOutput(t, out) + logIndex := util.GetLogIndexFromUploadOutput(t, out) + + if !util.RekorCLIIncompatible() { + // Now we should be able to verify it. + out = util.RunCli(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) + util.OutputContains(t, out, "Inclusion Proof:") + } + + saveEntry(t, logIndex, StoredEntry{UUID: uuid}) +} + +// Make sure we can add an intoto entry +func TestHarnessAddIntoto(t *testing.T) { + td := t.TempDir() + attestationPath := filepath.Join(td, "attestation.json") + pubKeyPath := filepath.Join(td, "pub.pem") + + // Get some random data so it's unique each run + d := util.RandomData(t, 10) + id := base64.StdEncoding.EncodeToString(d) + + it := in_toto.ProvenanceStatement{ + StatementHeader: in_toto.StatementHeader{ + Type: in_toto.StatementInTotoV01, + PredicateType: slsa.PredicateSLSAProvenance, + Subject: []in_toto.Subject{ + { + Name: "foobar", + Digest: slsa.DigestSet{ + "foo": "bar", + }, + }, + }, + }, + Predicate: slsa.ProvenancePredicate{ + Builder: slsa.ProvenanceBuilder{ + ID: "foo" + id, + }, + }, + } + + b, err := json.Marshal(it) + if err != nil { + t.Fatal(err) + } + + pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) + priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) + if err != nil { + t.Fatal(err) + } + + s, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) + if err != nil { + t.Fatal(err) + } + + signer, err := dsse.NewEnvelopeSigner(&sigx509.Verifier{ + S: s, + }) + if err != nil { + t.Fatal(err) + } + + env, err := signer.SignPayload("application/vnd.in-toto+json", b) + if err != nil { + t.Fatal(err) + } + + eb, err := json.Marshal(env) + if err != nil { + t.Fatal(err) + } + + util.Write(t, string(eb), attestationPath) + util.Write(t, sigx509.ECDSAPub, pubKeyPath) + + // If we do it twice, it should already exist + out := util.RunCliStdout(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) + util.OutputContains(t, out, "Created entry at") + uuid := util.GetUUIDFromUploadOutput(t, out) + logIndex := util.GetLogIndexFromUploadOutput(t, out) + + out = util.RunCli(t, "get", "--log-index", fmt.Sprintf("%d", logIndex), "--format=json") + g := util.GetOut{} + if err := json.Unmarshal([]byte(out), &g); err != nil { + t.Fatal(err) + } + // The attestation should be stored at /var/run/attestations/sha256:digest + + got := in_toto.ProvenanceStatement{} + if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(it, got); diff != "" { + t.Errorf("diff: %s", diff) + } + + attHash := sha256.Sum256(b) + + intotoModel := &models.IntotoV002Schema{} + if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil { + t.Errorf("could not convert body into intoto type: %v", err) + } + if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil { + t.Errorf("could not find hash over attestation %v", intotoModel) + } + recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value) + if err != nil { + t.Errorf("error converting attestation hash to []byte: %v", err) + } + + if !bytes.Equal(attHash[:], recordedPayloadHash) { + t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), + *intotoModel.Content.PayloadHash.Value)) + } + + out = util.RunCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) + util.OutputContains(t, out, "Entry already exists") + saveEntry(t, logIndex, StoredEntry{Attestation: g.Attestation, UUID: uuid}) +} + +func getEntries(t *testing.T) (string, map[int]StoredEntry) { + tmpDir := os.Getenv("REKOR_HARNESS_TMPDIR") + if tmpDir == "" { + t.Skip("Skipping test, REKOR_HARNESS_TMPDIR is not set") + } + file := filepath.Join(tmpDir, "attestations") + + t.Log("Reading", file) + attestations := map[int]StoredEntry{} + contents, err := os.ReadFile(file) + if errors.Is(err, os.ErrNotExist) || contents == nil { + return file, attestations + } + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(contents, &attestations); err != nil { + t.Fatal(err) + } + return file, attestations +} + +func saveEntry(t *testing.T, logIndex int, entry StoredEntry) { + file, attestations := getEntries(t) + t.Logf("Storing entry for logIndex %d", logIndex) + attestations[logIndex] = entry + contents, err := json.Marshal(attestations) + if err != nil { + t.Fatal(err) + } + if err := os.WriteFile(file, contents, 0777); err != nil { + t.Fatal(err) + } +} diff --git a/pkg/util/util.go b/pkg/util/util.go index fa0e82336..b800738aa 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -20,6 +20,8 @@ package util import ( "bytes" "encoding/base64" + "encoding/json" + "errors" "fmt" "io/ioutil" "math/rand" @@ -27,6 +29,7 @@ import ( "os/exec" "path" "path/filepath" + "strconv" "strings" "testing" "time" @@ -49,6 +52,10 @@ type GetOut struct { LogIndex int IntegratedTime int64 } +type StoredEntry struct { + Attestation string + UUID string +} // This was generated with gpg --gen-key, and all defaults. // The email is "test@rekor.dev", and the name is Rekor Test. @@ -459,3 +466,116 @@ func SetupTestData(t *testing.T) { out := RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath) OutputContains(t, out, "Created entry at") } + +func ActiveTreeSize(t *testing.T) int { + out := runCliStdout(t, "loginfo", "--format", "json", "--store_tree_state", "false") + t.Log(out) + var s struct { + ActiveTreeSize int + } + if err := json.Unmarshal([]byte(out), &s); err != nil { + t.Fatal(err) + } + return s.ActiveTreeSize +} + +// RekorCLIIncompatible Check if we have a new server version and an old CLI version +// since the new server returns an EntryID but the old CLI version expects a UUID +// Also, new rekor server allows upload of intoto v0.0.2, and old rekor cli versions +// don't understand how to parse these entries. +// TODO: use semver comparisons. +func RekorCLIIncompatible() bool { + if sv := os.Getenv("SERVER_VERSION"); sv != "v0.10.0" && sv != "v0.11.0" { + if cv := os.Getenv("CLI_VERSION"); cv == "v0.10.0" || cv == "v0.11.0" { + return true + } + } + + return false +} +func runCliStdout(t *testing.T, arg ...string) string { + t.Helper() + // Coverage flag must be the first arg passed to coverage binary + // No impact when running with regular binary + arg = append([]string{coverageFlag()}, arg...) + arg = append(arg, rekorServerFlag()) + c := exec.Command(cli, arg...) + + if os.Getenv("REKORTMPDIR") != "" { + // ensure that we use a clean state.json file for each run + c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR")) + } + b, err := c.Output() + if err != nil { + t.Log(string(b)) + t.Fatal(err) + } + return stripCoverageOutput(string(b)) +} +func GetEntries(t *testing.T) (string, map[int]StoredEntry) { + t.Helper() + tmpDir := os.Getenv("REKOR_HARNESS_TMPDIR") + if tmpDir == "" { + t.Skip("Skipping test, REKOR_HARNESS_TMPDIR is not set") + } + file := filepath.Join(tmpDir, "attestations") + + t.Log("Reading", file) + attestations := map[int]StoredEntry{} + contents, err := os.ReadFile(file) + if errors.Is(err, os.ErrNotExist) || contents == nil { + return file, attestations + } + if err != nil { + t.Fatal(err) + } + if err := json.Unmarshal(contents, &attestations); err != nil { + t.Fatal(err) + } + return file, attestations +} + +func CompareAttestation(t *testing.T, logIndex int, got string) { + t.Helper() + _, entries := GetEntries(t) + expected, ok := entries[logIndex] + if !ok { + t.Fatalf("expected to find persisted entries with logIndex %d but none existed: %v", logIndex, entries) + } + + if got != expected.Attestation { + t.Fatalf("attestations don't match, got %v expected %v", got, expected) + } +} + +func GetLogIndexFromUploadOutput(t *testing.T, out string) int { + t.Helper() + t.Log(out) + // Output looks like "Created entry at index X, available at $URL/UUID", so grab the index X: + split := strings.Split(strings.TrimSpace(out), ",") + ss := strings.Split(split[0], " ") + i, err := strconv.Atoi(ss[len(ss)-1]) + if err != nil { + t.Fatal(err) + } + return i +} +func RunCliStdout(t *testing.T, arg ...string) string { + t.Helper() + // Coverage flag must be the first arg passed to coverage binary + // No impact when running with regular binary + arg = append([]string{coverageFlag()}, arg...) + arg = append(arg, rekorServerFlag()) + c := exec.Command(cli, arg...) + + if os.Getenv("REKORTMPDIR") != "" { + // ensure that we use a clean state.json file for each run + c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR")) + } + b, err := c.Output() + if err != nil { + t.Log(string(b)) + t.Fatal(err) + } + return stripCoverageOutput(string(b)) +} diff --git a/tests/harness_test.go b/tests/harness_test.go deleted file mode 100644 index ec26781e2..000000000 --- a/tests/harness_test.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2022 The Sigstore Authors. -// -// 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. - -//go:build e2e -// +build e2e - -package e2e - -import ( - "bytes" - "crypto" - "crypto/ecdsa" - "crypto/sha256" - "crypto/x509" - "encoding/base64" - "encoding/hex" - "encoding/json" - "encoding/pem" - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "strconv" - "strings" - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/in-toto/in-toto-golang/in_toto" - slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2" - "github.com/secure-systems-lab/go-securesystemslib/dsse" - "github.com/sigstore/rekor/pkg/generated/models" - sigx509 "github.com/sigstore/rekor/pkg/pki/x509" - "github.com/sigstore/rekor/pkg/sharding" - "github.com/sigstore/rekor/pkg/types" - "github.com/sigstore/sigstore/pkg/signature" -) - -type StoredEntry struct { - Attestation string - UUID string -} - -// Make sure we can add an entry -func TestHarnessAddEntry(t *testing.T) { - // Create a random artifact and sign it. - artifactPath := filepath.Join(t.TempDir(), "artifact") - sigPath := filepath.Join(t.TempDir(), "signature.asc") - - sigx509.CreatedX509SignedArtifact(t, artifactPath, sigPath) - dataBytes, _ := ioutil.ReadFile(artifactPath) - h := sha256.Sum256(dataBytes) - dataSHA := hex.EncodeToString(h[:]) - - // Write the public key to a file - pubPath := filepath.Join(t.TempDir(), "pubKey.asc") - if err := ioutil.WriteFile(pubPath, []byte(sigx509.RSACert), 0644); err != nil { - t.Fatal(err) - } - - // Verify should fail initially - runCliErr(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) - - // It should upload successfully. - out := runCli(t, "upload", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) - outputContains(t, out, "Created entry at") - uuid := getUUIDFromUploadOutput(t, out) - logIndex := getLogIndexFromUploadOutput(t, out) - - if !rekorCLIIncompatible() { - // Now we should be able to verify it. - out = runCli(t, "verify", "--type=hashedrekord", "--pki-format=x509", "--artifact-hash", dataSHA, "--signature", sigPath, "--public-key", pubPath) - outputContains(t, out, "Inclusion Proof:") - } - - saveEntry(t, logIndex, StoredEntry{UUID: uuid}) -} - -// Make sure we can add an intoto entry -func TestHarnessAddIntoto(t *testing.T) { - td := t.TempDir() - attestationPath := filepath.Join(td, "attestation.json") - pubKeyPath := filepath.Join(td, "pub.pem") - - // Get some random data so it's unique each run - d := randomData(t, 10) - id := base64.StdEncoding.EncodeToString(d) - - it := in_toto.ProvenanceStatement{ - StatementHeader: in_toto.StatementHeader{ - Type: in_toto.StatementInTotoV01, - PredicateType: slsa.PredicateSLSAProvenance, - Subject: []in_toto.Subject{ - { - Name: "foobar", - Digest: slsa.DigestSet{ - "foo": "bar", - }, - }, - }, - }, - Predicate: slsa.ProvenancePredicate{ - Builder: slsa.ProvenanceBuilder{ - ID: "foo" + id, - }, - }, - } - - b, err := json.Marshal(it) - if err != nil { - t.Fatal(err) - } - - pb, _ := pem.Decode([]byte(sigx509.ECDSAPriv)) - priv, err := x509.ParsePKCS8PrivateKey(pb.Bytes) - if err != nil { - t.Fatal(err) - } - - s, err := signature.LoadECDSASigner(priv.(*ecdsa.PrivateKey), crypto.SHA256) - if err != nil { - t.Fatal(err) - } - - signer, err := dsse.NewEnvelopeSigner(&sigx509.Verifier{ - S: s, - }) - if err != nil { - t.Fatal(err) - } - - env, err := signer.SignPayload("application/vnd.in-toto+json", b) - if err != nil { - t.Fatal(err) - } - - eb, err := json.Marshal(env) - if err != nil { - t.Fatal(err) - } - - write(t, string(eb), attestationPath) - write(t, sigx509.ECDSAPub, pubKeyPath) - - // If we do it twice, it should already exist - out := runCliStdout(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) - outputContains(t, out, "Created entry at") - uuid := getUUIDFromUploadOutput(t, out) - logIndex := getLogIndexFromUploadOutput(t, out) - - out = runCli(t, "get", "--log-index", fmt.Sprintf("%d", logIndex), "--format=json") - g := getOut{} - if err := json.Unmarshal([]byte(out), &g); err != nil { - t.Fatal(err) - } - // The attestation should be stored at /var/run/attestations/sha256:digest - - got := in_toto.ProvenanceStatement{} - if err := json.Unmarshal([]byte(g.Attestation), &got); err != nil { - t.Fatal(err) - } - if diff := cmp.Diff(it, got); diff != "" { - t.Errorf("diff: %s", diff) - } - - attHash := sha256.Sum256(b) - - intotoModel := &models.IntotoV002Schema{} - if err := types.DecodeEntry(g.Body.(map[string]interface{})["IntotoObj"], intotoModel); err != nil { - t.Errorf("could not convert body into intoto type: %v", err) - } - if intotoModel.Content == nil || intotoModel.Content.PayloadHash == nil { - t.Errorf("could not find hash over attestation %v", intotoModel) - } - recordedPayloadHash, err := hex.DecodeString(*intotoModel.Content.PayloadHash.Value) - if err != nil { - t.Errorf("error converting attestation hash to []byte: %v", err) - } - - if !bytes.Equal(attHash[:], recordedPayloadHash) { - t.Fatal(fmt.Errorf("attestation hash %v doesnt match the payload we sent %v", hex.EncodeToString(attHash[:]), - *intotoModel.Content.PayloadHash.Value)) - } - - out = runCli(t, "upload", "--artifact", attestationPath, "--type", "intoto", "--public-key", pubKeyPath) - outputContains(t, out, "Entry already exists") - saveEntry(t, logIndex, StoredEntry{Attestation: g.Attestation, UUID: uuid}) -} - -func getEntries(t *testing.T) (string, map[int]StoredEntry) { - tmpDir := os.Getenv("REKOR_HARNESS_TMPDIR") - if tmpDir == "" { - t.Skip("Skipping test, REKOR_HARNESS_TMPDIR is not set") - } - file := filepath.Join(tmpDir, "attestations") - - t.Log("Reading", file) - attestations := map[int]StoredEntry{} - contents, err := os.ReadFile(file) - if errors.Is(err, os.ErrNotExist) || contents == nil { - return file, attestations - } - if err != nil { - t.Fatal(err) - } - if err := json.Unmarshal(contents, &attestations); err != nil { - t.Fatal(err) - } - return file, attestations -} - -func saveEntry(t *testing.T, logIndex int, entry StoredEntry) { - file, attestations := getEntries(t) - t.Logf("Storing entry for logIndex %d", logIndex) - attestations[logIndex] = entry - contents, err := json.Marshal(attestations) - if err != nil { - t.Fatal(err) - } - if err := os.WriteFile(file, contents, 0777); err != nil { - t.Fatal(err) - } -} - -func compareAttestation(t *testing.T, logIndex int, got string) { - _, entries := getEntries(t) - expected, ok := entries[logIndex] - if !ok { - t.Fatalf("expected to find persisted entries with logIndex %d but none existed: %v", logIndex, entries) - } - - if got != expected.Attestation { - t.Fatalf("attestations don't match, got %v expected %v", got, expected) - } -} - -// Make sure we can get and verify all entries -// For attestations, make sure we can see the attestation -// Older versions of the CLI may not be able to parse the retrieved entry. -func TestHarnessGetAllEntriesLogIndex(t *testing.T) { - if rekorCLIIncompatible() { - t.Skipf("Skipping getting entries by UUID, old rekor-cli version %s is incompatible with server version %s", os.Getenv("CLI_VERSION"), os.Getenv("SERVER_VERSION")) - } - - treeSize := activeTreeSize(t) - if treeSize == 0 { - t.Fatal("There are 0 entries in the log, there should be at least 2") - } - for i := 0; i < treeSize; i++ { - out := runCli(t, "get", "--log-index", fmt.Sprintf("%d", i), "--format", "json") - if !strings.Contains(out, "IntotoObj") { - continue - } - var intotoObj struct { - Attestation string - } - if err := json.Unmarshal([]byte(out), &intotoObj); err != nil { - t.Fatal(err) - } - compareAttestation(t, i, intotoObj.Attestation) - t.Log("IntotoObj matches stored attestation") - } -} - -func TestHarnessGetAllEntriesUUID(t *testing.T) { - if rekorCLIIncompatible() { - t.Skipf("Skipping getting entries by UUID, old rekor-cli version %s is incompatible with server version %s", os.Getenv("CLI_VERSION"), os.Getenv("SERVER_VERSION")) - } - - treeSize := activeTreeSize(t) - if treeSize == 0 { - t.Fatal("There are 0 entries in the log, there should be at least 2") - } - _, entries := getEntries(t) - - for _, e := range entries { - outUUID := runCli(t, "get", "--uuid", e.UUID, "--format", "json") - outEntryID := runCli(t, "get", "--uuid", entryID(t, e.UUID), "--format", "json") - - if outUUID != outEntryID { - t.Fatalf("Getting by uuid %s and entryID %s gave different outputs:\nuuid: %v\nentryID:%v\n", e.UUID, entryID(t, e.UUID), outUUID, outEntryID) - } - - if !strings.Contains(outUUID, "IntotoObj") { - continue - } - var intotoObj struct { - Attestation string - } - if err := json.Unmarshal([]byte(outUUID), &intotoObj); err != nil { - t.Fatal(err) - } - if intotoObj.Attestation != e.Attestation { - t.Fatalf("attestations don't match, got %v expected %v", intotoObj.Attestation, e.Attestation) - } - } -} - -func entryID(t *testing.T, uuid string) string { - if sharding.ValidateEntryID(uuid) == nil { - return uuid - } - treeID, err := strconv.Atoi(os.Getenv("TREE_ID")) - if err != nil { - t.Fatal(err) - } - tid := strconv.FormatInt(int64(treeID), 16) - ts, err := sharding.PadToTreeIDLen(tid) - if err != nil { - t.Fatal(err) - } - return ts + uuid -} - -func activeTreeSize(t *testing.T) int { - out := runCliStdout(t, "loginfo", "--format", "json", "--store_tree_state", "false") - t.Log(string(out)) - var s struct { - ActiveTreeSize int - } - if err := json.Unmarshal([]byte(out), &s); err != nil { - t.Fatal(err) - } - return s.ActiveTreeSize -} - -// Check if we have a new server version and an old CLI version -// since the new server returns an EntryID but the old CLI version expects a UUID -// Also, new rekor server allows upload of intoto v0.0.2, and old rekor cli versions -// don't understand how to parse these entries. -// TODO: use semver comparisons. -func rekorCLIIncompatible() bool { - if sv := os.Getenv("SERVER_VERSION"); sv != "v0.10.0" && sv != "v0.11.0" { - if cv := os.Getenv("CLI_VERSION"); cv == "v0.10.0" || cv == "v0.11.0" { - return true - } - } - - return false -} diff --git a/tests/util.go b/tests/util.go index 842a350dc..91f20a9d2 100644 --- a/tests/util.go +++ b/tests/util.go @@ -14,7 +14,6 @@ // limitations under the License. //go:build e2e -// +build e2e package e2e @@ -33,9 +32,8 @@ import ( ) const ( - cli = "../rekor-cli" - server = "../rekor-server" - nodeDataDir = "node" + cli = "../rekor-cli" + server = "../rekor-server" ) func outputContains(t *testing.T, output, sub string) { @@ -76,26 +74,6 @@ func runCli(t *testing.T, arg ...string) string { return run(t, "", cli, arg...) } -func runCliStdout(t *testing.T, arg ...string) string { - t.Helper() - // Coverage flag must be the first arg passed to coverage binary - // No impact when running with regular binary - arg = append([]string{coverageFlag()}, arg...) - arg = append(arg, rekorServerFlag()) - c := exec.Command(cli, arg...) - - if os.Getenv("REKORTMPDIR") != "" { - // ensure that we use a clean state.json file for each run - c.Env = append(c.Env, "HOME="+os.Getenv("REKORTMPDIR")) - } - b, err := c.Output() - if err != nil { - t.Log(string(b)) - t.Fatal(err) - } - return stripCoverageOutput(string(b)) -} - func runCliErr(t *testing.T, arg ...string) string { t.Helper() // Coverage flag must be the first arg passed to coverage binary @@ -134,20 +112,12 @@ func stripCoverageOutput(out string) string { return strings.Split(strings.Split(out, "PASS")[0], "FAIL")[0] } -func readFile(t *testing.T, p string) string { - b, err := ioutil.ReadFile(p) - if err != nil { - t.Fatal(err) - } - return strings.TrimSpace(string(b)) -} - func randomSuffix(n int) string { const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" b := make([]byte, n) for i := range b { - b[i] = letterBytes[rand.Intn(len(letterBytes))] + b[i] = letterBytes[rand.Intn(len(letterBytes))] //nolint:gosec } return string(b) } @@ -156,7 +126,7 @@ func randomData(t *testing.T, n int) []byte { t.Helper() rand.Seed(time.Now().UnixNano()) data := make([]byte, n) - if _, err := rand.Read(data[:]); err != nil { + if _, err := rand.Read(data[:]); err != nil { //nolint:gosec t.Fatal(err) } return data @@ -188,7 +158,7 @@ func extractLogEntry(t *testing.T, le models.LogEntry) models.LogEntryAnon { func write(t *testing.T, data string, path string) { t.Helper() - if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { + if err := ioutil.WriteFile(path, []byte(data), 0644); err != nil { //nolint:gosec t.Fatal(err) } }