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 "crypto/sha256"
6 "fmt"
7 "net/http"
8 "slices"
9 "sort"
10 "strings"
11 "testing"
12)
13
14func claimProbeApps(t *testing.T, cx *claimCase) []claimAppRef {
15 t.Helper()
16 rows, err := cx.st.AppRows()
17 if err != nil {
18 t.Fatal(err)
19 }
20 refs := make([]claimAppRef, 0, len(rows))
21 for _, row := range rows {
22 refs = append(refs, claimAppRef{Account: row.Sqid, App: row.App})
23 }
24 return refs
25}
26
27func TestClaimProbeDetectsSQLMutationOutsideDefaultApps(t *testing.T) {
28 cx := newClaimCase(t)
29 const path = "/claim-witness/third-app"
30 p := claimProbe{setup: func(t *testing.T, cx *claimCase) {
31 id := cx.enroll(t, "bab", "third")
32 cx.srv.registerRoute("POST "+path, claimWitnessHandler(cx, func(r *http.Request) error {
33 scopes := []claimSQLScope{{AccountID: OpsAccountID, App: "third", AppID: id, Mode: claimModeEffect}}
34 return cx.st.withClaimSQL(gateCtx(t), scopes, allowAllClaimSQL, func(st *Store) error {
35 _, err := st.db.Exec(`UPDATE apps SET claim_op='ungated' WHERE account_id=? AND app=?`, OpsAccountID, "third")
36 return err
37 })
38 }))
39 }, do: claimAPIReply(http.MethodPost, path, `{}`), effect: func(*testing.T, *claimCase, claimReply) string { return "" }}
40 problems := runClaimProbe(t, cx, "route POST "+path, p)
41 if !slices.ContainsFunc(problems, func(p string) bool { return strings.Contains(p, "SQL rows of bab/third changed") }) {
42 t.Fatalf("third app mutation escaped census: %v", problems)
43 }
44}
45
46// Digest every app-scoped table directly, including rows whose parent app
47// or account no longer exists. Per-app diagnostics remain useful but cannot
48// establish that orphan SQL rows were unchanged.
49func claimProbeAllSQL(t *testing.T, cx *claimCase) map[string]string {
50 t.Helper()
51 out := map[string]string{}
52 for name, tb := range claimSQLTables {
53 if tb.NonApp != "" {
54 continue
55 }
56 query := "SELECT * FROM " + claimQ(name) + " t"
57 if tb.FreeWhenNoApp {
58 query += " WHERE COALESCE(" + tb.col("t", tb.App) + ", '') != ''"
59 }
60 rows, err := cx.st.db.QueryContext(context.Background(), query)
61 if err != nil {
62 if strings.Contains(err.Error(), "no such table") {
63 continue
64 }
65 t.Fatal(err)
66 }
67 cols, err := rows.Columns()
68 if err != nil {
69 rows.Close()
70 t.Fatal(err)
71 }
72 var lines []string
73 for rows.Next() {
74 vals := make([]any, len(cols))
75 ptrs := make([]any, len(cols))
76 for i := range vals {
77 ptrs[i] = &vals[i]
78 }
79 if err := rows.Scan(ptrs...); err != nil {
80 rows.Close()
81 t.Fatal(err)
82 }
83 // Typed values and quoted strings keep distinct SQL rows distinct.
84 var parts []string
85 for _, v := range vals {
86 parts = append(parts, fmt.Sprintf("%T:%#v", v, v))
87 }
88 lines = append(lines, strings.Join(parts, "\x1f"))
89 }
90 err = rows.Err()
91 rows.Close()
92 if err != nil {
93 t.Fatal(err)
94 }
95 sort.Strings(lines)
96 out[name] = fmt.Sprintf("%x", sha256.Sum256([]byte(strings.Join(lines, "\x1e"))))
97 }
98 return out
99}
100
101func TestClaimProbeDetectsOrphanSQLMutation(t *testing.T) {
102 cx := newClaimCase(t)
103 const path = "/claim-witness/orphan-audit"
104 p := claimProbe{setup: func(t *testing.T, cx *claimCase) {
105 scopes := []claimSQLScope{{AccountID: OpsAccountID, App: "orphan", AppID: "old", Mode: claimModeEffect}}
106 if err := cx.st.withClaimSQL(gateCtx(t), scopes, allowAllClaimSQL, func(st *Store) error {
107 _, err := st.db.Exec(`INSERT INTO audit (app,address,action,created_at,account_id) VALUES ('orphan','test','old',1,?)`, OpsAccountID)
108 return err
109 }); err != nil {
110 t.Fatal(err)
111 }
112 cx.srv.registerRoute("POST "+path, claimWitnessHandler(cx, func(r *http.Request) error {
113 return cx.st.withClaimSQL(gateCtx(t), scopes, allowAllClaimSQL, func(st *Store) error { _, err := st.db.Exec(`DELETE FROM audit WHERE app='orphan'`); return err })
114 }))
115 }, do: claimAPIReply(http.MethodPost, path, `{}`), effect: func(*testing.T, *claimCase, claimReply) string { return "" }}
116 problems := runClaimProbe(t, cx, "route POST "+path, p)
117 if !slices.ContainsFunc(problems, func(s string) bool { return strings.Contains(s, "app-scoped SQL tables changed: [audit]") }) {
118 t.Fatalf("orphan write escaped: %v", problems)
119 }
120}
121