carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "bytes"
5 "encoding/json"
6 "errors"
7 "fmt"
8 "github.com/carlosframework/platform/internal/rootstate"
9 "os"
10 "path/filepath"
11 "strings"
12 "testing"
13 "time"
14)
15
16func rootHistoryFixture(t *testing.T) (*RootHistory, *healthyRetryStore, HistoryBinding) {
17 t.Helper()
18 s := &healthyRetryStore{dir: t.TempDir(), fault: errors.New("injected sync failure")}
19 b := HistoryBinding{Host: proofHost, Account: "bab", App: "console"}
20 h := &RootHistory{Store: s}
21 r := historyRecord{Schema: 1, Binding: b, Revision: 1, Programs: historyPrograms{Revision: 1, Complete: true, Values: []string{"old/program"}}, Refusals: historyRefusals{Revision: 1, Complete: true}}
22 if err := h.write(r); err != nil {
23 t.Fatal(err)
24 }
25 return h, s, b
26}
27
28func TestPlan14RootHistoryMissingIsUnknown(t *testing.T) {
29 h, s, b := rootHistoryFixture(t)
30 if err := os.Remove(filepath.Join(s.dir, historyKey(b))); err != nil {
31 t.Fatal(err)
32 }
33 if _, err := h.Programs(b); err == nil {
34 t.Fatal("missing program history granted fresh installation")
35 }
36 if _, err := h.Refusals(b); err == nil {
37 t.Fatal("missing refusal history granted permission")
38 }
39}
40
41func TestPlan14RootHistoryObservedProgramDoesNotInitializeRefusals(t *testing.T) {
42 h, s, b := rootHistoryFixture(t)
43 if err := os.Remove(filepath.Join(s.dir, historyKey(b))); err != nil {
44 t.Fatal(err)
45 }
46 if err := h.RecordInstalledProgram(b, "observed/program"); err != nil {
47 t.Fatal(err)
48 }
49 programs, err := h.Programs(b)
50 if err != nil || len(programs) != 1 || programs[0] != "observed/program" {
51 t.Fatalf("observed programs: %v, %v", programs, err)
52 }
53 if _, err := h.Refusals(b); err == nil {
54 t.Fatal("current service proved absence of historical failures")
55 }
56}
57
58func TestPlan14RootHistoryRejectsMalformed(t *testing.T) {
59 h, s, b := rootHistoryFixture(t)
60 good, err := s.Read(historyKey(b))
61 if err != nil {
62 t.Fatal(err)
63 }
64 for _, attack := range []string{"schema", "identity", "revision", "programs", "refusals", "trailing", "extra"} {
65 t.Run(attack, func(t *testing.T) {
66 var r historyRecord
67 if err := json.Unmarshal(good, &r); err != nil {
68 t.Fatal(err)
69 }
70 var raw []byte
71 switch attack {
72 case "schema":
73 r.Schema = 2
74 case "identity":
75 r.Binding.Account = "bap"
76 case "revision":
77 r.Refusals.Revision = r.Revision + 1
78 case "programs":
79 r.Programs.Complete = false
80 case "refusals":
81 r.Refusals.Complete = false
82 }
83 raw, _ = json.Marshal(r)
84 if attack == "trailing" {
85 raw = append(raw, []byte(" {}")...)
86 }
87 if attack == "extra" {
88 raw = append(raw[:len(raw)-1], []byte(`,"allow":true}`)...)
89 }
90 if err := s.Write(historyKey(b), raw); err != nil {
91 t.Fatal(err)
92 }
93 if attack != "refusals" {
94 if _, err := h.Programs(b); err == nil {
95 t.Fatal("accepted unavailable program history")
96 }
97 }
98 if attack != "programs" {
99 if _, err := h.Refusals(b); err == nil {
100 t.Fatal("accepted unavailable refusal history")
101 }
102 }
103 })
104 }
105}
106
107func TestPlan14RootHistoryLegacyTampering(t *testing.T) {
108 for _, attack := range []string{"delete", "rename", "forge"} {
109 t.Run(attack, func(t *testing.T) {
110 h, _, b := rootHistoryFixture(t)
111 legacy := t.TempDir()
112 at := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC)
113 r := Refusal{Schema: 1, Host: b.Host, App: b.App, Version: "new", PointerPromotedAt: at, RefusedAt: at, Reason: "failed"}
114 if err := WriteRefusal(legacy, r); err != nil {
115 t.Fatal(err)
116 }
117 if err := RecordProgram(legacy, b.Host, "foreign/program"); err != nil {
118 t.Fatal(err)
119 }
120 if err := h.AddRefusal(b, r); err != nil {
121 t.Fatal(err)
122 }
123 switch attack {
124 case "delete":
125 if err := os.Remove(filepath.Join(RefusalDir(legacy), b.Host+".json")); err != nil {
126 t.Fatal(err)
127 }
128 case "rename":
129 if err := os.Rename(legacy, legacy+"-old"); err != nil {
130 t.Fatal(err)
131 }
132 t.Cleanup(func() { os.RemoveAll(legacy + "-old") })
133 if err := os.Mkdir(legacy, 0700); err != nil {
134 t.Fatal(err)
135 }
136 case "forge":
137 r.Recovered = true
138 r.Version = "old"
139 if err := WriteRefusal(legacy, r); err != nil {
140 t.Fatal(err)
141 }
142 }
143 programs, err := h.Programs(b)
144 if err != nil || len(programs) != 1 || programs[0] != "old/program" {
145 t.Fatalf("program exception from legacy: %v %v", programs, err)
146 }
147 refs, err := h.Refusals(b)
148 if err != nil || len(refs) != 1 || refs[0].Version != "new" {
149 t.Fatalf("refusal lost: %v %v", refs, err)
150 }
151 })
152 }
153}
154
155func TestPlan14RootHistoryVisibleWriteNeedsSync(t *testing.T) {
156 h, s, b := rootHistoryFixture(t)
157 r := Refusal{Schema: 1, Host: b.Host, App: b.App, Version: "new", RefusedAt: time.Now(), Reason: "failed"}
158 h.Store = &historyFailNewStore{healthyRetryStore: s, remaining: 2}
159 if err := h.AddRefusal(b, r); !errors.Is(err, s.fault) {
160 t.Fatalf("write: %v", err)
161 }
162 if _, err := h.Refusals(b); !errors.Is(err, s.fault) {
163 t.Fatalf("visible unsynced state authorized read: %v", err)
164 }
165 refs, err := h.Refusals(b)
166 if err != nil || len(refs) != 1 {
167 t.Fatalf("durable retry: %v %v", refs, err)
168 }
169}
170
171func TestPlan14RootHistoryRefusalKinds(t *testing.T) {
172 for _, kind := range []string{RefusalNotUp, RefusalWrongProgram, RefusalNotProven} {
173 t.Run("kind="+kind, func(t *testing.T) {
174 h, _, b := rootHistoryFixture(t)
175 at := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC)
176 r := Refusal{Schema: 1, Kind: kind, Host: b.Host, App: b.App, Version: "new", PointerPromotedAt: at, RefusedAt: at, Reason: "failed"}
177 if err := h.AddRefusal(b, r); err != nil {
178 t.Fatal(err)
179 }
180 blocked, err := h.Blocks(b, &ChannelPointer{Version: "new", PromotedAt: at.Add(time.Second)})
181 if err != nil || blocked != (kind != RefusalNotUp) {
182 t.Fatalf("retry clears wrong kind: blocked=%v err=%v", blocked, err)
183 }
184 })
185 }
186}
187
188func TestPlan14RootHistoryAttemptSurvivesReopen(t *testing.T) {
189 h, s, b := rootHistoryFixture(t)
190 a := HistoryAttempt{ID: "attempt-1", Version: "new", Source: "record-etag", Started: time.Now().UTC()}
191 if err := h.Begin(b, a); err != nil {
192 t.Fatal(err)
193 }
194 h = &RootHistory{Store: s}
195 if err := h.Begin(b, HistoryAttempt{ID: "attempt-2", Version: "new", Source: "record-etag", Started: a.Started}); err == nil {
196 t.Fatal("unresolved attempt retried")
197 }
198 if err := h.Begin(b, a); err == nil {
199 t.Fatal("pending intent replay granted another forward attempt")
200 }
201 if _, err := h.Refusals(b); err == nil {
202 t.Fatal("pending attempt asserted a complete mint refusal list")
203 }
204 if err := h.Settle(b, "attempt-2", nil); err == nil {
205 t.Fatal("wrong attempt settled")
206 }
207 if err := h.Settle(b, a.ID, nil); err != nil {
208 t.Fatal(err)
209 }
210}
211
212type historyFailNewStore struct {
213 *healthyRetryStore
214 remaining int
215}
216
217func (s *historyFailNewStore) Write(name string, raw []byte) error {
218 var r historyRecord
219 if err := json.Unmarshal(raw, &r); err != nil {
220 return err
221 }
222 if len(r.Refusals.Values) > 0 && s.remaining > 0 {
223 s.fail = 1
224 s.remaining--
225 }
226 return s.healthyRetryStore.Write(name, raw)
227}
228
229func TestPlan14RootHistoryCategoryIsolation(t *testing.T) {
230 h, _, b := rootHistoryFixture(t)
231 r, err := h.read(b)
232 if err != nil {
233 t.Fatal(err)
234 }
235 r.Refusals.Complete = false
236 if err := h.write(r); err != nil {
237 t.Fatal(err)
238 }
239 if _, err := h.Programs(b); err != nil {
240 t.Fatalf("known programs lost: %v", err)
241 }
242 if _, err := h.Refusals(b); err == nil {
243 t.Fatal("unknown refusals allowed")
244 }
245 if err := h.Begin(b, HistoryAttempt{ID: "a", Version: "new", Source: "record", Started: time.Now()}); err == nil {
246 t.Fatal("incomplete category allowed an attempt")
247 }
248}
249
250func TestPlan14RootHistoryDenyImportUnknown(t *testing.T) {
251 h, s, b := rootHistoryFixture(t)
252 if err := os.Remove(filepath.Join(s.dir, historyKey(b))); err != nil {
253 t.Fatal(err)
254 }
255 r := Refusal{Host: b.Host, App: b.App, Version: "new", RefusedAt: time.Now(), Reason: "failed", Recovered: true}
256 if err := h.AddRefusal(b, r); err != nil {
257 t.Fatal(err)
258 }
259 saved, err := h.read(b)
260 if err != nil {
261 t.Fatal(err)
262 }
263 if saved.Programs.Complete || saved.Refusals.Complete || len(saved.Refusals.Values) != 1 {
264 t.Fatalf("import invented completeness: %+v", saved)
265 }
266 if _, err := h.Refusals(b); err == nil {
267 t.Fatal("legacy recovery asserted absence")
268 }
269}
270
271func TestPlan14RootHistoryOverflowQuarantines(t *testing.T) {
272 h, _, b := rootHistoryFixture(t)
273 for i := 0; i < historyLimit; i++ {
274 r := Refusal{Host: b.Host, App: b.App, Version: fmt.Sprint(i), RefusedAt: time.Now(), Reason: "failed"}
275 if err := h.AddRefusal(b, r); err != nil {
276 t.Fatal(err)
277 }
278 }
279 extra := Refusal{Host: b.Host, App: b.App, Version: "overflow", RefusedAt: time.Now(), Reason: "failed"}
280 if err := h.AddRefusal(b, extra); err == nil {
281 t.Fatal("overflow succeeded")
282 }
283 if _, err := h.Refusals(b); err == nil {
284 t.Fatal("overflow retained completeness and dropped a deny")
285 }
286 r, err := h.read(b)
287 if err != nil || len(r.Refusals.Values) != historyLimit {
288 t.Fatalf("lost retained denies: %v %v", len(r.Refusals.Values), err)
289 }
290}
291
292func TestPlan14RootHistoryRejectsCrossBinding(t *testing.T) {
293 h, s, b := rootHistoryFixture(t)
294 raw, err := s.Read(historyKey(b))
295 if err != nil {
296 t.Fatal(err)
297 }
298 for _, field := range []string{"host", "account", "app"} {
299 other := b
300 switch field {
301 case "host":
302 other.Host = "other.example"
303 case "account":
304 other.Account = "bap"
305 case "app":
306 other.App = "other"
307 }
308 if err := s.Write(historyKey(other), raw); err != nil {
309 t.Fatal(err)
310 }
311 if _, err := h.Programs(other); err == nil {
312 t.Fatalf("reused another %s's programs", field)
313 }
314 if _, err := h.Refusals(other); err == nil {
315 t.Fatalf("reused another %s's refusals", field)
316 }
317 }
318}
319
320func TestPlan14RootHistoryDuplicateRefusal(t *testing.T) {
321 h, _, b := rootHistoryFixture(t)
322 r := Refusal{Host: b.Host, App: b.App, Version: "new", RefusedAt: time.Now(), Reason: "failed"}
323 if err := h.AddRefusal(b, r); err != nil {
324 t.Fatal(err)
325 }
326 if err := h.AddRefusal(b, r); err != nil {
327 t.Fatal(err)
328 }
329 refs, err := h.Refusals(b)
330 if err != nil || len(refs) != 1 {
331 t.Fatalf("duplicate deny changed revision/content: %d %v", len(refs), err)
332 }
333}
334
335func TestPlan14RootHistoryOversizedDenyQuarantines(t *testing.T) {
336 h, _, b := rootHistoryFixture(t)
337 r := Refusal{Host: b.Host, App: b.App, Version: "new", RefusedAt: time.Now(), Reason: strings.Repeat("x", rootstate.MaxRecord)}
338 if err := h.AddRefusal(b, r); err == nil {
339 t.Fatal("oversized deny succeeded")
340 }
341 if _, err := h.Refusals(b); err == nil {
342 t.Fatal("oversized deny silently discarded")
343 }
344}
345
346func TestPlan14RootHistoryDuplicateJSONRefused(t *testing.T) {
347 h, s, b := rootHistoryFixture(t)
348 raw, err := s.Read(historyKey(b))
349 if err != nil {
350 t.Fatal(err)
351 }
352 raw = bytes.Replace(raw, []byte(`"complete":true`), []byte(`"complete":false,"complete":true`), 1)
353 if err := s.Write(historyKey(b), raw); err != nil {
354 t.Fatal(err)
355 }
356 if _, err := h.Programs(b); err == nil {
357 t.Fatal("duplicate complete field accepted")
358 }
359}
360
361func TestPlan14RootHistoryMissingTargetRefuses(t *testing.T) {
362 h, _, b := rootHistoryFixture(t)
363 for _, p := range []*ChannelPointer{nil, {}} {
364 if blocked, err := h.Blocks(b, p); err == nil || !blocked {
365 t.Fatal("missing target granted permission")
366 }
367 }
368}
369
370func TestPlan14RootHistoryPendingCannotMint(t *testing.T) {
371 h, _, b := rootHistoryFixture(t)
372 if err := h.Begin(b, HistoryAttempt{ID: "pending", Version: "new", Source: "etag", Started: time.Now()}); err != nil {
373 t.Fatal(err)
374 }
375 if _, err := h.Refusals(b); err == nil {
376 t.Fatal("pending update asserted a complete mint refusal list")
377 }
378}
379