carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostbox
2
3import (
4 "encoding/json"
5 "os"
6 "path/filepath"
7 "reflect"
8 "testing"
9)
10
11func TestPlan14GenerationPayloadInventory(t *testing.T) {
12 for _, attack := range []string{"none", "empty", "missing-payload", "missing-revision", "missing-table", "unknown-field", "trailing-json", "wrong-key", "incomplete", "oversize", "unknown-kind", "duplicate-entry", "missing-identity", "foreign-root", "replaced-path"} {
13 t.Run(attack, func(t *testing.T) {
14 j, err := OpenLaunchJournal(filepath.Join(t.TempDir(), "journal.db"))
15 if err != nil {
16 t.Fatal(err)
17 }
18 defer j.Close()
19 s, err := NewJournalService(j)
20 if err != nil {
21 t.Fatal(err)
22 }
23 in := StopInventory{Complete: true, Host: "tenant.example", Incarnation: "inc", InstallationStamp: 1, Box: "box", Enrollment: "en", DirectoryRevision: "1", Entries: []StopInventoryEntry{{Kind: "path", ArtifactID: "payload", ManagedRoot: "data", RelativePath: "instances/tenant.example/state", Ownership: "owner", Materialization: "materialized", Device: 1, Inode: 2, Type: 0100000}}}
24 if attack != "empty" {
25 if err := s.Inventory.WriteStopInventory(t.Context(), in); err != nil {
26 t.Fatal(err)
27 }
28 }
29 query := ""
30 switch attack {
31 case "missing-payload":
32 query = "DELETE FROM stop_inventory"
33 case "missing-revision":
34 query = "DELETE FROM journal_inventory_versions"
35 case "missing-table":
36 query = "DROP TABLE stop_inventory"
37 case "unknown-field":
38 query = "UPDATE stop_inventory SET body=json_set(body,'$.Unknown',true)"
39 case "trailing-json":
40 query = "UPDATE stop_inventory SET body=CAST(body || '{}' AS BLOB)"
41 case "wrong-key":
42 query = "UPDATE stop_inventory SET body=json_set(body,'$.Host','other.example')"
43 case "incomplete":
44 query = "UPDATE stop_inventory SET body=json_set(body,'$.Complete',json('false'))"
45 case "oversize":
46 query = "UPDATE stop_inventory SET body=zeroblob(1048577)"
47 case "unknown-kind":
48 in.Entries[0].Kind = "unknown"
49 case "duplicate-entry":
50 in.Entries = append(in.Entries, in.Entries[0])
51 case "missing-identity":
52 in.Entries[0].Inode = 0
53 case "foreign-root":
54 in.Entries[0].ManagedRoot = "opt"
55 case "replaced-path":
56 if err := os.Rename(j.path, j.path+".old"); err != nil {
57 t.Fatal(err)
58 }
59 if err := os.WriteFile(j.path, []byte("other"), 0600); err != nil {
60 t.Fatal(err)
61 }
62 }
63 if query != "" {
64 if _, err := j.db.Exec(query); err != nil {
65 t.Fatal(err)
66 }
67 }
68 if attack == "unknown-kind" || attack == "duplicate-entry" || attack == "missing-identity" || attack == "foreign-root" {
69 raw, err := json.Marshal(in)
70 if err != nil {
71 t.Fatal(err)
72 }
73 if _, err := j.db.Exec("UPDATE stop_inventory SET body=?", raw); err != nil {
74 t.Fatal(err)
75 }
76 }
77 // Use the production read-only connection, except for the retained-inode test.
78 reader := j
79 if attack != "replaced-path" {
80 reader, err = OpenGenerationLaunchReader(j.path)
81 if err != nil {
82 t.Fatal(err)
83 }
84 defer reader.Close()
85 }
86 got, err := reader.GenerationPayloads(t.Context())
87 switch attack {
88 case "none":
89 if err != nil || len(got) != 1 || got[0].Revision != 1 || !reflect.DeepEqual(got[0].Inventory, in) {
90 t.Fatalf("payloads=%+v err=%v", got, err)
91 }
92 case "empty":
93 if err != nil || len(got) != 0 {
94 t.Fatalf("empty=%+v err=%v", got, err)
95 }
96 default:
97 if err == nil {
98 t.Fatalf("unsafe %s payloads=%+v", attack, got)
99 }
100 }
101 })
102 }
103}
104
105func TestPlan14GenerationPayloadReaderDoesNotInitialize(t *testing.T) {
106 path := filepath.Join(t.TempDir(), "missing.db")
107 if err := os.WriteFile(path, nil, 0600); err != nil {
108 t.Fatal(err)
109 }
110 j, err := OpenGenerationLaunchReader(path)
111 if err != nil {
112 t.Fatal(err)
113 }
114 defer j.Close()
115 if _, err := j.GenerationPayloads(t.Context()); err == nil {
116 t.Fatal("missing inventory schema accepted")
117 }
118 if st, err := os.Stat(path); err != nil || st.Size() != 0 {
119 t.Fatalf("reader wrote schema: %v %v", st, err)
120 }
121}
122