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}