code wiki / _hdl_build / nx_uiq_wire_lib.nx

nx_uiq_wire_lib.nx source

↩ module page · 188 lines · 9342 B

1// nx_uiq_wire_lib.nx -- FROM THE FIRST BYTE UP: the wire/response layer of experiential testing. 2// Every other uiq organ starts at the HTML source or the rendered pixels; this one starts at BYTE 0 of 3// the HTTP response -- the status line + headers the edge emits BEFORE any body. Audits the SOTA 4// security/quality header posture (OWASP Secure Headers / web.dev): Content-Type(+charset), 5// X-Content-Type-Options:nosniff, X-Frame-Options|CSP frame-ancestors (clickjacking), 6// Content-Security-Policy (the defense-in-depth that neutralises stored injection BEFORE escaping), 7// Referrer-Policy, Strict-Transport-Security (HSTS). Parses a captured raw response (status line + 8// headers up to the blank line); the fetch is the already-sovereign nx_https_get (compose, don't rebuild). 9// HONEST ENVELOPE (in output): header PRESENCE + a coarse CSP-strength read; it does NOT execute the CSP 10// or prove the policy is tight -- presence is the floor, not proof. license_tier: ORIGINAL genealogy: experiential wire layer 11import "nx_syscalls.nx" 12import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 13const UW_MAGIC_1024: i64 = 1024 14const UW_MAGIC_8192: i64 = 8192 15const UW_MAGIC_8191: i64 = 8191 16const UW_MAGIC_4096: i64 = 4096 17const UW_MAGIC_4095: i64 = 4095 18 19const UW_SEC_TOTAL: i64 = 5 // the 5 scored security headers 20 21func uw_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 22// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 23// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 24// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 25// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 26func uw_pn(v: i64) -> i64 { nxi_out(v); return 0 } 27func uw_cat(d: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; var o: i64=off; while s[i]!=(0 as u8){d[o]=s[i]; o=o+1; i=i+1} return o } 28func uw_catn(d: *u8, off: i64, v: i64) -> i64 { 29 let t: *u8 = sys_mmap(28); var m: i64=v; var o: i64=off; if m<0 { d[o]=45 as u8; o=o+1; m=0-m } 30 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 } 31 var i: i64=0; while i<k { d[o]=t[k-1-i]; o=o+1; i=i+1 } return o 32} 33func uw_len(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 34func uw_lc(c: i64) -> i64 { if c>=65 { if c<=90 { return c+32 } } return c } 35 36// header block = bytes up to the first \r\n\r\n (or end). returns that length. 37func uw_hdr_end(buf: *u8, n: i64) -> i64 { 38 var i: i64=0 39 while i+3<n { 40 if buf[i]==(13 as u8) { if buf[i+1]==(10 as u8) { if buf[i+2]==(13 as u8) { if buf[i+3]==(10 as u8) { return i+2 } } } } 41 i=i+1 42 } 43 return n 44} 45// case-insensitive: is `name:` present at a line start within [0,hn)? a line start = index 0 or preceded by \n. 46func uw_has(buf: *u8, hn: i64, name: *u8) -> i64 { 47 let ln: i64 = uw_len(name) 48 var i: i64=0 49 while i<hn { 50 var atline: i64=0 51 if i==0 { atline=1 } else { if buf[i-1]==(10 as u8) { atline=1 } } 52 if atline==1 { 53 var k: i64=0 54 var ok: i64=1 55 while k<ln { if i+k>=hn { ok=0; k=ln } else { let a: i64=uw_lc(buf[i+k] as i64); let b: i64=uw_lc(name[k] as i64); if a!=b { ok=0; k=ln } else { k=k+1 } } } 56 if ok==1 { if i+ln<hn { if buf[i+ln]==(58 as u8) { return 1 } } } 57 } 58 i=i+1 59 } 60 return 0 61} 62// does the header VALUE for `name` contain `needle` (case-insensitive)? scans the whole header block for 63// name: then needle within that line. Coarse but sufficient for frame-ancestors / nosniff detection. 64func uw_val_has(buf: *u8, hn: i64, name: *u8, needle: *u8) -> i64 { 65 let ln: i64 = uw_len(name) 66 let nn: i64 = uw_len(needle) 67 var i: i64=0 68 while i<hn { 69 var atline: i64=0 70 if i==0 { atline=1 } else { if buf[i-1]==(10 as u8) { atline=1 } } 71 if atline==1 { 72 var k: i64=0; var ok: i64=1 73 while k<ln { if i+k>=hn { ok=0; k=ln } else { let a: i64=uw_lc(buf[i+k] as i64); let b: i64=uw_lc(name[k] as i64); if a!=b { ok=0; k=ln } else { k=k+1 } } } 74 if ok==1 { if i+ln<hn { if buf[i+ln]==(58 as u8) { 75 var e: i64=i+ln 76 while e<hn { if buf[e]==(10 as u8) { e=hn } else { e=e+1 } } 77 var j: i64=i+ln 78 let lim: i64=e 79 while j+nn<=lim { 80 var m: i64=0; var mok: i64=1 81 while m<nn { let a2: i64=uw_lc(buf[j+m] as i64); let b2: i64=uw_lc(needle[m] as i64); if a2!=b2 { mok=0; m=nn } else { m=m+1 } } 82 if mok==1 { return 1 } 83 j=j+1 84 } 85 return 0 86 } } } 87 } 88 i=i+1 89 } 90 return 0 91} 92func uw_status(buf: *u8, n: i64) -> i64 { 93 var i: i64=0 94 while i<n { if buf[i]==(32 as u8) { let c0: i64=buf[i+1] as i64; let c1: i64=buf[i+2] as i64; let c2: i64=buf[i+3] as i64; return (c0-48)*100 + (c1-48)*10 + (c2-48) } if buf[i]==(13 as u8) { return 0 } i=i+1 } 95 return 0 96} 97 98// audit into out[]: [0]=status [1]=ctype [2]=nosniff [3]=frameguard [4]=csp [5]=referrer [6]=hsts [7]=sec_score 99func uw_audit(buf: *u8, n: i64, out: *i64) -> i64 { 100 let hn: i64 = uw_hdr_end(buf, n) 101 out[0] = uw_status(buf, n) 102 out[1] = uw_has(buf, hn, "content-type" as *u8) 103 out[2] = uw_val_has(buf, hn, "x-content-type-options" as *u8, "nosniff" as *u8) 104 var fg: i64 = uw_has(buf, hn, "x-frame-options" as *u8) 105 if fg==0 { fg = uw_val_has(buf, hn, "content-security-policy" as *u8, "frame-ancestors" as *u8) } 106 out[3] = fg 107 out[4] = uw_has(buf, hn, "content-security-policy" as *u8) 108 out[5] = uw_has(buf, hn, "referrer-policy" as *u8) 109 out[6] = uw_has(buf, hn, "strict-transport-security" as *u8) 110 out[7] = out[2]+out[3]+out[4]+out[5]+out[6] 111 return 0 112} 113 114func uw_slurp(path: *u8, buf: *u8, cap: i64) -> i64 { 115 let fd: i64 = sys_openat_rd(path) 116 if fd<0 { return 0 } 117 var total: i64=0 118 var nrd: i64=sys_read(fd, buf, cap) 119 while nrd>0 { total=total+nrd; if total>=cap { nrd=0 } else { nrd=sys_read(fd, ((buf as i64)+total) as *u8, cap-total) } } 120 sys_close(fd) 121 return total 122} 123 124func uw_emit(label: *u8, out: *i64) -> i64 { 125 let j: *u8 = sys_mmap(UW_MAGIC_1024) 126 var p: i64=0 127 p = uw_cat(j, 0, "{\"organ\":\"nx_uiq_wire\",\"target\":\"" as *u8) 128 p = uw_cat(j, p, label) 129 p = uw_cat(j, p, "\",\"status\":" as *u8) 130 p = uw_catn(j, p, out[0]) 131 p = uw_cat(j, p, ",\"headers\":{\"content_type\":" as *u8) 132 p = uw_catn(j, p, out[1]) 133 p = uw_cat(j, p, ",\"x_content_type_options_nosniff\":" as *u8) 134 p = uw_catn(j, p, out[2]) 135 p = uw_cat(j, p, ",\"frame_guard\":" as *u8) 136 p = uw_catn(j, p, out[3]) 137 p = uw_cat(j, p, ",\"content_security_policy\":" as *u8) 138 p = uw_catn(j, p, out[4]) 139 p = uw_cat(j, p, ",\"referrer_policy\":" as *u8) 140 p = uw_catn(j, p, out[5]) 141 p = uw_cat(j, p, ",\"hsts\":" as *u8) 142 p = uw_catn(j, p, out[6]) 143 p = uw_cat(j, p, "},\"sec_score\":" as *u8) 144 p = uw_catn(j, p, out[7]) 145 p = uw_cat(j, p, ",\"sec_total\":5,\"verdict\":\"" as *u8) 146 if out[7]>=5 { p = uw_cat(j, p, "HARDENED" as *u8) } else { if out[7]>=3 { p = uw_cat(j, p, "PARTIAL" as *u8) } else { p = uw_cat(j, p, "EXPOSED" as *u8) } } 147 p = uw_cat(j, p, "\",\"env\":\"first-byte-up;response-header-presence;coarse-not-policy-exec;fetch=nx_https_get\"}\n" as *u8) 148 sys_write(1, j, p) 149 return 0 150} 151 152func uw_write_kat(path: *u8, body: *u8) -> i64 { 153 let fd: i64 = sys_openat_wr(path, 420) 154 if fd<0 { return 0-1 } 155 var n: i64=0; while body[n]!=(0 as u8){n=n+1} 156 sys_write(fd, body, n); sys_close(fd); return 0 157} 158// selftest: a BARE response = EXPOSED (only content-type); a HARDENED response = 5/5; frame-ancestors 159// counts as frame guard; case-insensitive. 0 = GREEN. 160func uw_selftest() -> i64 { 161 var f: i64=0 162 let bare: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nCache-Control: no-cache\r\n\r\n<html></html>" as *u8 163 uw_write_kat("uwkat_bare.txt" as *u8, bare) 164 let b1: *u8 = sys_mmap(UW_MAGIC_8192) 165 let n1: i64 = uw_slurp("uwkat_bare.txt" as *u8, b1, UW_MAGIC_8191) 166 let o1: *i64 = sys_mmap(64) as *i64 167 uw_audit(b1, n1, o1) 168 if o1[0]!=200 { f=f+1 } 169 if o1[1]!=1 { f=f+1 } 170 if o1[7]!=0 { f=f+1 } 171 let hard: *u8 = "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nX-Content-Type-Options: nosniff\r\nContent-Security-Policy: default-src 'self'; frame-ancestors 'self'\r\nReferrer-Policy: strict-origin-when-cross-origin\r\nStrict-Transport-Security: max-age=63072000\r\n\r\nbody" as *u8 172 uw_write_kat("uwkat_hard.txt" as *u8, hard) 173 let b2: *u8 = sys_mmap(UW_MAGIC_8192) 174 let n2: i64 = uw_slurp("uwkat_hard.txt" as *u8, b2, UW_MAGIC_8191) 175 let o2: *i64 = sys_mmap(64) as *i64 176 uw_audit(b2, n2, o2) 177 if o2[7]!=5 { f=f+1 } 178 if o2[3]!=1 { f=f+1 } 179 if o2[4]!=1 { f=f+1 } 180 let xfo: *u8 = "HTTP/1.1 200 OK\r\nX-FRAME-OPTIONS: SAMEORIGIN\r\n\r\nx" as *u8 181 uw_write_kat("uwkat_xfo.txt" as *u8, xfo) 182 let b3: *u8 = sys_mmap(UW_MAGIC_4096) 183 let n3: i64 = uw_slurp("uwkat_xfo.txt" as *u8, b3, UW_MAGIC_4095) 184 let o3: *i64 = sys_mmap(64) as *i64 185 uw_audit(b3, n3, o3) 186 if o3[3]!=1 { f=f+1 } 187 return f 188}