1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package main
19
20 import (
21 "bufio"
22 "bytes"
23 "crypto/sha256"
24 "encoding/base64"
25 "encoding/hex"
26 "encoding/json"
27 "fmt"
28 "io"
29 "io/ioutil"
30 "math/rand"
31 "net/http"
32 "os"
33 "path/filepath"
34 "regexp"
35 "strconv"
36 "strings"
37 "testing"
38
39 "github.com/sigstore/rekor/pkg/sharding"
40
41 "github.com/sigstore/rekor/pkg/util"
42 )
43
44 func TestDuplicates(t *testing.T) {
45 artifactPath := filepath.Join(t.TempDir(), "artifact")
46 sigPath := filepath.Join(t.TempDir(), "signature.asc")
47
48 util.CreatedPGPSignedArtifact(t, artifactPath, sigPath)
49
50
51 pubPath := filepath.Join(t.TempDir(), "pubKey.asc")
52 if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil {
53 t.Fatal(err)
54 }
55
56
57 out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath)
58 util.OutputContains(t, out, "Created entry at")
59
60
61 out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath)
62 util.OutputContains(t, out, "Entry already exists")
63
64
65 util.CreatedPGPSignedArtifact(t, artifactPath, sigPath)
66 out = util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath)
67 util.OutputContains(t, out, "Created entry at")
68 }
69
70
71
72
73
74 func TestMetricsCounts(t *testing.T) {
75 latencyMetric := "rekor_latency_by_api_count{method=\"GET\",path=\"/api/v1/log\"}"
76 qpsMetric := "rekor_qps_by_api{code=\"200\",method=\"GET\",path=\"/api/v1/log\"}"
77
78 latencyCount, err := getRekorMetricCount(latencyMetric, t)
79 if err != nil {
80 t.Fatal(err)
81 }
82
83 qpsCount, err := getRekorMetricCount(qpsMetric, t)
84 if err != nil {
85 t.Fatal(err)
86 }
87
88 resp, err := http.Get("http://localhost:3000/api/v1/log")
89 if err != nil {
90 t.Fatal(err)
91 }
92 resp.Body.Close()
93
94 latencyCount2, err := getRekorMetricCount(latencyMetric, t)
95 if err != nil {
96 t.Fatal(err)
97 }
98
99 qpsCount2, err := getRekorMetricCount(qpsMetric, t)
100 if err != nil {
101 t.Fatal(err)
102 }
103
104 if latencyCount2-latencyCount != 1 {
105 t.Error("rekor_latency_by_api_count did not increment")
106 }
107
108 if qpsCount2-qpsCount != 1 {
109 t.Error("rekor_qps_by_api did not increment")
110 }
111 }
112 func getRekorMetricCount(metricLine string, t *testing.T) (int, error) {
113 t.Helper()
114 re, err := regexp.Compile(fmt.Sprintf("^%s\\s*([0-9]+)$", regexp.QuoteMeta(metricLine)))
115 if err != nil {
116 return 0, err
117 }
118
119 resp, err := http.Get("http://localhost:2112/metrics")
120 if err != nil {
121 return 0, err
122 }
123 defer resp.Body.Close()
124
125 scanner := bufio.NewScanner(resp.Body)
126 for scanner.Scan() {
127 match := re.FindStringSubmatch(scanner.Text())
128 if len(match) != 2 {
129 continue
130 }
131
132 result, err := strconv.Atoi(match[1])
133 if err != nil {
134 return 0, err
135 }
136 t.Log("Matched metric line: " + scanner.Text())
137 return result, nil
138 }
139 return 0, nil
140 }
141 func TestEnvVariableValidation(t *testing.T) {
142 os.Setenv("REKOR_FORMAT", "bogus")
143 defer os.Unsetenv("REKOR_FORMAT")
144
145 util.RunCliErr(t, "loginfo")
146 }
147 func TestGetCLI(t *testing.T) {
148
149 artifactPath := filepath.Join(t.TempDir(), "artifact")
150 sigPath := filepath.Join(t.TempDir(), "signature.asc")
151 t.Cleanup(func() {
152 os.Remove(artifactPath)
153 os.Remove(sigPath)
154 })
155 util.CreatedPGPSignedArtifact(t, artifactPath, sigPath)
156
157
158 pubPath := filepath.Join(t.TempDir(), "pubKey.asc")
159 if err := ioutil.WriteFile(pubPath, []byte(util.PubKey), 0644); err != nil {
160 t.Error(err)
161 }
162 t.Cleanup(func() {
163 os.Remove(pubPath)
164 })
165 out := util.RunCli(t, "upload", "--artifact", artifactPath, "--signature", sigPath, "--public-key", pubPath)
166 util.OutputContains(t, out, "Created entry at")
167
168 uuid, err := sharding.GetUUIDFromIDString(util.GetUUIDFromUploadOutput(t, out))
169 if err != nil {
170 t.Error(err)
171 }
172
173
174 util.RunCli(t, "get", "--log-index", "0")
175
176 out = util.RunCli(t, "get", "--format=json", "--uuid", uuid)
177
178
179 g := util.GetOut{}
180 if err := json.Unmarshal([]byte(out), &g); err != nil {
181 t.Error(err)
182 }
183
184 if g.IntegratedTime == 0 {
185 t.Errorf("Expected IntegratedTime to be set. Got %s", out)
186 }
187
188 util.RunCli(t, "get", "--format=json", "--log-index", strconv.Itoa(g.LogIndex))
189
190
191 out = util.RunCli(t, "search", "--artifact", artifactPath)
192 util.OutputContains(t, out, uuid)
193
194 out = util.RunCli(t, "search", "--public-key", pubPath)
195 util.OutputContains(t, out, uuid)
196
197 artifactBytes, err := ioutil.ReadFile(artifactPath)
198 if err != nil {
199 t.Error(err)
200 }
201 sha := sha256.Sum256(artifactBytes)
202
203 out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha256:%s", hex.EncodeToString(sha[:])))
204 util.OutputContains(t, out, uuid)
205
206
207 tid := getTreeID(t)
208 entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid), uuid)
209 if err != nil {
210 t.Error(err)
211 }
212 out = util.RunCli(t, "get", "--format=json", "--uuid", entryID.ReturnEntryIDString())
213 }
214 func getTreeID(t *testing.T) int64 {
215 t.Helper()
216 out := util.RunCli(t, "loginfo")
217 tidStr := strings.TrimSpace(strings.Split(out, "TreeID: ")[1])
218 tid, err := strconv.ParseInt(tidStr, 10, 64)
219 if err != nil {
220 t.Errorf(err.Error())
221 }
222 t.Log("Tree ID:", tid)
223 return tid
224 }
225 func TestSearchNoEntriesRC1(t *testing.T) {
226 util.RunCliErr(t, "search", "--email", "noone@internetz.com")
227 }
228 func TestHostnameInSTH(t *testing.T) {
229
230 rekorContainerID := strings.Trim(util.Run(t, "", "docker", "ps", "-q", "-f", "name=rekor-server"), "\n")
231 resp, err := http.Get(fmt.Sprintf("%s/api/v1/log", rekorServer()))
232 if err != nil {
233 t.Fatal(err)
234 }
235 defer resp.Body.Close()
236
237 body, err := ioutil.ReadAll(resp.Body)
238 if err != nil {
239 t.Fatal(err)
240 }
241
242 if !strings.Contains(string(body), fmt.Sprintf(" %s ", rekorContainerID)) {
243 t.Errorf("logInfo does not contain the hostname (%v) of the rekor-server container: %v", rekorContainerID, string(body))
244 }
245 if strings.Contains(string(body), "rekor.sigstore.dev") {
246 t.Errorf("logInfo contains rekor.sigstore.dev which should not be set by default")
247 }
248 }
249 func rekorServer() string {
250 if s := os.Getenv("REKOR_SERVER"); s != "" {
251 return s
252 }
253 return "http://localhost:3000"
254 }
255 func TestSearchSHA512(t *testing.T) {
256 sha512 := "c7694a1112ea1404a3c5852bdda04c2cc224b3567ef6ceb8204dbf2b382daacfc6837ee2ed9d5b82c90b880a3c7289778dbd5a8c2c08193459bcf7bd44581ed0"
257 var out string
258 out = util.RunCli(t, "upload", "--type", "intoto:0.0.2",
259 "--artifact", "tests/envelope.sha512",
260 "--pki-format", "x509",
261 "--public-key", "tests/test_sha512.pub")
262 util.OutputContains(t, out, "Created entry at")
263 uuid := util.GetUUIDFromTimestampOutput(t, out)
264 out = util.RunCli(t, "search", "--sha", fmt.Sprintf("sha512:%s", sha512))
265 util.OutputContains(t, out, uuid)
266 }
267 func TestVerifyNonExistentUUID(t *testing.T) {
268
269 out := util.RunCliErr(t, "verify", "--uuid", "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
270 util.OutputContains(t, out, "entry in log cannot be located")
271
272
273 tid := getTreeID(t)
274 h := sha256.Sum256([]byte("123"))
275 entryID, err := sharding.CreateEntryIDFromParts(fmt.Sprintf("%x", tid),
276 hex.EncodeToString(h[:]))
277 if err != nil {
278 t.Fatal(err)
279 }
280 body := fmt.Sprintf("{\"entryUUIDs\":[\"%s\"]}", entryID.ReturnEntryIDString())
281 resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()),
282 "application/json",
283 bytes.NewReader([]byte(body)))
284 if err != nil {
285 t.Fatal(err)
286 }
287 c, _ := ioutil.ReadAll(resp.Body)
288 if resp.StatusCode != 200 {
289 t.Fatalf("expected status 200, got %d instead", resp.StatusCode)
290 }
291 if strings.TrimSpace(string(c)) != "[]" {
292 t.Fatalf("expected empty JSON array as response, got %s instead", string(c))
293 }
294 }
295 func TestSearchQueryLimit(t *testing.T) {
296 tests := []struct {
297 description string
298 limit int
299 shouldErr bool
300 }{
301 {
302 description: "request 6 entries",
303 limit: 6,
304 }, {
305 description: "request 10 entries",
306 limit: 10,
307 }, {
308 description: "request more than max",
309 limit: 12,
310 shouldErr: true,
311 },
312 }
313
314 for _, test := range tests {
315 t.Run(test.description, func(t *testing.T) {
316 b := bytes.NewReader(getBody(t, test.limit))
317 resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), "application/json", b)
318 if err != nil {
319 t.Fatal(err)
320 }
321 c, _ := ioutil.ReadAll(resp.Body)
322 t.Log(string(c))
323 if resp.StatusCode != 200 && !test.shouldErr {
324 t.Fatalf("expected test to pass but it failed")
325 }
326 if resp.StatusCode != 422 && test.shouldErr {
327 t.Fatal("expected test to fail but it passed")
328 }
329 if test.shouldErr && !strings.Contains(string(c), "logIndexes in body should have at most 10 items") {
330 t.Fatal("expected max limit error but didn't get it")
331 }
332 })
333 }
334 }
335 func getBody(t *testing.T, limit int) []byte {
336 t.Helper()
337 s := fmt.Sprintf("{\"logIndexes\": [%d", limit)
338 for i := 1; i < limit; i++ {
339 s = fmt.Sprintf("%s, %d", s, i)
340 }
341 s += "]}"
342 return []byte(s)
343 }
344 func TestSearchQueryMalformedEntry(t *testing.T) {
345 wd, err := os.Getwd()
346 if err != nil {
347 t.Fatal(err)
348 }
349 b, err := ioutil.ReadFile(filepath.Join(wd, "tests/rekor.json"))
350 if err != nil {
351 t.Fatal(err)
352 }
353 body := fmt.Sprintf("{\"entries\":[\"%s\"]}", b)
354 resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()),
355 "application/json",
356 bytes.NewBuffer([]byte(body)))
357 if err != nil {
358 t.Fatal(err)
359 }
360 if resp.StatusCode != 400 {
361 t.Fatalf("expected status 400, got %d instead", resp.StatusCode)
362 }
363 }
364
365 func TestHTTPMaxRequestBodySize(t *testing.T) {
366
367 pipeR, pipeW := io.Pipe()
368 go func() {
369 _, _ = io.CopyN(base64.NewEncoder(base64.StdEncoding, pipeW), rand.New(rand.NewSource(123)), 33*1024768)
370 pipeW.Close()
371 }()
372
373 bodyReader := io.MultiReader(strings.NewReader("{ \"key\": \""), pipeR, strings.NewReader("\"}"))
374 resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()),
375 "application/json",
376 bodyReader)
377 if err != nil {
378 t.Fatal(err)
379 }
380 if resp.StatusCode != http.StatusRequestEntityTooLarge {
381 t.Fatalf("expected status %d, got %d instead", http.StatusRequestEntityTooLarge, resp.StatusCode)
382 }
383 }
384
View as plain text