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}