code wiki / _hdl_build / nx_pub_serve_gate.nx

nx_pub_serve_gate.nx source

↩ module page · 85 lines · 5739 B

1// nx_pub_serve_gate.nx -- PUB-SERVE R0 proof: the publisher's OWN static server serves its liveroot over real 2// loopback HTTP. Forks nx_pub_serve, then (as a sovereign HTTP client) asserts: (1) a published file is served 3// 200 + correct bytes; (2) a missing file -> 404; (3) a path-traversal (../) is REFUSED (security boundary); 4// (4) RE-PUBLISHING (overwrite the file) is served IMMEDIATELY with no server restart -- proving publish = file 5// drop + serve, NOT a daemon-swap. Sovereign sockets both sides, per-wsid port. license_tier: ORIGINAL expect_exit: 0 6import "nx_syscalls.nx" 7import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 8import "nx_publisher.nx" 9import "nx_pub_serve.nx" 10import "nx_runpath.nx" 11 12func g_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 13func g_n(v: i64) -> i64 { var m: i64=v; if m<0{g_w("-" as *u8);m=0-m} let t:*u8=sys_mmap(24); 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 i:i64=0; let o:*u8=sys_mmap(24); while i<k{o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 } 14func g_row(id: *u8, ok: i64, pass: *i64) -> i64 { g_w(" " as *u8); g_w(id); g_w(": " as *u8); if ok==1 { g_w("OK\n" as *u8); pass[0]=pass[0]+1 } else { g_w("FAIL\n" as *u8) } return 0 } 15func sv_write(path: *u8, content: *u8) -> i64 { let fd: i64 = sys_openat_wr(path, 0x1a4); if fd<0 { return 0 } var n: i64=0; while content[n]!=(0 as u8){n=n+1} sys_write(fd, content, n); sys_close(fd); return 1 } 16func sv_nap(ms: i64) -> i64 { let ts: *i64 = sys_mmap(16) as *i64; ts[0]=0; ts[1]=ms*1000000; __syscall(35, ts as i64, 0, 0, 0, 0, 0); return 0 } 17 18// sovereign HTTP/1.0 client: connect 127.0.0.1:port (retry until server is up), GET /<path>, read response. bytes read. 19func cg_get(port: i64, path: *u8, resp: *u8, cap: i64) -> i64 { 20 var tries: i64 = 0 21 while tries < 80 { 22 let fd: i64 = sys_socket(2, 1, 0) 23 if fd >= 0 { 24 let tv: *u8 = sys_mmap(16); tv[0]=2 as u8; var tz: i64=1; while tz<16 { tv[tz]=0 as u8; tz=tz+1 } // 2s 25 sys_setsockopt(fd, 1, 20, tv, 16) // SO_RCVTIMEO -> sys_read can't block forever 26 let addr: *u8 = sys_mmap(16) 27 addr[0]=2 as u8; addr[1]=0 as u8; addr[2]=((port>>8)&0xff) as u8; addr[3]=(port&0xff) as u8 28 addr[4]=127 as u8; addr[5]=0 as u8; addr[6]=0 as u8; addr[7]=1 as u8 29 var z: i64=8; while z<16 { addr[z]=0 as u8; z=z+1 } 30 if nx_connect_bounded(fd, addr, 16, NX_CONN_DEFAULT_MS) == 0 { 31 let r: *u8 = sys_mmap(512); var o: i64=0 32 o = fa_cat(r, o, "GET /" as *u8); o = fa_cat(r, o, path); o = fa_cat(r, o, " HTTP/1.0\r\nConnection: close\r\n\r\n" as *u8) 33 sys_write(fd, r, o) 34 let n: i64 = sys_read(fd, resp, cap) 35 sys_close(fd) 36 return n 37 } 38 sys_close(fd) 39 } 40 sv_nap(20) 41 tries = tries + 1 42 } 43 return 0 - 1 44} 45 46func main() -> i64 { 47 let pass: *i64 = sys_mmap(8) as *i64; pass[0]=0 48 g_w("=== NX-PUB-SERVE GATE (the publisher serves its own liveroot over real HTTP) ===\n" as *u8) 49 let wsid: *u8 = sys_mmap(64); rp_wsid(wsid); rp_ensure(wsid) 50 let liveroot: *u8 = sys_mmap(512); rp_path(wsid, "psg_live" as *u8, liveroot); __syscall(83, liveroot as i64, 493, 0, 0, 0, 0) 51 // per-wsid port (avoid sibling collision): 18620 + sum(wsid) mod 300 52 var psum: i64 = 0; var pj: i64 = 0; while wsid[pj]!=(0 as u8){ psum = psum + (wsid[pj] as i64); pj = pj + 1 } 53 let port: i64 = 18620 + (psum % 300) 54 55 let testf: *u8 = sys_mmap(700); pub_join(liveroot, "pubtest.html" as *u8, testf) 56 sv_write(testf, "<h1>PUBLISHED-OK</h1>\n" as *u8) 57 58 let pid: i64 = sys_fork() 59 if pid == 0 { psv_listen(liveroot, port); sys_exit(0) } // child: serve forever 60 61 let resp: *u8 = sys_mmap(65536) 62 let n1: i64 = cg_get(port, "pubtest.html" as *u8, resp, 65536) 63 let ok200: i64 = ((pub_substr(resp, n1, "200 OK" as *u8)==1) as i64) & ((pub_substr(resp, n1, "PUBLISHED-OK" as *u8)==1) as i64) 64 let n2: i64 = cg_get(port, "missing.html" as *u8, resp, 65536) 65 let ok404: i64 = (pub_substr(resp, n2, "404" as *u8)==1) as i64 66 let n3: i64 = cg_get(port, "../nx_publisher.nx" as *u8, resp, 65536) 67 let blocked: i64 = (pub_substr(resp, n3, "200 OK" as *u8)==0) as i64 68 // re-publish: overwrite the file, GET again -> served NEW content with NO restart 69 sv_write(testf, "<h1>UPDATED-V2</h1>\n" as *u8) 70 let n4: i64 = cg_get(port, "pubtest.html" as *u8, resp, 65536) 71 let republished: i64 = (pub_substr(resp, n4, "UPDATED-V2" as *u8)==1) as i64 72 73 __syscall(129, pid, 9, 0, 0, 0, 0) // kill the server -- rv64 kill=129; raw x86 62 is an RV64 KEY translated to lseek(8) (debt idx 2277) 74 let st: *i64 = sys_mmap(8) as *i64; var rr: i64 = 0; while rr < 20 { sys_wait4(0 - 1, st, 1); rr = rr + 1 } // non-blocking reap (WNOHANG); never block teardown 75 76 g_w(" port="); g_n(port); g_w(" resp_bytes: pubtest="); g_n(n1); g_w(" missing="); g_n(n2); g_w(" traversal="); g_n(n3); g_w(" republish="); g_n(n4); g_w("\n") 77 g_row("SERVES the published file over HTTP (200 OK + the published bytes)" as *u8, ok200, pass) 78 g_row("404 for a file not in liveroot" as *u8, ok404, pass) 79 g_row("SECURITY: path traversal (../) REFUSED (not 200)" as *u8, blocked, pass) 80 g_row("RE-PUBLISH served immediately, NO restart (publish = file-drop, not daemon-swap)" as *u8, republished, pass) 81 82 g_w("PUB-SERVE rows=4 pass="); g_n(pass[0]) 83 if pass[0]==4 { g_w(" verdict=GREEN\n"); sys_exit(0); return 0 } 84 g_w(" verdict=RED\n"); sys_exit(1); return 1 85}