code wiki / _hdl_build / nx_studio_gate_smoke.nx
nx_studio_gate_smoke.nx source
↩ module page · 130 lines · 7249 B
1// nx_studio_gate_smoke.nx -- forks nx_studio_gate and drives it as a REAL HTTP client over a socket, proving
2// the OPERATOR-ONLY gate end-to-end: register -> login -> GET /app[no token]=401 (THE GATE) -> GET /app[valid]
3// =200+studio page -> GET /app[bad token]=401 -> GET /whoami[valid]=200. This is the gate-first safety proof
4// (no content reachable without a valid OPAQUE session) that the prod cutover requires. Needs
5// /tmp/nx_studio_gate.sov.elf built in the same WSL session. exit 0 = pass, N = assertion N failed.
6import "nx_syscalls.nx"
7import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
8import "nx_assert.nx"
9const SMK_MAGIC_32768: i64 = 32768
10const SMK_MAGIC_2048: i64 = 2048
11const SMK_MAGIC_32767: i64 = 32767
12
13const SMK_PORT: i64 = 18097
14const DAEMON_ELF: *u8 = "/tmp/nx_studio_gate.sov.elf" as *u8
15
16func smk_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
17func smk_find(buf: *u8, n: i64, needle: *u8, nl: i64) -> i64 {
18 if nl==0 { return 0 }
19 var i: i64=0
20 while i+nl<=n { var j: i64=0; var ok: i64=1; while j<nl { if buf[i+j]!=needle[j]{ok=0;j=nl} else {j=j+1} } if ok==1 {return i} i=i+1 }
21 return 0-1
22}
23func smk_trunc(path: *u8) -> i64 { let fd: i64=sys_openat_wr(path, 0x1a4); if fd>=0 { sys_close(fd) } return 0 }
24
25func smk_http(req: *u8, reqn: i64, resp: *u8, cap: i64) -> i64 {
26 let fd: i64 = sys_socket(2, 1, 0); if fd < 0 { return 0-1 }
27 sys_set_socket_timeout(fd, 5)
28 let a: *u8 = sys_mmap(16)
29 a[0]=2 as u8; a[1]=0 as u8; a[2]=((SMK_PORT>>8)&0xff) as u8; a[3]=(SMK_PORT&0xff) as u8
30 a[4]=127 as u8; a[5]=0 as u8; a[6]=0 as u8; a[7]=1 as u8
31 var zi: i64=8; while zi<16 { a[zi]=0 as u8; zi=zi+1 }
32 if nx_connect_bounded(fd, a, 16, NX_CONN_DEFAULT_MS) != 0 { sys_close(fd); return 0-2 }
33 sys_write(fd, req, reqn)
34 var off: i64=0; var go: i64=1
35 while go==1 { let r: i64=sys_read(fd, ((resp as i64)+off) as *u8, cap-off); if r<=0 {go=0} else { off=off+r; if off>=cap {go=0} } }
36 sys_close(fd); return off
37}
38func smk_post(reqbuf: *u8, path: *u8, body: *u8) -> i64 {
39 let bl: i64 = smk_slen(body)
40 var o: i64=0
41 let p1: *u8="POST " as *u8; var i: i64=0; while p1[i]!=(0 as u8){reqbuf[o]=p1[i];o=o+1;i=i+1}
42 i=0; while path[i]!=(0 as u8){reqbuf[o]=path[i];o=o+1;i=i+1}
43 let p2: *u8=" HTTP/1.1\r\nHost: x\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " as *u8; i=0; while p2[i]!=(0 as u8){reqbuf[o]=p2[i];o=o+1;i=i+1}
44 let t: *u8=sys_mmap(28); var m: i64=bl; var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var q: i64=k-1; while q>=0{reqbuf[o]=t[q];o=o+1;q=q-1}
45 let p3: *u8="\r\nConnection: close\r\n\r\n" as *u8; i=0; while p3[i]!=(0 as u8){reqbuf[o]=p3[i];o=o+1;i=i+1}
46 i=0; while body[i]!=(0 as u8){reqbuf[o]=body[i];o=o+1;i=i+1}
47 return o
48}
49// build a GET; tok==0 -> no X-Nishi-Session header, else include it.
50func smk_get(reqbuf: *u8, path: *u8, tok: *u8) -> i64 {
51 var o: i64=0
52 let p1: *u8="GET " as *u8; var i: i64=0; while p1[i]!=(0 as u8){reqbuf[o]=p1[i];o=o+1;i=i+1}
53 i=0; while path[i]!=(0 as u8){reqbuf[o]=path[i];o=o+1;i=i+1}
54 let p2: *u8=" HTTP/1.1\r\nHost: x\r\n" as *u8; i=0; while p2[i]!=(0 as u8){reqbuf[o]=p2[i];o=o+1;i=i+1}
55 if (tok as i64)!=0 {
56 let p3: *u8="X-Nishi-Session: " as *u8; i=0; while p3[i]!=(0 as u8){reqbuf[o]=p3[i];o=o+1;i=i+1}
57 i=0; while tok[i]!=(0 as u8){reqbuf[o]=tok[i];o=o+1;i=i+1}
58 reqbuf[o]=13 as u8; o=o+1; reqbuf[o]=10 as u8; o=o+1
59 }
60 let p4: *u8="Connection: close\r\n\r\n" as *u8; i=0; while p4[i]!=(0 as u8){reqbuf[o]=p4[i];o=o+1;i=i+1}
61 return o
62}
63
64func main() -> i64 {
65 smk_trunc("/tmp/nx_sg_keys.log" as *u8)
66 smk_trunc("/tmp/nx_sg_store.log" as *u8)
67
68 let pid: i64 = sys_fork()
69 if pid == 0 {
70 let argv: *i64 = sys_mmap(64) as *i64
71 argv[0]=DAEMON_ELF as i64; argv[1]="18097" as *u8 as i64; argv[2]="/tmp/nx_sg_keys.log" as *u8 as i64
72 argv[3]="/tmp/nx_sg_store.log" as *u8 as i64; argv[4]="14" as *u8 as i64; argv[5]="8192" as *u8 as i64; argv[6]=0
73 let envp: *i64 = sys_mmap(16) as *i64; envp[0]="PATH=/usr/bin:/bin" as *u8 as i64; envp[1]=0
74 sys_execve(DAEMON_ELF, argv, envp)
75 sys_exit(127)
76 }
77 sys_sleep_ms(500)
78
79 let resp: *u8 = sys_mmap(SMK_MAGIC_32768)
80 let req: *u8 = sys_mmap(SMK_MAGIC_2048)
81
82 // 1. register
83 let rqn: i64 = smk_post(req, "/studio/register" as *u8, "handle=smoke&pw=testpass123" as *u8)
84 let r1: i64 = smk_http(req, rqn, resp, SMK_MAGIC_32767)
85 nx_puts_err("register -> bytes="); nx_puti_err(r1)
86 if smk_find(resp, r1, "200 OK" as *u8, 6) < 0 { nx_kill(pid,9); return 1 }
87 if smk_find(resp, r1, "mnemonic" as *u8, 8) < 0 { nx_kill(pid,9); return 2 }
88 nx_puts_err(" PASS\n" as *u8)
89
90 // 2. login -> token
91 let lqn: i64 = smk_post(req, "/studio/login" as *u8, "handle=smoke&pw=testpass123" as *u8)
92 let r2: i64 = smk_http(req, lqn, resp, SMK_MAGIC_32767)
93 if smk_find(resp, r2, "200 OK" as *u8, 6) < 0 { nx_kill(pid,9); return 3 }
94 let tp: i64 = smk_find(resp, r2, "\"token\":\"" as *u8, 9)
95 if tp < 0 { nx_kill(pid,9); return 4 }
96 let tok: *u8 = sys_mmap(512); var ti: i64=0; var si: i64=tp+9
97 while si < r2 { if resp[si]==(34 as u8) { si=r2 } else { tok[ti]=resp[si]; ti=ti+1; si=si+1 } }
98 tok[ti]=0 as u8
99 nx_puts_err("login -> token chars="); nx_puti_err(ti); nx_puts_err(" PASS\n" as *u8)
100
101 // 3. GET /app with NO token -> 401 (THE GATE: no content reachable without a session)
102 let gq1: i64 = smk_get(req, "/studio/app" as *u8, 0 as *u8)
103 let r3: i64 = smk_http(req, gq1, resp, SMK_MAGIC_32767)
104 if smk_find(resp, r3, "401" as *u8, 3) < 0 { nx_kill(pid,9); return 5 }
105 if smk_find(resp, r3, "Search by Image" as *u8, 15) >= 0 { nx_kill(pid,9); return 6 } // MUST NOT leak the page
106 nx_puts_err("GET /app [no token] -> 401, no content leak PASS (THE GATE)\n" as *u8)
107
108 // 4. GET /app with VALID token -> 200 + studio page
109 let gq2: i64 = smk_get(req, "/studio/app" as *u8, tok)
110 let r4: i64 = smk_http(req, gq2, resp, SMK_MAGIC_32767)
111 if smk_find(resp, r4, "200 OK" as *u8, 6) < 0 { nx_kill(pid,9); return 7 }
112 if smk_find(resp, r4, "Search by Image" as *u8, 15) < 0 { nx_kill(pid,9); return 8 }
113 nx_puts_err("GET /app [valid] -> 200 + studio page PASS\n" as *u8)
114
115 // 5. GET /app with BAD token -> 401
116 let gq3: i64 = smk_get(req, "/studio/app" as *u8, "AAAAAAAAAAAAAAAAAAAAAAAA" as *u8)
117 let r5: i64 = smk_http(req, gq3, resp, SMK_MAGIC_32767)
118 if smk_find(resp, r5, "401" as *u8, 3) < 0 { nx_kill(pid,9); return 9 }
119 nx_puts_err("GET /app [bad token] -> 401 PASS\n" as *u8)
120
121 // 6. whoami valid -> 200 (sanity)
122 let wq: i64 = smk_get(req, "/studio/whoami" as *u8, tok)
123 let r6: i64 = smk_http(req, wq, resp, SMK_MAGIC_32767)
124 if smk_find(resp, r6, "200 OK" as *u8, 6) < 0 { nx_kill(pid,9); return 10 }
125 nx_puts_err("GET /whoami [valid] -> 200 PASS\n" as *u8)
126
127 let st: *i64 = sys_mmap(16) as *i64; nx_kill(pid, 9); sys_wait4(pid, st, 0)
128 nx_puts_err("nx_studio_gate_smoke verdict=GREEN pass=6 (register/login/app-no-token-401/app-valid-200/app-bad-401/whoami)\n" as *u8)
129 return 0
130}