carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "os"
6 "testing"
7
8 "github.com/carlosframework/platform/internal/moves"
9)
10
11func TestPlan14RootFloorBootDecision(t *testing.T) {
12 for _, attack := range []string{"none", "current below", "prev below", "stale verdict", "missing floor", "corrupt floor", "old reader", "pending participation", "replaced binary"} {
13 t.Run(attack, func(t *testing.T) {
14 f := rootMovesFloors{private: &rollbackMemory{files: map[string][]byte{}}, public: &rollbackMemory{files: map[string][]byte{}}, identity: rootFloorIdentity{Deployment: "d", Box: "b", Enrollment: "e"}}
15 b := moves.Floor{Min: 1, MinRelease: "v2.0.0"}
16 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: f.identity, Floor: &b, Source: "history", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
17 t.Fatal(err)
18 }
19 current := rootFloorBinary{Version: "v2.0.0", Protocol: 1, Digest: floorTestDigest}
20 prev := current
21 j := rootFloorJudge{floors: f, current: "current", prev: "prev", binary: func(path string) (rootFloorBinary, error) {
22 if path == "prev" {
23 return prev, nil
24 }
25 return current, nil
26 }, idle: func(context.Context) error { return nil }}
27 if err := j.judge(t.Context()); err != nil {
28 t.Fatal(err)
29 }
30 switch attack {
31 case "current below":
32 current.Version = "v1.0.0"
33 prev = current
34 case "prev below":
35 prev.Version = "v1.0.0"
36 case "stale verdict":
37 if err := f.refresh(t.Context(), &b, nil, nil); err != nil {
38 t.Fatal(err)
39 }
40 case "missing floor":
41 f.public.Remove(rootFloorSelector)
42 case "corrupt floor":
43 f.public.Write(rootFloorSelector, []byte("broken"))
44 case "replaced binary":
45 prev.Digest = "replacement"
46 }
47 calls := 0
48 gate := func(context.Context, string) error {
49 calls++
50 if attack == "old reader" {
51 return os.ErrPermission
52 }
53 return nil
54 }
55 idle := func(context.Context) error {
56 if attack == "pending participation" {
57 return os.ErrPermission
58 }
59 return nil
60 }
61 got, err := rootFloorBootCandidate(t.Context(), j, true, gate, idle)
62
63 if err != nil {
64 t.Fatal(err)
65 }
66 want := "current"
67 if attack == "none" {
68 want = "prev"
69 }
70 if got != want {
71 t.Fatal(got, want)
72 }
73 if attack == "prev below" && calls != 0 {
74 t.Fatal("below-floor previous binary executed for gate")
75 }
76 })
77 }
78}
79
80func TestPlan14PrevRebootSafeVerdict(t *testing.T) {
81 f := rootMovesFloors{private: &rollbackMemory{files: map[string][]byte{}}, public: &rollbackMemory{files: map[string][]byte{}}, identity: rootFloorIdentity{"d", "b", "e"}}
82 bound := moves.Floor{Min: 1, MinRelease: "v2.0.0"}
83 if err := f.reconstruct(t.Context(), rootFloorReconstruction{Identity: f.identity, Floor: &bound, Source: "accepted", Digest: floorTestDigest, Authorization: floorTestAuthorization(t, f)}); err != nil {
84 t.Fatal(err)
85 }
86 prev := rootFloorBinary{Version: "v2.0.0", Protocol: 1, Digest: floorTestDigest}
87 broken := false
88 j := rootFloorJudge{floors: f, current: "current", prev: "prev", binary: func(path string) (rootFloorBinary, error) {
89 if path == "current" && broken {
90 return rootFloorBinary{}, os.ErrPermission
91 }
92 return prev, nil
93 }, idle: func(context.Context) error { return nil }}
94 if err := j.judge(t.Context()); err != nil {
95 t.Fatal(err)
96 }
97 prev.File.Device++
98 broken = true
99 got, err := rootFloorBootCandidate(t.Context(), j, true, func(context.Context, string) error { return nil }, func(context.Context) error { return nil })
100 if err != nil || got != "prev" {
101 t.Fatal("interrupted update lost unchanged previous image after device renumber", got, err)
102 }
103 prev.Digest = "substituted"
104 if err := rootFloorPrevVerdict(j); err == nil {
105 t.Fatal("substituted bytes accepted")
106 }
107}
108