| 1 | package console |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "net/http" |
| 6 | "net/url" |
| 7 | "testing" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/objstore" |
| 10 | ) |
| 11 | |
| 12 | func TestRevivalPollingWithoutCallbackKeys(t *testing.T) { |
| 13 | fx, _, member, _ := storeArmed(t, "revival-poll-only") |
| 14 | grantStore(t, fx, member) |
| 15 | requestScanning(t, fx, member, false) |
| 16 | scanner := objstore.NewMemScanner() |
| 17 | fx.srv.cfg.StoreScanner = scanner |
| 18 | fx.srv.cfg.CallbackSigner = nil |
| 19 | fx.srv.cfg.SecretsPub = "" |
| 20 | enableDeploymentScanning(t, fx) |
| 21 | if status, out := postJSON(t, scanURL(fx, "titogo")+"/grant", map[string]any{}, member); status != http.StatusOK { |
| 22 | t.Fatalf("grant without callback or sealing keys = %d %v", status, out) |
| 23 | } |
| 24 | rec := storedRecord(t, fx, "titogo") |
| 25 | if rec.Scanning.WebhookGen != 0 || rec.Scanning.WebhookKeyID != "" { |
| 26 | t.Fatalf("polling-only grant created callback state: %+v", rec.Scanning) |
| 27 | } |
| 28 | if _, exists := appVars(t, fx, "titogo")[objstore.DefaultEnvPrefix+"_SCAN_CALLBACK_SECRET"]; exists { |
| 29 | t.Fatal("polling-only grant delivered a callback secret") |
| 30 | } |
| 31 | |
| 32 | token := appToken(t, fx, "poll-only") |
| 33 | key := " uploads/report.pdf " |
| 34 | bucket := rec.Provisioned.Bucket |
| 35 | seedObject(scanner, bucket, key, "infected-upload") |
| 36 | scanner.ScanAs(key, objstore.VerdictInfected) |
| 37 | other := "uploads/report.pdf" |
| 38 | version := seedObject(scanner, bucket, other, "clean-other-upload") |
| 39 | scanner.Put(bucket, other, version, objstore.VerdictClean) |
| 40 | if status, out := postJSON(t, enqueueURL(fx, "titogo"), map[string]any{"key": key}, token); status != http.StatusAccepted { |
| 41 | t.Fatalf("enqueue = %d %v", status, out) |
| 42 | } |
| 43 | if status, out := getStatus(t, fx, token, "key="+url.QueryEscape(key)); status != http.StatusOK || out["state"] != "pending" { |
| 44 | t.Fatalf("before drain = %d %v, want pending for the exact key", status, out) |
| 45 | } |
| 46 | if err := fx.srv.scanDrainTick(context.Background()); err != nil { |
| 47 | t.Fatal(err) |
| 48 | } |
| 49 | if status, out := getStatus(t, fx, token, "key="+url.QueryEscape(key)); status != http.StatusOK || out["state"] != "infected" { |
| 50 | t.Fatalf("after drain = %d %v, want infected for the exact key", status, out) |
| 51 | } |
| 52 | if scanner.Scans(key) != 1 || scanner.Scans(other) != 0 { |
| 53 | t.Fatalf("wrong objects scanned: requested=%d other=%d", scanner.Scans(key), scanner.Scans(other)) |
| 54 | } |
| 55 | if n := queueLen(t, fx, fx.sqid, "titogo"); n != 0 { |
| 56 | t.Fatalf("queue after drain = %d", n) |
| 57 | } |
| 58 | if status, out := postJSON(t, enqueueURL(fx, "titogo"), map[string]any{"key": key, "callback_path": "/hooks/scan"}, token); status != http.StatusServiceUnavailable || out["reason"] != "callbacks_not_configured" { |
| 59 | t.Fatalf("callback without signer = %d %v", status, out) |
| 60 | } |
| 61 | } |
| 62 | |