nx_fin_route_gate.nx source
↩ module page · 166 lines · 9487 B
1// nx_fin_route_gate.nx -- proves nishifamily.com/finance is correctly served BEHIND THE OPAQUE WALL.
2// Drives the shared route table (olg_route) in-process, exactly as the daemon would, with a real OPAQUE
3// ctx (temp /tmp keys/store; light KSF for test speed, like nx_opaque_login_gate):
4// (a) GET /finance with NO session -> 401, and the page does NOT leak (no brand bytes in the response)
5// (b) GET /finance with a VALID Ed25519 no-cookie token -> 200 OK + the zero-JS dashboard (brand + live
6// engine output present, NO <script over the wire, served as text/html)
7// This is the integration proof of "finance behind the opaque system" AND a compile-check of the new
8// route. It does NOT touch the live daemon -- activation in production = an operator-coordinated daemon
9// rebuild/restart (the daemon imports this same route table). Exits 0 iff ALL pass. license_tier: ORIGINAL
10import "nx_opaque_login_routes.nx"
11import "nx_opaque_login.nx"
12import "nx_syscalls.nx"
13
14func g_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
15func g_putn(v: i64) -> i64 {
16 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 }
17 var m: i64 = v; if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
18 let d: *u8 = sys_mmap(24); var k: i64 = 0
19 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
20 var j: i64 = k - 1
21 while j >= 0 { sys_write(1, ((d as i64)+j) as *u8, 1); j = j - 1 }
22 return 0
23}
24func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 {
25 if got == want { st[0] = st[0] + 1; g_puts(" PASS "); g_puts(name); g_puts("\n") }
26 else { st[1] = st[1] + 1; g_puts(" FAIL "); g_puts(name); g_puts(" got="); g_putn(got); g_puts(" want="); g_putn(want); g_puts("\n") }
27 return 0
28}
29func rfind(hay: *u8, n: i64, needle: *u8) -> i64 {
30 var nl: i64 = 0; while needle[nl] != (0 as u8) { nl = nl + 1 }
31 if nl == 0 { return 0 }
32 var i: i64 = 0
33 while i + nl <= n {
34 var j: i64 = 0; var ok: i64 = 1
35 while j < nl { if hay[i + j] != needle[j] { ok = 0; j = nl } else { j = j + 1 } }
36 if ok == 1 { return i }
37 i = i + 1
38 }
39 return 0 - 1
40}
41func cpstr(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64 = off; var i: i64 = 0; while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 } return o }
42func cpbuf(dst: *u8, off: i64, src: *u8, n: i64) -> i64 { var o: i64 = off; var i: i64 = 0; while i < n { dst[o] = src[i]; o = o + 1; i = i + 1 } return o }
43func olg_trunc(path: *u8) -> i64 { let fd: i64 = sys_openat_wr(path, 0x1a4); if fd >= 0 { sys_close(fd) } return 0 }
44
45func main() -> i64 {
46 let st: *i64 = sys_mmap(16) as *i64
47 st[0] = 0; st[1] = 0
48
49 let keys: *u8 = "/tmp/nx_finroute_keys.log\x00" as *u8
50 let store: *u8 = "/tmp/nx_finroute_store.log\x00" as *u8
51 olg_trunc(keys); olg_trunc(store)
52 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
53 if olg_ctx_setup(ctx, keys, store, "nishi_site_admin" as *u8, 16, "Nishi site admin" as *u8, 16, 8192, 1, 1) != 0 {
54 g_puts("ctx-setup FAIL\n"); return 1
55 }
56 let pw: *u8 = "correct horse battery staple" as *u8
57 let mn: *u8 = sys_mmap(512); let mnn: *i64 = sys_mmap(16) as *i64
58 if olg_register(ctx, "elder" as *u8, 5, pw, 28, mn, 512, mnn) != NX_MAUTH_OK { g_puts("register FAIL\n"); return 2 }
59 let b64: *u8 = sys_mmap(512); let b64n: *i64 = sys_mmap(16) as *i64
60 if olg_login(ctx, "elder" as *u8, 5, pw, 28, b64, 512, b64n) != NX_MAUTH_OK { g_puts("login FAIL\n"); return 3 }
61 let now: i64 = sys_now_realtime_sec()
62 let resp: *u8 = sys_mmap(262144)
63
64 // (a) NO session -> 401, no page leak
65 let req1: *u8 = "GET /finance HTTP/1.1\r\nHost: nishifamily\r\n\r\n\x00" as *u8
66 var r1n: i64 = 0; while req1[r1n] != (0 as u8) { r1n = r1n + 1 }
67 let n1: i64 = olg_route(ctx, req1, r1n, now, resp, 262144)
68 var is401: i64 = 0
69 if rfind(resp, n1, "401\x00" as *u8) >= 0 { is401 = 1 }
70 chk("no-session /finance -> 401 Unauthorized", is401, 1, st)
71 var leak: i64 = 0
72 if rfind(resp, n1, "Nishi Finance\x00" as *u8) >= 0 { leak = 1 }
73 chk("no-session /finance leaks NO dashboard bytes", leak, 0, st)
74
75 // (b) valid session -> 200 + the zero-JS dashboard
76 let req2: *u8 = sys_mmap(2048)
77 var o: i64 = 0
78 o = cpstr(req2, o, "GET /finance HTTP/1.1\r\nX-Nishi-Session: \x00" as *u8)
79 o = cpbuf(req2, o, b64, b64n[0])
80 o = cpstr(req2, o, "\r\nHost: nishifamily\r\n\r\n\x00" as *u8)
81 let r2n: i64 = o
82 let n2: i64 = olg_route(ctx, req2, r2n, now, resp, 262144)
83 var is200: i64 = 0
84 if rfind(resp, n2, "200 OK\x00" as *u8) >= 0 { is200 = 1 }
85 chk("valid-session /finance -> 200 OK", is200, 1, st)
86 var brand: i64 = 0
87 if rfind(resp, n2, "Nishi Finance\x00" as *u8) >= 0 { brand = 1 }
88 chk("valid-session serves the dashboard (brand present)", brand, 1, st)
89 var av: i64 = 0
90 if rfind(resp, n2, "Avalanche\x00" as *u8) >= 0 { av = 1 }
91 chk("served page carries live engine output", av, 1, st)
92 var nojs: i64 = 0
93 if rfind(resp, n2, "<script\x00" as *u8) < 0 { nojs = 1 }
94 chk("served page is zero-JS over the wire", nojs, 1, st)
95 var html: i64 = 0
96 if rfind(resp, n2, "text/html\x00" as *u8) >= 0 { html = 1 }
97 chk("served as text/html", html, 1, st)
98
99 // ---- GET /finance/intake -> the intake form (more-specific route, before /finance) ----
100 let req3: *u8 = sys_mmap(2048)
101 var o3: i64 = cpstr(req3, 0, "GET /finance/intake HTTP/1.1\r\nX-Nishi-Session: \x00" as *u8)
102 o3 = cpbuf(req3, o3, b64, b64n[0])
103 o3 = cpstr(req3, o3, "\r\nHost: nishifamily\r\n\r\n\x00" as *u8)
104 let n3: i64 = olg_route(ctx, req3, o3, now, resp, 262144)
105 var hasform: i64 = 0
106 if rfind(resp, n3, "Save my situation\x00" as *u8) >= 0 { hasform = 1 }
107 chk("GET /finance/intake -> serves the intake form", hasform, 1, st)
108
109 // ---- POST /finance/intake -> records the submission + Saved ----
110 let req4: *u8 = sys_mmap(2048)
111 var o4: i64 = cpstr(req4, 0, "POST /finance/intake HTTP/1.1\r\nX-Nishi-Session: \x00" as *u8)
112 o4 = cpbuf(req4, o4, b64, b64n[0])
113 o4 = cpstr(req4, o4, "\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\nincome_monthly=4000&debt_balance=20000&hourly_wage=25&has_adhd=1\x00" as *u8)
114 let n4: i64 = olg_route(ctx, req4, o4, now, resp, 262144)
115 var post200: i64 = 0
116 if rfind(resp, n4, "200 OK\x00" as *u8) >= 0 { post200 = 1 }
117 chk("POST /finance/intake -> 200 OK", post200, 1, st)
118 var saved: i64 = 0
119 if rfind(resp, n4, "Saved\x00" as *u8) >= 0 { saved = 1 }
120 chk("POST /finance/intake -> recorded the submission (Saved)", saved, 1, st)
121
122 // ---- POST /finance/progress: mark the user's next climb step done -> 303 back to /finance ----
123 // no session -> 401 (you cannot advance someone's progress unauthenticated)
124 let req5: *u8 = "POST /finance/progress HTTP/1.1\r\nContent-Length: 0\r\nHost: nishifamily\r\n\r\n\x00" as *u8
125 var r5n: i64 = 0; while req5[r5n] != (0 as u8) { r5n = r5n + 1 }
126 let n5: i64 = olg_route(ctx, req5, r5n, now, resp, 262144)
127 var pg401: i64 = 0
128 if rfind(resp, n5, "401\x00" as *u8) >= 0 { pg401 = 1 }
129 chk("no-session /finance/progress -> 401 (session-gated)", pg401, 1, st)
130 // valid session -> 303 See Other, Location: /finance (post-redirect-get)
131 let req6: *u8 = sys_mmap(2048)
132 var o6: i64 = cpstr(req6, 0, "POST /finance/progress HTTP/1.1\r\nX-Nishi-Session: \x00" as *u8)
133 o6 = cpbuf(req6, o6, b64, b64n[0])
134 o6 = cpstr(req6, o6, "\r\nContent-Length: 0\r\nHost: nishifamily\r\n\r\n\x00" as *u8)
135 let n6: i64 = olg_route(ctx, req6, o6, now, resp, 262144)
136 var pg303: i64 = 0
137 if rfind(resp, n6, "303 See Other\x00" as *u8) >= 0 { pg303 = 1 }
138 chk("valid-session /finance/progress -> 303 (marks a step, redirects)", pg303, 1, st)
139 var pgloc: i64 = 0
140 if rfind(resp, n6, "Location: /finance\x00" as *u8) >= 0 { pgloc = 1 }
141 chk("/finance/progress redirects to /finance (post-redirect-get)", pgloc, 1, st)
142
143 // ---- POST /finance/bills: record one bill -> 303 back to /finance ----
144 let req7: *u8 = "POST /finance/bills HTTP/1.1\r\nContent-Length: 0\r\nHost: nishifamily\r\n\r\n\x00" as *u8
145 var r7n: i64 = 0; while req7[r7n] != (0 as u8) { r7n = r7n + 1 }
146 let n7: i64 = olg_route(ctx, req7, r7n, now, resp, 262144)
147 var pb401: i64 = 0
148 if rfind(resp, n7, "401\x00" as *u8) >= 0 { pb401 = 1 }
149 chk("no-session /finance/bills -> 401 (session-gated)", pb401, 1, st)
150 let req8: *u8 = sys_mmap(2048)
151 var o8: i64 = cpstr(req8, 0, "POST /finance/bills HTTP/1.1\r\nX-Nishi-Session: \x00" as *u8)
152 o8 = cpbuf(req8, o8, b64, b64n[0])
153 o8 = cpstr(req8, o8, "\r\nContent-Type: application/x-www-form-urlencoded\r\n\r\nbill_amount=180&bill_cadence=1&bill_due_day=5&bill_category=1&bill_paid=0\x00" as *u8)
154 let n8: i64 = olg_route(ctx, req8, o8, now, resp, 262144)
155 var pb303: i64 = 0
156 if rfind(resp, n8, "303 See Other\x00" as *u8) >= 0 { pb303 = 1 }
157 chk("valid-session /finance/bills -> 303 (records the bill, redirects)", pb303, 1, st)
158 var pbloc: i64 = 0
159 if rfind(resp, n8, "Location: /finance\x00" as *u8) >= 0 { pbloc = 1 }
160 chk("/finance/bills redirects to /finance (post-redirect-get)", pbloc, 1, st)
161
162 g_puts("nx_fin_route_gate: PASS="); g_putn(st[0]); g_puts(" FAIL="); g_putn(st[1]); g_puts("\n")
163 if st[1] == 0 { g_puts("FINANCE BEHIND OPAQUE: GREEN\n"); return 0 }
164 g_puts("FINANCE BEHIND OPAQUE: RED\n")
165 return 1
166}