code wiki / _hdl_build / nx_web_harden.nx

nx_web_harden.nx source

↩ module page · 91 lines · 6812 B

1// nx_web_harden.nx -- "90s-NEUTRAL" attack-vector neutralizer. The balance current tools miss: ad blockers STRIP 2// ads (site loses revenue + hostile sites detect the block and wall you); sandboxes BLOCK the page (dead). We do 3// neither -- we RENDER the site and its ads intact (display impressions still fire, the operator still gets paid) 4// and remove ONLY the weaponization: 5// * forced downloads -- programmatic <a download>.click() and clicks/navigations to a binary payload 6// * popunders/popups -- window.open() -> inert decoy (site believes it opened one) 7// * navigate-to-binary -- location.assign/replace to .exe/.apk/... no-op 8// * beforeunload hijack -- the "are you sure you want to leave" nav-trap, silenced 9// * meta-refresh-to-payload + meta-CSP -- http-equiv neutralized so the parser ignores it 10// Injected as the FIRST <script> in <head> so it wins before the page's own scripts run, in the user's REAL 11// browser (Chrome/Firefox) -- so this is ordinary, fully-supported browser JS. license_tier: ORIGINAL 12import "nx_syscalls.nx" 13 14func wh_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 15func wh_put(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){dst[off]=s[i];off=off+1;i=i+1} return off } 16func wh_lc(c: i64) -> i64 { if c>=65 { if c<=90 { return c+32 } } return c } 17// case-insensitive: is `lit` present at src[pos..]? 18func wh_ci_at(src: *u8, slen: i64, pos: i64, lit: *u8, litlen: i64) -> i64 { 19 if pos+litlen>slen { return 0 } 20 var i: i64=0 21 while i<litlen { if wh_lc(src[pos+i]&0xff)!=wh_lc(lit[i]&0xff) { return 0 } i=i+1 } 22 return 1 23} 24// case-insensitive substring find from `from`; -1 if absent. 25func wh_ci_find(src: *u8, slen: i64, lit: *u8, litlen: i64, from: i64) -> i64 { 26 var i: i64=from; let last: i64=slen-litlen 27 while i<=last { if wh_ci_at(src,slen,i,lit,litlen)==1 { return i } i=i+1 } 28 return 0-1 29} 30 31// build the neutralizer <script> (real-browser JS -- no regex/backslash; string methods only; no '!'/'#'). 32func wh_build_preamble(out: *u8, cap: i64) -> i64 { 33 var o: i64 = 0 34 o = wh_put(out, o, "<script>(function(){try{" as *u8) 35 // dangerous payload extensions -> bad(url) test (path only, lower-cased, query/anchor stripped) 36 o = wh_put(out, o, "var BAD=['.exe','.apk','.dmg','.msi','.scr','.bat','.cmd','.com','.jar','.iso','.zip','.rar','.7z','.msix','.appx','.pkg','.deb','.xpi','.crx','.hta','.vbs','.ps1'];" as *u8) 37 o = wh_put(out, o, "function bad(u){u=(''+(u||'')).toLowerCase();var q=u.indexOf('?');if(q>=0){u=u.slice(0,q)}var h=u.indexOf('&');if(h>=0){u=u.slice(0,h)}for(var i=0;i<BAD.length;i++){var e=BAD[i];if(u.length>=e.length){if(u.slice(u.length-e.length)===e){return true}}}return false}" as *u8) 38 // 1) forced download: programmatic click() on <a download> or an anchor to a binary -> swallow (site sees no error) 39 o = wh_put(out, o, "var _cl=HTMLElement.prototype.click;HTMLElement.prototype.click=function(){try{if(this&&this.tagName==='A'){if(this.hasAttribute('download')){return}if(bad(this.href)){return}}}catch(e){}return _cl.apply(this,arguments)};" as *u8) 40 // 2) popunder / popup-to-download -> inert decoy window 41 o = wh_put(out, o, "window.open=function(){return{closed:false,close:function(){},focus:function(){},blur:function(){},postMessage:function(){},location:{href:'',assign:function(){},replace:function(){}},document:{write:function(){},writeln:function(){},close:function(){},open:function(){}}}};" as *u8) 42 // 3) navigate-to-binary via location.assign / location.replace 43 o = wh_put(out, o, "try{var L=window.location;var _as=L.assign.bind(L);var _rp=L.replace.bind(L);L.assign=function(u){if(bad(u)){return}return _as(u)};L.replace=function(u){if(bad(u)){return}return _rp(u)};}catch(e){}" as *u8) 44 // 4) beforeunload navigation-hijack / leave-nag 45 o = wh_put(out, o, "window.addEventListener('beforeunload',function(e){try{e.stopImmediatePropagation();e.returnValue=undefined}catch(x){}},true);" as *u8) 46 // 4b) forms that auto-submit to a binary payload (another forced-download path) 47 o = wh_put(out, o, "try{var _sb=HTMLFormElement.prototype.submit;HTMLFormElement.prototype.submit=function(){try{if(this&&bad(this.action)){return}}catch(e){}return _sb.apply(this,arguments)};}catch(e){}" as *u8) 48 // 5) strip download attrs from anchors created at runtime (covers dynamically-injected forced-download links) 49 o = wh_put(out, o, "try{var strip=function(root){try{if(root.tagName==='A'){root.removeAttribute('download')}if(root.querySelectorAll){var d=root.querySelectorAll('a[download]');for(var k=0;k<d.length;k++){d[k].removeAttribute('download')}}}catch(e){}};" as *u8) 50 o = wh_put(out, o, "new MutationObserver(function(ms){for(var i=0;i<ms.length;i++){var a=ms[i].addedNodes;for(var j=0;j<a.length;j++){strip(a[j])}}}).observe(document.documentElement||document,{childList:true,subtree:true});" as *u8) 51 o = wh_put(out, o, "if(document.addEventListener){document.addEventListener('DOMContentLoaded',function(){strip(document)})}}catch(e){}" as *u8) 52 o = wh_put(out, o, "}catch(e){}})();</script>" as *u8) 53 out[o]=0 as u8 54 return o 55} 56 57// copy src[s..e) into out at off, neutralizing every ci "http-equiv" -> "data-equiv" (equal length) so the parser 58// ignores meta-refresh (payload redirect) AND meta-CSP (which would otherwise block our injected script). Bounded. 59func wh_copy_scrub(out: *u8, off: i64, cap: i64, src: *u8, s: i64, e: i64) -> i64 { 60 var i: i64 = s 61 while i < e { 62 if wh_ci_at(src, e, i, "http-equiv" as *u8, 10) == 1 { 63 if off < (cap-11) { off = wh_put(out, off, "data-equiv" as *u8) } 64 i = i + 10 65 } else { 66 if off < (cap-1) { out[off]=src[i]; off=off+1 } 67 i = i + 1 68 } 69 } 70 return off 71} 72 73// produce the served page: inject the neutralizer right after the opening <head> (so it runs first), scrubbing 74// http-equiv throughout. The whole page -- ads, images, iframes, styles, scripts -- is otherwise preserved. 75func wh_neutralize(out: *u8, cap: i64, body: *u8, blen: i64, pre: *u8, prelen: i64) -> i64 { 76 var ins: i64 = 0 77 let hp: i64 = wh_ci_find(body, blen, "<head" as *u8, 5, 0) 78 if hp >= 0 { 79 var g: i64 = hp 80 while g < blen { if (body[g]&0xff)==62 { ins = g+1; g = blen } else { g = g+1 } } 81 } else { 82 let tp: i64 = wh_ci_find(body, blen, "<html" as *u8, 5, 0) 83 if tp >= 0 { var g: i64 = tp; while g < blen { if (body[g]&0xff)==62 { ins = g+1; g = blen } else { g = g+1 } } } 84 } 85 var o: i64 = 0 86 o = wh_copy_scrub(out, o, cap, body, 0, ins) 87 var p: i64 = 0; while p < prelen { if o < (cap-1) { out[o]=pre[p]; o=o+1 } p=p+1 } 88 o = wh_copy_scrub(out, o, cap, body, ins, blen) 89 out[o]=0 as u8 90 return o 91}