carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3import (
4 "context"
5 "net/http"
6 "strings"
7 "testing"
8 "time"
9
10 "github.com/carlosframework/platform/internal/objstore"
11 "github.com/carlosframework/platform/internal/release"
12)
13
14const systemAntivirusURL = "/api/cli/system/antivirus"
15
16// systemArmed is a console with an engine wired and an operator who may act.
17func systemArmed(t *testing.T, code string) (*consoleFixture, string) {
18 t.Helper()
19 fx, _, token, _ := storeArmed(t, code)
20 fx.srv.cfg.StoreScanner = objstore.NewMemScanner()
21 return fx, token
22}
23
24// Authority here is an ADDRESS, not a role. Admin reaches almost everything;
25// enabling deployment-wide scanning starts reading tenant bytes across every
26// store, and that is the deployment owner's call.
27func TestSystemAntivirusNeedsOperator(t *testing.T) {
28 fx, token := systemArmed(t, "code-sys-operator")
29 fx.srv.cfg.StoreOperators = []string{"someone-else@keymail.dev"}
30
31 status, out := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": true}, token)
32 if status != http.StatusForbidden {
33 t.Fatalf("enable as a non-operator = %d %v, want 403", status, out)
34 }
35 if msg := str(t, out, "error"); !strings.Contains(msg, "CARLOS_STORE_OPERATORS") {
36 t.Fatalf("error = %q, want the operator list named", msg)
37 }
38
39 status, out = getJSON(t, fx.ts.URL+systemAntivirusURL, token)
40 if status != http.StatusForbidden {
41 t.Fatalf("status as a non-operator = %d %v, want 403", status, out)
42 }
43}
44
45// Enabling with no engine wired would record a control that cannot run, and
46// `scan status` would then report health for a deployment where no sweep can
47// happen — the class of lie this whole feature is careful about.
48func TestSystemAntivirusEnableRefusesWithoutScanner(t *testing.T) {
49 fx, token := systemArmed(t, "code-sys-nowire")
50 fx.srv.cfg.StoreScanner = nil
51
52 status, out := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": true}, token)
53 if status != http.StatusServiceUnavailable {
54 t.Fatalf("enable with no engine = %d %v, want 503", status, out)
55 }
56 if msg := str(t, out, "error"); !strings.Contains(msg, "CARLOS_CLAMD_ADDR") {
57 t.Fatalf("error = %q, want the wiring variable named", msg)
58 }
59 if av, _, err := release.GetAntivirus(context.Background(), fx.rel); err != nil || av != nil {
60 t.Fatalf("a refused enable still wrote a record: %+v, %v", av, err)
61 }
62
63 // Disabling is always allowed, engine or not: it is the way out of a bad
64 // state, and a door that refused would strand whoever needed it.
65 if s, o := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": false}, token); s != http.StatusOK {
66 t.Fatalf("disable with no engine = %d %v, want 200", s, o)
67 }
68}
69
70// Wiring and policy are reported as separate facts, because they fail
71// separately. Collapsing them into one "healthy" is how an operator ends up
72// debugging the wrong half.
73func TestSystemAntivirusStatusSeparatesWiringFromPolicy(t *testing.T) {
74 fx, token := systemArmed(t, "code-sys-status")
75
76 // Wired, no record: not enabled, and the reply says which is which.
77 status, out := getJSON(t, fx.ts.URL+systemAntivirusURL, token)
78 if status != http.StatusOK {
79 t.Fatalf("status = %d %v", status, out)
80 }
81 if wired, _ := out["wired"].(bool); !wired {
82 t.Fatalf("wired = %v, want true", out["wired"])
83 }
84 if enabled, _ := out["enabled"].(bool); enabled {
85 t.Fatalf("enabled = %v with no record, want false", out["enabled"])
86 }
87 if str(t, out, "signature") == "" {
88 t.Fatal("a wired engine reported no signature database")
89 }
90
91 // Enabled, engine gone: still enabled, but the engine error is visible.
92 if s, o := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": true}, token); s != http.StatusOK {
93 t.Fatalf("enable = %d %v", s, o)
94 }
95 fx.srv.cfg.StoreScanner = nil
96 _, out = getJSON(t, fx.ts.URL+systemAntivirusURL, token)
97 if enabled, _ := out["enabled"].(bool); !enabled {
98 t.Fatal("unwiring the engine changed the recorded policy")
99 }
100 if wired, _ := out["wired"].(bool); wired {
101 t.Fatal("wired stayed true with no scanner")
102 }
103}
104
105func TestSystemAntivirusStatusReportsQueueAppsWithWork(t *testing.T) {
106 fx, token := systemArmed(t, "code-sys-queue-depth")
107 fx.srv.scanQueue.Bump(objstore.AppRef{Account: fixtureAcct, App: fixtureApp})
108 fx.srv.scanQueue.Bump(objstore.AppRef{Account: fixtureAcct, App: "second"})
109
110 status, out := getJSON(t, fx.ts.URL+systemAntivirusURL, token)
111 if status != http.StatusOK {
112 t.Fatalf("status = %d %v", status, out)
113 }
114 if got := int(out["queue_apps_with_work"].(float64)); got != 2 {
115 t.Fatalf("queue_apps_with_work = %d, want 2", got)
116 }
117}
118
119// The record is the audit trail: who turned it on, and when.
120func TestSystemAntivirusRecordsWhoAndWhen(t *testing.T) {
121 fx, token := systemArmed(t, "code-sys-audit")
122 before := time.Now().Add(-time.Second)
123
124 if s, o := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": true}, token); s != http.StatusOK {
125 t.Fatalf("enable = %d %v", s, o)
126 }
127 av, _, err := release.GetAntivirus(context.Background(), fx.rel)
128 if err != nil || av == nil {
129 t.Fatalf("record = %+v, %v", av, err)
130 }
131 if av.By != "alice@keymail.dev" {
132 t.Fatalf("by = %q, want the operator who did it", av.By)
133 }
134 if av.At.Before(before) {
135 t.Fatalf("at = %v, not stamped", av.At)
136 }
137}
138
139// A body that says neither is a 400, not a guess. There is no safe default
140// for a switch that decides whether tenant bytes get read.
141func TestSystemAntivirusNeedsAnExplicitState(t *testing.T) {
142 fx, token := systemArmed(t, "code-sys-explicit")
143
144 status, out := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{}, token)
145 if status != http.StatusBadRequest {
146 t.Fatalf("empty body = %d %v, want 400", status, out)
147 }
148}
149
150// A record this build cannot parse already stops every sweep — so `disable`
151// refusing over it strands the operator in a state no door can leave. The one
152// verb whose whole job is "stop" must not refuse for the reason it is needed,
153// which is the same argument apiscan.go makes for revoke and enforce-off.
154//
155// `enable` still refuses: repairing by overwriting is a way OUT of a bad
156// state, never a way into an enabled one.
157func TestSystemAntivirusDisableRepairsAnUnreadableRecord(t *testing.T) {
158 ctx := context.Background()
159 fx, token := systemArmed(t, "code-sys-repair")
160
161 if _, err := fx.rel.Put(ctx, release.SystemKey(release.AntivirusName),
162 []byte(`{"schema":99,"enabled":true}`), ""); err != nil {
163 t.Fatal(err)
164 }
165
166 if s, o := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": true}, token); s != http.StatusBadGateway {
167 t.Fatalf("enable over an unreadable record = %d %v, want 502 — enabling must never guess", s, o)
168 }
169 if s, o := postJSON(t, fx.ts.URL+systemAntivirusURL, map[string]any{"enabled": false}, token); s != http.StatusOK {
170 t.Fatalf("disable over an unreadable record = %d %v, want 200", s, o)
171 }
172 av, _, err := release.GetAntivirus(ctx, fx.rel)
173 if err != nil || av == nil || av.Enabled {
174 t.Fatalf("record after the repair = %+v, %v", av, err)
175 }
176 if av.By != "alice@keymail.dev" {
177 t.Fatalf("by = %q, want the operator who repaired it", av.By)
178 }
179}
180