Skip to content

Commit

Permalink
re-implement collection, test with minisign
Browse files Browse the repository at this point in the history
  • Loading branch information
dsa0x committed Aug 17, 2022
1 parent 8f45dd9 commit d99c7c3
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 31 deletions.
2 changes: 1 addition & 1 deletion cmd/rekor-cli/app/search.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func addSearchPFlags(cmd *cobra.Command) error {

cmd.Flags().Var(NewFlagValue(emailFlag, ""), "email", "email associated with the public key's subject")

cmd.Flags().Var(NewFlagValue(operatorFlag, ""), "operator", "operator to use for the search")
cmd.Flags().Var(NewFlagValue(operatorFlag, ""), "operator", "operator to use for the search. supported values are 'and' and 'or'")
return nil
}

Expand Down
91 changes: 80 additions & 11 deletions pkg/api/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ import (

func SearchIndexHandler(params index.SearchIndexParams) middleware.Responder {
httpReqCtx := params.HTTPRequest.Context()
var result Container
var result Collection

// default behaviour mimics "or"
if params.Query.Operator == "and" {
result = make(Uniq)
if params.Query.Operator != "" {
result = NewSuperSet(params.Query.Operator)
} else {
result = &Arr{}
}
Expand Down Expand Up @@ -87,7 +86,7 @@ func SearchIndexHandler(params index.SearchIndexParams) middleware.Responder {
result.Add(resultUUIDs)
}

return index.NewSearchIndexOK().WithPayload(result.Result())
return index.NewSearchIndexOK().WithPayload(result.Values())
}

func SearchIndexNotImplementedHandler(params index.SearchIndexParams) middleware.Responder {
Expand All @@ -108,31 +107,101 @@ func storeAttestation(ctx context.Context, uuid string, attestation []byte) erro
return storageClient.StoreAttestation(ctx, uuid, attestation)
}

type Container interface {
// Collection is a collection of elements.
type Collection interface {
// Add adds elements to the collection.
Add([]string)
Result() []string

// Values returns the collection as a slice of strings.
Values() []string
}

// Uniq is a collection of unique elements.
type Uniq map[string]struct{}

func NewUniq() Uniq {
return make(Uniq)
}

func (u Uniq) Add(elements []string) {
for _, v := range elements {
u[v] = struct{}{}
for _, e := range elements {
u[e] = struct{}{}
}
}
func (u Uniq) Result() []string {

func (u Uniq) Values() []string {
var result []string
for k := range u {
result = append(result, k)
}
return result
}

// Intersect returns the intersection of two collections.
func (u Uniq) Intersect(other Uniq) Uniq {
result := make(Uniq)
for k := range u {
if _, ok := other[k]; ok {
result[k] = struct{}{}
}
}
return result
}

func (u Uniq) Union(other Uniq) Uniq {
result := make(Uniq)
for k := range u {
result[k] = struct{}{}
}
for k := range other {
result[k] = struct{}{}
}
return result
}

// SuperSet is a collection of sets.
//
// its result is a union or intersection of all the sets, depending on the operator.
type SuperSet struct {
subsets []Uniq
operator string
}

// NewSuperSet creates a new SuperSet.
func NewSuperSet(operator string) *SuperSet {
return &SuperSet{
subsets: []Uniq{},
operator: operator,
}
}

func (u *SuperSet) Add(elements []string) {
subset := Uniq{}
subset.Add(elements)
u.subsets = append(u.subsets, subset)
}

func (u *SuperSet) Values() []string {
if len(u.subsets) == 0 {
return []string{}
}
subset := u.subsets[0]
for i := 1; i < len(u.subsets); i++ {
if strings.EqualFold(u.operator, "and") {
subset = subset.Intersect(u.subsets[i])
} else {
subset = subset.Union(u.subsets[i])
}
}
return subset.Values()
}

// Arr is a collection of elements.
type Arr []string

func (a *Arr) Add(elements []string) {
*a = append(*a, elements...)
}
func (a Arr) Result() []string {
func (a Arr) Values() []string {
return a
}
145 changes: 145 additions & 0 deletions pkg/api/index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//
// Copyright 2021 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.

package api

import (
"testing"
)

func Test_Collection(t *testing.T) {

vals := []string{"foo", "bar", "baz", "baz", "baz"}

t.Run("Unique", func(t *testing.T) {
unq := NewUniq()
unq.Add(vals)

if len(unq.Values()) != 3 {
t.Errorf("expected 3 unique values, got %d", len(unq.Values()))
}
expected := []string{"foo", "bar", "baz"}
for i, v := range unq.Values() {
if v != expected[i] {
t.Errorf("expected %s, got %s", expected[i], v)
}
}
})

t.Run("Array", func(t *testing.T) {
arr := new(Arr)
arr.Add(vals)

if len(arr.Values()) != 5 {
t.Errorf("expected 5 values, got %d", len(arr.Values()))
}
expected := []string{"foo", "bar", "baz", "baz", "baz"}
for i, v := range arr.Values() {
if v != expected[i] {
t.Errorf("expected %s, got %s", expected[i], v)
}
}
})

t.Run("Superset", func(t *testing.T) {

uniq1 := []string{"foo", "bar", "baz"}
uniq2 := []string{"foo", "bar", "baz"}
uniq3 := []string{"corge", "grault", "garply", "foo"}

t.Run("with 'and' operator", func(t *testing.T) {
set := NewSuperSet("and")
set.Add(uniq1)
set.Add(uniq2)
set.Add(uniq3)

if len(set.Values()) != 1 {
t.Errorf("expected 1 value, got %d", len(set.Values()))
}
expected := []string{"foo", "bar", "baz", "baz", "baz"}
for i, v := range set.Values() {
if v != expected[i] {
t.Errorf("expected %s, got %s", expected[i], v)
}
}
})
t.Run("with 'and' operator", func(t *testing.T) {
uuidsA := []string{"0df6e4ab06182f05a13e0146305074cff36c63437d5bb53bf46ece15d74edf02043d20ece23babc6",
"0df6e4ab06182f05016470086c6497c10e835dd0589d6a350b14d963dd14c2dfd76fac132553142a",
"0df6e4ab06182f05d70fcbedeae7ddf9385133939e033711425bf5a8ef61d2eace49b53d63ce273f",
"0df6e4ab06182f050c5b1129c133e323413bb9785404d9ceebcb5bb7c03667627509eb8b20ea6b29",
"0df6e4ab06182f0585dbed3baa2fab630730300b12f485978eb1021f16313b48f40b3adde89e0c74",
"0df6e4ab06182f05773cde2d6ea38f4d806595a13f0375f66bd7c16ca7be94a43440d7348a391512",
"0df6e4ab06182f050a6d1d200d74624a81a778ae57aa4f283d3b4f10ca0e847fb6f5b7032596e89a",
"0df6e4ab06182f055d1b38d6db5dd411a54aa802d585d045797d021c2815724a818794f46059e653",
"0df6e4ab06182f057743b9e8231dbb685f87c53609f6edbf2d8d5c52eb28e69547a7c13cff48c495",
"0df6e4ab06182f05bb4db598441629000805d0bec4e5ecfc90e4ef4026662792a8e1eacade484f51",
"0df6e4ab06182f05c9f5e15c421c728f3ebfc3480a338ab7736446e4a6501217c68c06806b7c27b5",
"0df6e4ab06182f054da7daf014969f8e0d1e81fe571e3d1ca540a5aa130d9a150d3936e4534cf66a",
"1bfef9e85f0f91c4e8d834029f61c9b5734c922bec60786e470aa41b1951cbb7de4d7ac6803aede4",
"1bfef9e85f0f91c4a5e0a5f9d7bdcfe2e85d9963dc4128e08a4aa155b82c44af83bf02967d24f6a3",
"657389de1f073e38edade3f2ba09480216eff6eb44e77a00a32a5f75e638469de7daa085f1638d8c",
"657389de1f073e389fb364bd8662cf8bd39757118a05449f24d45b138cda577625a671f9fc8fb3e1",
"657389de1f073e3883d831825aeb3a385e6ef97171df1070c9e8aec77c1ebf9fb319e9a87d0e4574",
"657389de1f073e389e677665baf5e2698299e451cadcd68e948a14c474033f723c0e76eaa9ee6aa8",
"657389de1f073e3841454e084bb56b250f17f476bee1070b97dc3372209683101c008c47e394bf56",
"657389de1f073e38fb9c7e751d230400f700ad63929346ad9fcbcb478daccbd9b0b74004e59ace55",
"657389de1f073e38aad111ace026e5c64915ff288ff01a23ebc87f0d8cfa8efdc50cb092f5ec55b0",
"657389de1f073e38fa581f2197a0463a6351e4c9372d7827a7f480056b74deda151b847ad484a8f0",
"2d7f0df4ed912a469b8ff6f4bc8a41bc2508060ccfc7a5b9c30a296eb4395b268495a3678badc903",
"2d7f0df4ed912a4603b82fb46b32565cefe7d60a6b145d5d0bc58312e05ef70315c6de1beec12de8",
"62cfc0a8491065b096b4782db55bc13d28c2cae9811e401c638df411b7835a9f1b58d716c2bd1d8b",
"62cfc0a8491065b0aa62af2751d29c31aeefe83f67635ee648cfb23062bdd8180c7e8581f21df19d",
"1b0c3160051d38eca51939fbd76c2df7ef076a4443d328bdd818842f81857c3098c4f3992cab4dea",
"1b0c3160051d38ec5dcec1eb4d289d1ef2b07430fb1b6a9ed88968cbbbf08110f9ff8e2752d8bc9b",
"5e948ae2d19e988b08b0eb3d10835e7970a373eeefe704a457c8698a2bf8a110901fb88a66617c14",
"5e948ae2d19e988bab42857f50ba5b211fd431df41093ed928345d2b032dabaff603da5797deb721",
"0bd5877c2445882c1edc3490e24a6a52a4e79cd849945aef2f7235c545f231499d1e607c93c10df8",
"0bd5877c2445882c72cecafd3b0a69f5b23aedec8c54bf3fecfaca3b7446791e7abf15dd0bfc11f0",
"08f62aeece01b20d53ba119d1102fe516ca447723d03823fd6ae806ddb12f400e38634a0ee060459",
"08f62aeece01b20d9736bdf9ffc5227d0875dc8e2e278dd78790daf726d2e4bc4ac732d46b33c775",
"12e608407df18f3133bb6d7aead4caeb57fbefb6b7776d7409c8c37a9a754c3d818db055495c6d25",
"12e608407df18f31ac39d2f2e73b2d9682ed608db7cd428f386b4938cdb05d93c588f10f1bf9178a"}

uuidsB := []string{
"0df6e4ab06182f05a13e0146305074cff36c63437d5bb53bf46ece15d74edf02043d20ece23babc6",
"0df6e4ab06182f057f95e1d163c5713eb1315edb1560e773b656dd58d9cbd4eccc64c7c65db397d8"}

set := NewSuperSet("and")
set.Add(uuidsA)
set.Add(uuidsB)

if len(set.Values()) != 1 {
t.Errorf("expected 1 value, got %d", len(set.Values()))
}

if set.Values()[0] != "0df6e4ab06182f05a13e0146305074cff36c63437d5bb53bf46ece15d74edf02043d20ece23babc6" {
t.Errorf("expected %s, got %s", uuidsB[1], set.Values()[0])
}
})
t.Run("with 'or' operator", func(t *testing.T) {
set := NewSuperSet("or")
set.Add(uniq1)
set.Add(uniq2)
set.Add(uniq3)

if len(set.Values()) != 6 {
t.Errorf("expected 6 values, got %d", len(set.Values()))
}

})
})

}
84 changes: 65 additions & 19 deletions tests/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"os"
"os/exec"
"path/filepath"
"reflect"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -311,14 +312,76 @@ func TestMinisign(t *testing.T) {
"--public-key", pubPath, "--pki-format", "minisign")
outputContains(t, out, "Created entry at")

uuid := getUUIDFromUploadOutput(t, out)
uuidA := getUUIDFromUploadOutput(t, out)

out = runCli(t, "verify", "--artifact", artifactPath, "--signature", sigPath,
"--public-key", pubPath, "--pki-format", "minisign")
outputContains(t, out, "Inclusion Proof")

out = runCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign")
outputContains(t, out, uuid)
outputContains(t, out, uuidA)

// crease a second artifact and sign it
artifactPath_B := filepath.Join(t.TempDir(), "artifact2")
createArtifact(t, artifactPath_B)
out = run(t, "\n", "minisign", "-S", "-s", keyPath, "-m", artifactPath_B, "-x", sigPath)
// Now upload to the log!
out = runCli(t, "upload", "--artifact", artifactPath_B, "--signature", sigPath,
"--public-key", pubPath, "--pki-format", "minisign")
outputContains(t, out, "Created entry at")
uuidB := getUUIDFromUploadOutput(t, out)

tests := []struct {
name string
expectedUuidACount int
expectedUuidBCount int
artifact string
operator string
}{
{
name: "artifact A AND signature should return artifact A",
expectedUuidACount: 1,
expectedUuidBCount: 0,
artifact: artifactPath,
operator: "and",
},
{
name: "artifact A OR signature should return artifact A and B",
expectedUuidACount: 1,
expectedUuidBCount: 1,
artifact: artifactPath,
operator: "or",
},
{
name: "artifact B AND signature should return artifact B",
expectedUuidACount: 0,
expectedUuidBCount: 1,
artifact: artifactPath_B,
operator: "and",
},
{
name: "artifact B OR signature should return artifact A and B",
expectedUuidACount: 1,
expectedUuidBCount: 1,
artifact: artifactPath_B,
operator: "or",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
out = runCli(t, "search", "--public-key", pubPath, "--pki-format", "minisign",
"--operator", test.operator, "--artifact", test.artifact)

expected := map[string]int{uuidA: test.expectedUuidACount, uuidB: test.expectedUuidBCount}
actual := map[string]int{
uuidA: strings.Count(out, uuidA),
uuidB: strings.Count(out, uuidB),
}
if !reflect.DeepEqual(expected, actual) {
t.Errorf("expected to find %v, found %v", expected, actual)
}
})
}
}

func TestSSH(t *testing.T) {
Expand Down Expand Up @@ -567,23 +630,6 @@ func TestX509(t *testing.T) {
out = runCli(t, "search", "--email", "test@rekor.dev")
outputContains(t, out, uuid)

artifactBytes, err := ioutil.ReadFile(artifactPath)
if err != nil {
t.Error(err)
}
sha := sha256.Sum256(artifactBytes)
dataSHA := hex.EncodeToString(sha[:])

out = runCli(t, "search", "--email", "test@rekor.dev", "--operator", "and","--sha", dataSHA)
if strings.Count(out, uuid) != 1 {
t.Errorf("expected to find one match for %v, found %v", uuid, strings.Count(out, uuid))
}

out = runCli(t, "search", "--email", "test@rekor.dev", "--operator", "or","--sha", dataSHA)
if strings.Count(out, uuid) != 2 {
t.Errorf("expected to find two matches for %v, found %v", uuid, strings.Count(out, uuid))
}

}

func TestUploadNoAPIKeyInOutput(t *testing.T) {
Expand Down

0 comments on commit d99c7c3

Please sign in to comment.