· 4 years ago · Apr 30, 2021, 01:18 AM
1/*
2
3in-browser client side html5 webproxy environment (sub_env_api.js) (unfinished)
4
5NOTE: this is a work in progress, it is currently unfinished and not fully or at all functional.
6
7This sub-environment api was written by the Transcendentian.
8It is open-source, so you may use it, modify it etc.
9
10right now it (this script) needs (requires) my main.js (https://docs.google.com/uc?id=0Bxb5iFgmM3V6eTRINGZ0Zm1zMkk) to be already (pre) loaded in order for it to run.
11
12The purpose of this api is to load and run webpages and/or resources in a sub-environment of another webpage. Uses include, a proxy page that runs javascript without the website being proxied knowing that it's being proxied, hugely expanded cors capabilities (is disabled in the sub environment) (example: draw a cross-origin image to a canvas and then access ctx.getImageData() );
13
14
15*/
16
17//properly undepricate __proto__:
18(function(w){var O=w.Object,Op=O?O.prototype:!1,po="__proto__",dGS=["__define","etter__"],eP="etPrototypeOf";
19if(!(po in w)){
20Op[dGS.join("G")](po,function(){return O["g"+eP](this);});
21Op[dGS.join("S")](po,function(v){O["g"+eP](this,v);});
22}}(self));
23
24//if(typeof self.phpact!="function")TJA.require("https://docs.google.com/uc?id=0Bxb5iFgmM3V6azZVQmpTb295bGM"); //include 2php.js
25
26//String.prototype.iindexOf=function(s){return this.toLowerCase().indexOf(s.toLowerCase());};//non-case-sensative indexOf
27
28var rp=function(s,f,r){return s.split(f).join(r);},//replace
29
30/* PHP code:
31https://pastebin.com/raw/TQ0Jt7pK
32
33no longer using php code, using https://raw-http-api.thetranscendent.repl.co/ instead
34*/
35
36 p2_="code_g2.php",
37prsHdrs=function(hs){var ho={},h,ci;
38 hs=hs.split("\r\n");
39 while(hs.length){h=hs.pop();if((ci=h.indexOf(": "))+1)ho[h.substr(0,ci).toLowerCase()]=h.substr(ci+2);}
40 return ho;
41},
42allcookies=[],
43HttpReq=function(meh,url,callback,ehdrs,bdy){
44 var cb2=function(r){var bgn=TJA.unArrBuf(r.slice(0,20)),rsp={"ERRS":[],"headers":{},"sc":0};
45 if(bgn.substr(0,4)=="HL: "){var LL=bgn.indexOf("\r\n"),HL=bgn.substring(4,LL)*1;if(HL){
46 var RHL=HL+2+LL,HS=TJA.unArrBuf(r.slice(LL+2,RHL)),
47 fl,CU,rul=[],rdr=HS.split("\r\n\r\n"); rdr.pop();
48
49 rsp.body=r.slice(RHL);
50 if(rdr.length){HS=rdr[rdr.length-1];
51 rdr.slice(1).forEach(function(rd){var lns=rd.split("\r\n"),fl=lns[0].split(" ");CU=fl[1];rul.push(fl[1]);lns.shift();
52 lns.forEach(function(rl){if(rl.length>12&&rl.substr(0,12).toLowerCase()=="set-cookie: "){
53 //save cookies
54 var buf=rl.substr(12),fe=buf.indexOf("="),si=buf.indexOf(";"),dt,buft,ph,tc={"name":buf.substr(0,fe),"value":buf.substring(fe+1,si)};buf=buf.substr(si+1);
55 dt=TJA.gurl(buft=buf.toLowerCase(), "expires=", ";");
56 tc.exp=dt.length?(new Date(dt)).getTime():"sess";
57 if((!dt.length)||(new Date()).getTime()<tc.exp){
58 tc.domain=TJA.gurl(buft, "domain=", ";");
59 if(!tc.domain.length) tc.domain=TJA.gurl(fl[1], "://", "/");
60 tc.path=(ph=TJA.gurl(buf, "path=", ";")).length?ph:fl[1].substr(fl[1].indexOf("/",9));
61 allcookies.push(tc);
62 }
63 }});
64 });
65 rsp.redirects=rul;
66 }
67
68 rsp.currentUrl=CU||url;
69 rsp.headers=prsHdrs(HS);
70 rsp.sc=HS.split("\r\n")[1].split(" ")[1];
71 }else rsp.body=r.slice(LL+2);}
72 callback.call(this,rsp);},
73
74 u2=phpact.bu+"/"+p2_+"/"+url,hdr=[["x-nope","cookie,referer,origin,x-real-ip"]];
75
76 if(typeof ehdrs=="object")for(var H in ehdrs)hdr.push(["X-A-"+H,ehdrs[H]]);
77 TJA.XHR(meh,u2,cb2,hdr,bdy).onerror=function(){var st=this.status;
78 if(st===0||st>390){
79 //phpact('$tmp=fopen("'+p2_+'","w");fwrite($tmp,file_get_contents("https://pastebin.com/raw/TQ0Jt7pK"));fclose($tmp);echo "Did.";', function(r){if((r=TJA.unArrBuf(r))=="Did.")TJA.XHR(meh,u2,cb2,hdr,bdy);else alert("Error: "+r);});
80 //no phpact, using repl now, use start the repl
81 }
82 };
83};
84
85
86
87
88
89
90// BEGIN AWESOME:
91
92var blobs={},haz_url=[], //The blobs object includes fetched files as blobs, with the 'real' url as the property name. use blobs[url].burl to get a blob url of a cached file; haz_url is an Array of Element constructors that need href or src, each item has format {"n":constructor_funct(){}.name,"m":"href"||"src"}
93
94Omap=new WeakMap(); //match iframes to their 'real' origins, url-fetching HTML elements to their real urls
95
96/*note: this section is part of the "engine" code,
97it will only run one time when the proxy system is loading
98then the virtual proxied pages that are running in the proxy system will all rely on
99functions and properties that are defined in the "engine"
100*/
101
102/* use new Proxy() insteand of fantasmic
103fantasmic=function(O,p,rgf,rsf,o2){ //O==object, p=property, rgf=replace get function, rsf=replace set function;
104var od=Object.getOwnPropertyDescriptor(O,p),y,G=od.get,S=od.set;
105if(y=typeof rgf=="function")od.get=function(){rgf.call(this,G);};
106if(y=typeof rsf=="function")od.set=function(v){rsf.call(this,S,v);};
107if(y)Object.defineProperty(o2||O,p,od);
108//please note, rgf & rsf accept an extra argument, the first, the old function they replace, and in rsf the set-to value is the 2nd argument instead of the first like a regular getter
109return od;},
110fillN=function(to,so,not){not=not||{}; //put fill'n in some objects (use to make passthrus)
111 //forget it, use new Proxy()
112},
113siMLp=function Simple_ML_parser(s){
114 //forget it, use nullIframe
115},
116siMLs=function Simple_ML_stringify(ML){//forget it, use nullIframe
117}; */
118
119//need src (get/set)ter: HTMLVideoElement,HTMLTrackElement,HTMLSourceElement,HTMLScriptElement,HTMLMediaElement,HTMLInputElement,Image,HTMLImageElement,HTMLIFrameElement,HTMLFrameElement,HTMLEmbedElement,Audio,HTMLAudioElement,webkitSpeechGrammar
120//need href /\ : URL,StyleSheet,SVGUseElement,SVGTextPathElement,SVGScriptElement,SVGRadialGradientElement,SVGPatternElement,SVGLinearGradientElement,SVGImageElement,SVGGradientElement,SVGFilterElement,SVGFEImageElement,SVGAElement,HTMLLinkElement,HTMLBaseElement,HTMLAreaElement,HTMLAnchorElement,CSSStyleSheet,CSSImportRule,SVGMPathElement
121
122
123/*note: the .href and .src and form.action properties always returns the "effective" url in javascript even if full URL is not set
124ex: anchor1.href="/aa";
125anchor1.href will now return "https://www.example.com/aa"
126
127
128HTMLBaseElement can fix this for all except form.action
129
130
131
132
133//awesome works:
134self.a1=["a","b","c"];
135var [do1,v2,v3]=a1;
136
137do1
138"a"
139v2
140"b"
141v3
142"c"
143
144*/
145
146var ref_ifr=document.createElement("iframe"); //use this as a reference for a browsers window's normal state
147document.body.appendChild(ref_ifr);ref_ifr.style.display="none";
148(function(){ //main iframe (use to get initial window properties)
149 var ins=new WeakSet(),cw=ref_ifr.contentWindow,ifrp=Object.getOwnPropertyNames(cw);
150 for(let v of ifrp){var V=cw[v],Vp,m;if(typeof V=="function"&&(Vp=V.prototype)&&v!="HTMLAnchorElement"&&((m="src" in Vp)||"href" in Vp)){m=m?"src":"href";
151 var pd=Object.getOwnPropertyDescriptor(Vp,m);
152 if(pd&&(!ins.has(pd))){pd.n=v;pd.m=m;haz_url.push(pd);ins.add(pd);}
153 }else if(v.substr(0,2)=="on"){}
154 }
155})();
156
157//don't 4get HTMLFormElement.action
158
159
160
161//note: a global, real-top-level function that takes the sub_env window's "self" object & 'fake' url as an arguments and outputs any array of: [alt_self,new_ndp]
162//will include at beggining of all sub-page <head> s:
163var sph_bgn=function(SELF,_url){ //include document.sub_env=sph_bgn(self,"url to show in js"); contents at beggining of <head> of fetched html, because of document.location, the document object must be replaced with a passthru (Proxy) copy, so simply don't include sub_env in the passthru
164//
165
166var alt_self={"document":{"__proto__":SELF.document.__proto__},"top":{/*top it off*/}, /* << to name a few; wait.. make this a pass thru 2 the real stuff, filtering out only location/url related stuff. Don't 4get to filter src, href, and therefore (get/set)Attribute. make document.origin && self.origin (get/set)ers*/
167 "location":{"__proto__":Location,"ancestorOrigins":{"__proto__":DOMStringList,"length":0},"assign":function(v){location2.s(SELF.location.assign,v);}}
168},
169
170new_ndp=function(so){ //new non-deleteable-properties; source-object; (replaces 'var' declaration); like Object.assign(self,{}) except better
171 for(var p in so){Object.defineProperty(alt_self,p,{value:so[p],writable:!0,enumerable:!0});}
172};
173
174 //note: may use random names for those 2 vars in altered sub_env code /\ /\
175
176 if("sessionStorage" in SELF){//sessionStorage is full url including ? querry but not #hash; localStorage is origin only including protocol
177 Object.assign(alt_self.location)
178 }
179
180 var wait_=function(f){if(typeof f=="function")while(f())!0;},
181
182 KNOT=("location,document,origin,self,window,top").split(","),
183 vaaAaar=function(P){if(KNOT.indexOf(P)<0&&(!(P in SELF)))Object.defineProperty(SELF,P,{"get":function(){return alt_self[P];},"set":function(v){alt_self[P]=v;}});
184 },
185
186
187 /*NOTE: use new URL(path,base); https://developer.mozilla.org/en-US/docs/Web/API/URL to create a base object to help make the new fake location object; have no way for scripts running in sub env to run location="http://example.com", only self.location="http://example.com" will work because of the way get/setters are defined
188
189maybe use something like
190const location={};
191try{ loction="a" }catch(e){ var stk=e.stack.substr(e.stack.indexOf(" at ")+4).split(":"); [functionname,line,character]
192//find and manually assign location
193}
194
195
196 Object.keys(URL.prototype)
197(14) ["href", "origin", "protocol", "username", "password", "host", "hostname", "port", "pathname", "search",
198"searchParams",
199"hash",
200"toJSON",
201"toString"]
202Object.keys(location)
203(14) [
204"replace", "assign",
205"href",
206"ancestorOrigins",
207"origin", "protocol", "host", "hostname", "port", "pathname", "search", "hash", "reload", "toString"]
208
209
210
211
212 don't 4get:
213 location.toString=function(){ return "proxied location"; };
214
215 so that location+"" will produce expected output
216 */
217
218
219
220 location2={"s":function(oLs,url){ /*use a general location change function for this & for link click*/ },"g":function(oLg){ /*location get function for document.location*/ return alt_self.location;}},
221 Elp=SELF.Element.prototype,
222 EvTp=SELF.EventTarget.prototype,
223 oldie={aEvntLst:Elp.addEventListener,rEvntLst:Elp.removeEventListener,/*gEvntLsts:SELF.getEventListeners,*/pstMsg:SELF.postMessage,
224 inrHTML:Object.getOwnPropertyDescriptor(Elp,"innerHTML"),
225 sAtr:Elp.setAttribute,gAtr:Elp.getAttribute},
226
227 ClkE_=function(e){var tar=e.target||e.srcElement; if(tar.nodeName=="A"){e.preventDefault();
228 //do link stuff (make main page fetch link resource); I can use this so that I don't have to change Anchor Element "href"s
229 }};
230
231 SELF.addEventListener("click",ClkE_,!1); //catch link-click in event bubble phase
232
233//fourtunately oldie.gEvntLsts returns a regular Object with each event type that has listener(s) as a regular Array, of regular Objects like: {listener:func, useCapture: false, passive: false, once: false, type: "message"}
234// no no no , we don't do getEventListeners because it's not a 'real' function, it is a dev-tools api only
235
236
237(EvTp.addEventListener=function(e,f,c){if(e=="message"){
238 //do something with e.source (the source window) to get fake origin?
239 var F_=function(E){/* call f with element as 'this' */},use_=Omap.get(f);F_.real=f;if(!use_)Omap.set(f,use_={'elms':(new WeakMap()),'n':0}); if(!use_.elms.has(this)){use_.elms.set(this,F_);use_.elms.n++;}
240 oldie.aEvntLst.call(this,e,F_,c); //woah, a 2 diminsional WeakMap [Listener].elms[TargetElement]
241}else oldie.aEvntLst.apply(this,arguments);}).toString=SELF.toString.bind(oldie.aEvntLst);
242(EvTp.removeEventListener=function(e,f,c){if(e=="message"){
243 //remove my hidden function
244 var elms=Omap.get(f).elms,hf; if(elms){
245 oldie.rEvntLst.call(this,e,hf=elms.get(this),c);
246 if(hf){elms.n--; //count instances of a function on different elements and remove the function key in Omap when there are no Listeners Listening
247 elms.delete(this); if(elms.n<1)Omap.delete(f); } }
248}else oldie.rEvntLst.apply(this,arguments);
249}).toString=SELF.toString.bind(oldie.rEvntLst);
250/*(SELF.getEventListeners=function(){Lrs=oldie.gEvntLsts.apply(this,arguments);
251 if(Lrs.message)Lrs.message.forEach(function(v,i,a){a[i]=v.real;});
252 if(Lrs.click)Lrs.click=Lrs.click.clean(ClkE_); //clean is from main.js
253return Lrs;}).toString=SELF.toString.bind(oldie.gEvntLsts);*/
254
255
256 //this function \/ helps 'declare' global variables (need because of self.newvarname=1;) << I don't think this works
257 var DkLrI=setInterval(function DkLr(){
258 for(var p in alt_self)vaaAaar(p);
259 },0);
260
261
262Object.defineProperty(alt_self.document,fantasmic(self,"location",location2.g,location2.s,alt_self));
263
264
265 var atrd=function(A,T){var a=["src","href"],M;return ((M=a.indexOf(A.toLowerCase()))+1)&&T.nodeName!="A"?a[M]:!1;},
266 g_hs=function(rgf,at){ /*do get href/src*/ },
267 s_hs=function(rsf,url,at){ /*do set href/src*/ }; //'at' means is attribute, element is 'this'
268 (Elp.setAttribute=function(a,v){var m;if(m=atrd(a,this)){
269 s_hs.call(this,oldie.sAtr,v,m);
270 }else oldie.sAtr.call(this,a,v);}).toString=SELF.toString.bind(oldie.sAtr);
271 (Elp.getAttribute=function(a){var m,r;if(m=atrd(a,this)){
272 r=g_hs.call(this,oldie.gAtr,m);
273 }else r=oldie.gAtr.call(this,a,v);return r;}).toString=SELF.toString.bind(oldie.gAtr);
274
275 haz_url.forEach(function(O){
276 fantasmic(SELF[O.n],O.m,g_hs,s_hs);
277 });
278
279 //also fix href/src & extra end & beggining line (scripts only) in innerHTML outerHTML (use Elp which is Element.prototype) childNodes & document.all clean extra script element out; don't 4get XMLHttpRequest; Use WeakMap. (get set has delete)() to set objects as keys (this will come in handy for storing inaccesable value data associated to an object & property) : var name Omap
280
281 /* 'target' Element property needs to be checked by functions that work with <A> and <FORM> elements
282_blank Opens the linked document in a new window
283_self Opens the linked document in the same frame as it was clicked (this is default)
284_parent Opens the linked document in the parent frameset
285_top Opens the linked document in the full body of the window
286framename Opens the linked document in a named iframe
287*/
288
289
290return [alt_self,new_ndp];
291},
292
293
294rel_pth=function(r,p){
295 var iOf="indexOf",S="substr",bgn=function(a,s){return a[iOf](s)===0;},rci=r[iOf]("://"),ptc=r[S](0,rci),afptc=r[S](rci+3),host=afptc[S](0,afptc[iOf]("/")),domn=ptc+"://"+host,qi=r[iOf]("?");
296 if(bgn(p,"http://")||bgn(p,"https://")||bgn(p,"data:")||bgn("about:")) return p;
297 if(bgn(p,"?")) return ((qi+1)?r[S](0,qi):r)+p;
298 if(p[0]=="/") return domn+p;
299 if(bgn(p,"://")) return ptc+p;
300 if(r[r.length-1]!="/") r=r[S](0,r.lastIndexOf("/")+1);
301 while(bgn(p,"../")){if(r[r.length-1]=="/")r=r[S](0,r.length-1);p=p[S](3);if(r.length>(domn.length+3))r=r[S](0,r.lastIndexOf("/")+1);}
302 return r+p;
303};
304
305
306//use anonymous function for context (will need to fix 'global var' declarations so they attach to the new global object):
307// ^ ^ do that by replacing string of var declarations with JSON, ex: var a=21,be="bee",c=['hi']; translates to: new_ndp(self,{a:21,be:"bee",c:['hi']});
308
309
310var trapper_func=
311(function(){const self=alt_self,document=self.document; var window=self,location=self.location; //finish this line
312 //insert fetched webpage <script> code here ( use these 2 lines \/ /\ as wrapper @ beggining & and of <script> )
313})();
314
315
316//note: beware of: iframe.contentWindow & contentDocument & window.open() & document. open, write, close () & document.referrer & new Worker() & XMLHttpRequest; For include, just use the <script> wrapper inside included scripts to.
317//don't forget to fake "blocked cross origin frame" errors
318
319//This turning out to be a really awesome sub-environment API
320
321/*
322definer func = trapper func
323one instance of the "document, self, location" proxies (new Proxy) for every fake "window/tab" instance
324access within the definer (sets the proxy objects to replace the real objects) inside the definer annoymouse function
325(the function used to loophole redefining those built-in objects)
326by using throw-away object properties that get deleted on the next line
327for vars that get defined by snared code (what's running on/in the proxy that is being tricked by the proxy) inside my definer function
328to be accessible on the "self" object proxy, use another annoymouse function inside the definer function with eval inside it and
329the object proxy can access all the eval-in-definer instances
330don't forget to put "var arguments=undefined;" at beggining of definer function
331
332*/
333
334
335(function(){
336//use this to parse html and extract proxy modifications from .innerHTML or .outerHTML
337
338var nullIframe=document.createElement("iframe");
339document.body.appendChild(nullIframe);
340var HtmlParse=nullIframe.contentDocument;
341nullIframe.remove();
342
343var real=Object.getOwnPropertyDescriptors(Element.prototype),
344rplProp=["src","href","origin"];
345
346Elp.__defineGetter__("innerHTML",function(){
347 var Huse=(this.nodeName=="HTML")?HtmlParse.documentElement:HtmlParse.body;
348 Huse.innerHTML=real.innerHTML.get.call(this);
349 for(let elm of Huse.all){
350 if(elm.nodeName=="SCRIPT"){ let iH=elm.innerHTML;
351 if(iH.length>3)elm.innerHTML=iH.substring(iH.indexOf("\n")+1,iH.lastIndexOf("\n")); //take out 1st and last lines because they contain my proxy code
352 }else if(elm.nodeName=="FORM"){
353 elm.action=rl2prxy.URL(elm.action); //replace real url with the url I want the proxied code to think it's running at
354 }
355 }
356 return Huse.innerHTML;
357});
358
359})();
360
361