code wiki / _hdl_build / nx_docportal_multipart_gate.nx
nx_docportal_multipart_gate.nx source
↩ module page · 125 lines · 8934 B
1import "nx_gate_base.nx"
2// nx_docportal_multipart_gate.nx -- SOVEREIGN referee for REAL multipart/form-data file upload on the doc
3// portal: craft the exact bytes an <input type=file> form submits, drive the pure router dad_handle, and
4// assert the file stores through the SAME /admin/upload path (binary-safe, session-gated, ?s= OR header).
5// GREEN iff 6/6. license_tier: ORIGINAL
6import "nx_docportal_admin_daemon.nx"
7import "hub/nx_modern_auth_flow.nx"
8import "nx_syscalls.nx"
9
10func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw("
11" as *u8); return ok }
12func gln(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
13func gcat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){d[o+i]=s[i];i=i+1} return o+i }
14func gcatn(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64=0; while i<n {d[o+i]=s[i];i=i+1} return o+n }
15func gcontains(hay: *u8, n: i64, needle: *u8) -> i64 { let nn: i64=gln(needle); var i: i64=0; while i+nn<=n { var m: i64=1; var j: i64=0; while j<nn { if (hay[i+j] as i64)!=(needle[j] as i64){m=0;j=nn} else {j=j+1} } if m==1 {return 1} i=i+1 } return 0 }
16func gtoken(resp: *u8, n: i64, out: *u8) -> i64 { let key: *u8="{\"token\":\"" as *u8; let kn: i64=10; var st: i64=0-1; var i: i64=0; while i+kn<=n { var m: i64=1; var j: i64=0; while j<kn { if (resp[i+j] as i64)!=(key[j] as i64){m=0;j=kn} else {j=j+1} } if m==1 {st=i+kn;i=n} i=i+1 } if st<0 {return 0} var w: i64=0; var k: i64=st; while k<n { if (resp[k] as i64)==34 {k=n} else {out[w]=resp[k];w=w+1;k=k+1} } return w }
17func gcell(ok: i64, id: *u8, txt: *u8) -> i64 { if ok==1 {gw(" PASS " as *u8)} else {gw(" FAIL " as *u8)} gw(id); gw(" " as *u8); gw(txt); gw("\n" as *u8); return ok }
18
19// build a multipart body (boundary ----B) with domain/visibility/pub_search parts + a `file` part whose
20// content INCLUDES an embedded blank line (\r\n\r\n) to prove binary/whitespace-safety. Returns length.
21func gbuild_body(out: *u8) -> i64 {
22 var o: i64 = 0
23 o = gcat(out, o, "------B\r\nContent-Disposition: form-data; name=\"domain\"\r\n\r\nandelinwest.com\r\n" as *u8)
24 o = gcat(out, o, "------B\r\nContent-Disposition: form-data; name=\"visibility\"\r\n\r\npublic\r\n" as *u8)
25 o = gcat(out, o, "------B\r\nContent-Disposition: form-data; name=\"pub_search\"\r\n\r\n1\r\n" as *u8)
26 o = gcat(out, o, "------B\r\nContent-Disposition: form-data; name=\"file\"; filename=\"deed.txt\"\r\nContent-Type: text/plain\r\n\r\n" as *u8)
27 o = gcat(out, o, "ESTATE PLANNING deed\r\n\r\nprobate clause: living trusts survive the blank line\r\n" as *u8)
28 o = gcat(out, o, "------B--\r\n" as *u8)
29 return o
30}
31
32func main() -> i64 {
33 gw("=== NX-DOCPORTAL-MULTIPART-GATE -- real <input type=file> upload stores through /admin/upload ===\n" as *u8)
34 let sp: *u8 = "/tmp/nx_mpg_store.log" as *u8
35 let kp: *u8 = "/tmp/nx_mpg_keys.log" as *u8
36 let f1: i64 = sys_openat_wr(sp, 0x180); if f1>=0 {sys_close(f1)}
37 let f2: i64 = sys_openat_wr(kp, 0x180); if f2>=0 {sys_close(f2)}
38 let oprf: *u8=sys_mmap(32); let akp: *u8=sys_mmap(32); let akb: *u8=sys_mmap(33); let edp: *u8=sys_mmap(32); let edb: *u8=sys_mmap(32)
39 if nx_uas_server_keys_load_or_init(kp, oprf, akp, akb, edp, edb) != NX_UAS_OK { gw("KEYS FAIL\n" as *u8); sys_exit(1) }
40 let realm: *u8="nishi_docportal" as *u8; let rn: i64=gln(realm)
41 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
42 if nx_auth_context_init(ctx, realm, rn, realm, rn, sp as i64, oprf, edp, edb, 900, 8192, 1, 1, 5, 1) != NX_MAUTH_OK { gw("CTX FAIL\n" as *u8); sys_exit(1) }
43 let mnb: *u8=sys_mmap(512); let mnn: *i64=sys_mmap(16) as *i64
44 if nx_modern_auth_register(ctx, "elder" as *u8, 5, "correct horse battery staple" as *u8, 28, mnb, 512, mnn) != NX_MAUTH_OK { gw("REG FAIL\n" as *u8); sys_exit(1) }
45 // mint a token
46 let tok: *u8=sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES); let tn: *i64=sys_mmap(8) as *i64; tn[0]=0
47 if nx_modern_auth_login(ctx, "elder" as *u8, 5, "correct horse battery staple" as *u8, 28, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tn) != NX_MAUTH_OK { gw("LOGIN FAIL\n" as *u8); sys_exit(1) }
48 let b64: *u8=sys_mmap(256); let b64n: i64=b64_encode(tok, NX_MAUTH_SESSION_TOKEN_BYTES, b64)
49
50 let body: *u8 = sys_mmap(4096)
51 let blen: i64 = gbuild_body(body)
52 let out: *u8 = sys_mmap(DAD_OUTCAP)
53 var pass: i64 = 0
54
55 // M1: header-auth multipart upload -> 200 UPLOADED domain=andelinwest.com
56 let req: *u8 = sys_mmap(8192)
57 var r: i64 = gcat(req, 0, "POST /admin/uploadfile HTTP/1.1\r\nHost: admin.andelinwest.com\r\nX-Nishi-Session: " as *u8)
58 r = gcatn(req, r, b64, b64n)
59 r = gcat(req, r, "\r\nContent-Type: multipart/form-data; boundary=----B\r\nContent-Length: " as *u8)
60 r = gcatn(req, r, mnb, 0) // (no-op; keep req buffer hot)
61 r = gcat(req, r, "999\r\n\r\n" as *u8)
62 r = gcatn(req, r, body, blen)
63 let o1: i64 = dad_handle(ctx, req, r, out)
64 var m1: i64 = 0
65 if gcontains(out, o1, "UPLOADED domain=andelinwest.com" as *u8) == 1 { if gcontains(out, o1, "cid=" as *u8) == 1 { m1 = 1 } }
66 pass = pass + gcell(m1, "M1" as *u8, "header-auth multipart upload -> 200 UPLOADED domain=andelinwest.com + cid" as *u8)
67
68 // M2: binary/whitespace-safe -- the text AFTER the file's embedded blank line was stored (searchable)
69 let sreq: *u8 = sys_mmap(1024)
70 let sr: i64 = gcat(sreq, 0, "GET /search?q=probate HTTP/1.1\r\nHost: andelinwest.com\r\n\r\n" as *u8)
71 let o2: i64 = dad_handle(ctx, sreq, sr, out)
72 var m2: i64 = 0
73 if gcontains(out, o2, "probate" as *u8) == 1 { m2 = 1 }
74 if gcontains(out, o2, "Estate" as *u8) == 1 { m2 = 1 }
75 if gcontains(out, o2, "estate" as *u8) == 1 { m2 = 1 }
76 pass = pass + gcell(m2, "M2" as *u8, "file stored binary-safe (text AFTER the embedded blank line indexed + searchable)" as *u8)
77
78 // M3: ?s= query-auth multipart upload -> 200 UPLOADED (nishi-first path)
79 let req3: *u8 = sys_mmap(8192)
80 var r3: i64 = gcat(req3, 0, "POST /admin/uploadfile?s=" as *u8)
81 r3 = nx_sa_tok_urlenc(b64, b64n, req3, r3)
82 r3 = gcat(req3, r3, " HTTP/1.1\r\nHost: admin.andelinwest.com\r\nContent-Type: multipart/form-data; boundary=----B\r\n\r\n" as *u8)
83 r3 = gcatn(req3, r3, body, blen)
84 let o3: i64 = dad_handle(ctx, req3, r3, out)
85 var m3: i64 = 0
86 if gcontains(out, o3, "UPLOADED domain=andelinwest.com" as *u8) == 1 { m3 = 1 }
87 pass = pass + gcell(m3, "M3" as *u8, "?s= query-auth multipart upload -> 200 UPLOADED (nishi-first no-cookie path)" as *u8)
88
89 // M4: NEG no session -> 401 (deny-by-default before any store)
90 let req4: *u8 = sys_mmap(8192)
91 var r4: i64 = gcat(req4, 0, "POST /admin/uploadfile HTTP/1.1\r\nHost: admin.andelinwest.com\r\nContent-Type: multipart/form-data; boundary=----B\r\n\r\n" as *u8)
92 r4 = gcatn(req4, r4, body, blen)
93 let o4: i64 = dad_handle(ctx, req4, r4, out)
94 var m4: i64 = 0
95 if gcontains(out, o4, "401" as *u8) == 1 { m4 = 1 }
96 pass = pass + gcell(m4, "M4" as *u8, "no-session multipart upload -> 401 (deny-by-default, nothing stored)" as *u8)
97
98 // M5: NEG multipart with NO file part -> 400
99 let nofile: *u8 = sys_mmap(512)
100 var nfo: i64 = gcat(nofile, 0, "------B\r\nContent-Disposition: form-data; name=\"domain\"\r\n\r\nandelinwest.com\r\n------B--\r\n" as *u8)
101 let req5: *u8 = sys_mmap(2048)
102 var r5: i64 = gcat(req5, 0, "POST /admin/uploadfile HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8)
103 r5 = gcatn(req5, r5, b64, b64n)
104 r5 = gcat(req5, r5, "\r\nContent-Type: multipart/form-data; boundary=----B\r\n\r\n" as *u8)
105 r5 = gcatn(req5, r5, nofile, nfo)
106 let o5: i64 = dad_handle(ctx, req5, r5, out)
107 var m5: i64 = 0
108 if gcontains(out, o5, "400" as *u8) == 1 { if gcontains(out, o5, "no file part" as *u8) == 1 { m5 = 1 } }
109 pass = pass + gcell(m5, "M5" as *u8, "multipart with no file part -> 400 (honest error, not a fake success)" as *u8)
110
111 // M6: NEG missing Content-Type boundary -> 400
112 let req6: *u8 = sys_mmap(2048)
113 var r6: i64 = gcat(req6, 0, "POST /admin/uploadfile HTTP/1.1\r\nHost: x\r\nX-Nishi-Session: " as *u8)
114 r6 = gcatn(req6, r6, b64, b64n)
115 r6 = gcat(req6, r6, "\r\nContent-Type: application/octet-stream\r\n\r\nrawbytes" as *u8)
116 let o6: i64 = dad_handle(ctx, req6, r6, out)
117 var m6: i64 = 0
118 if gcontains(out, o6, "400" as *u8) == 1 { if gcontains(out, o6, "boundary" as *u8) == 1 { m6 = 1 } }
119 pass = pass + gcell(m6, "M6" as *u8, "missing multipart boundary -> 400 (no silent mis-store)" as *u8)
120
121 gw("NX-DOCPORTAL-MULTIPART-GATE pass=" as *u8); let d: *u8=sys_mmap(8); d[0]=(48+pass) as u8; sys_write(1,d,1); gw("/6 verdict=" as *u8)
122 if pass == 6 { gw("GREEN (real file upload stores binary-safe through the unified path; ?s= + header auth; honest errors)\n" as *u8); sys_exit(0); return 0 }
123 gw("RED\n" as *u8); sys_exit(1)
124 return 1
125}