2013-05-29 18 views
6

Di solito considero l'ambito globale come un namespace a cui è sempre possibile accedere da qualsiasi luogo. Vorrei sapere se è teoricamente possibile che nasconda completamente lo scope globale. Ad esempio, supponiamo di avere un codice vorremmo eval UATE (nella console di un browser):JavaScript - Come nascondere lo scope globale dallo script eval'd

var code = 
    "console.log(this); " + // access the global object directly 
    "console.log(window); " + // access the global object as the window object 
    "newGlobalVar = 42; "; // implicitly create global object 
eval(code); 

Ponendo l'invito eval, this e window possono essere nascosti code:

(function (window) { eval(code); }).call({}); 

Ma non riesco a fermare lo code creare implicitamente variabili globali. È possibile in qualche modo? Non voglio usare questa roba, sono solo curioso.

+1

Stai provando a sandbox 'eval'd/codice arbitrario? C'è un modo migliore in giro se questo è il tuo obiettivo. –

+0

No, stavo solo pensando di proteggere lo scope globale dal codice utente. Questo mi ha portato a questa domanda, riguarda la teoria. – kol

+1

Quindi, nei browser moderni, teoricamente, si poteva [congelare] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) l'oggetto 'window'. Anche se non l'ho mai provato. –

risposta

7

Se si sta eseguendo nei browser abbastanza moderno, si può in gran parte di bloccare l'accesso window facendo una funzione che ombreggia le window e self variabili con parametri, e corre il codice in rigorosa modalità.

var obj = {}; 

var func = new Function("self", "window", "'use strict';" + code); 

func.call(obj, obj, obj); 

console.log(obj); // see if there were any attempts to set global variables. 

Qualsiasi tentativo di accedere window o self sarà semplicemente accedere al nostro obj oggetto, e il valore di this sarà anche la nostra obj.

Poiché siamo in modalità rigorosa, non sono consentite le globali implicite. Inoltre, il valore predefinito delle funzioni this sarà undefined anziché window.

Penso che ci siano un paio di hack che possono aggirare questo, ma questo dovrebbe coprire la maggior parte degli scenari.

+1

Sembra che tu possa ancora accedere alle variabili globali. 'code =" return location.href; ";', 'code =" return console; ";', e 'code =" return top; ";' tutti i risultati restituiti. – jongo45

+0

@ jongo45: Sì, puoi accedere ai globali perché sono globali. Ma non puoi fare alcuna manipolazione allo scopo globale. Sono piuttosto sicuro che OP stava cercando di impedire la creazione di nuovi globali, non di bloccare l'accesso a tutti. –

+0

@squint: non vero, top.myfunc = "123"; ti permette di fare console.log (myfunc) dopo che il codice è stato eseguito. Dovrebbe iterare tutte le variabili globali e renderle argomenti prima di chiamare il codice. – Rahly

1

Ho accettato la risposta di @ squint, perché penso che l'utilizzo della modalità rigorosa sia la soluzione.

Ma il problema può anche essere risolto senza una modalità rigorosa. In questo caso, si può memorizzare nella cache le variabili globali, eval il codice, e subito dopo eval restituito, eliminare quelle globali, che non hanno una voce nella cache (variabili implcitly globali possono essere eliminati):

(function() { 
    var cache = []; 
    for (var prop in this) { 
    if (this.hasOwnProperty(prop)) { 
     cache.push(prop); 
    } 
    } 
    (function (window) { try { eval(code); } catch (e) { } }).call({}); 
    for (var prop in this) { 
    if (this.hasOwnProperty(prop) && cache[prop] === undefined) { 
     var success = delete this[prop]; 
    } 
    } 
})(); 

questo non gestisce il caso in cui il codice eval'd sovrascrive una variabile creata in precedenza, implicitamente globale, ma non credo che sia impossibile da risolvere anche questo:

(function() { 
    var cache = {}; 
    for (var prop in this) { 
    if (this.hasOwnProperty(prop)) { 
     cache[prop] = this[prop]; 
    } 
    } 
    (function (window) { try { eval(code); } catch (e) { } }).call({}); 
    for (var prop in this) { 
    if (this.hasOwnProperty(prop)) { 
     var success = delete this[prop]; 
     if (success && cache[prop] !== undefined) { 
     this[prop] = cache[prop]; 
     } 
    } 
    } 
})(); 
4

Nota: questo è ancora un work in progresso e in parte ispirato allo snippet di codice di Squint.

function quarantinedFunction(fnText){ 
    var exceptionKeys=[ 
     "eval","Object", //need exceptions for this else error. (ie, 'Exception: redefining eval is deprecated') 
     "Number","String","Boolean","RegExp","JSON","Date", 
    ]; 
    var forbiddenKeys=[ 
     "fn","fnText","forbiddenKeys","exceptionKeys","empty","oForbiddenKeys", 
    ]; 
    var oForbiddenKeys=Object.create(null); 
    var empty=Object.create(null); 
    Object.freeze(empty); 
    forbiddenKeys.forEach(function(key){ 
     oForbiddenKeys[key]=null; 
    }); 
    [this,self].forEach(function(obj){ 
     Object.getOwnPropertyNames(obj).forEach(function(key){ 
      if(!key.match(/^[\$\w]+$/))return; 
      oForbiddenKeys[key]=null; 
     }); 
    }); 
    exceptionKeys.forEach(function(key){ 
     delete oForbiddenKeys[key]; 
    }); 

    if(0){//debugging. 
     return function(){ 
      return Object.keys(oForbiddenKeys); 
      return Object.keys(empty); 
     }; 
    } 

    fnText=[ 
     '"use strict";', 
     "var "+Object.keys(oForbiddenKeys).join(", ")+";", 
     "{", 
     fnText, 
     "}" 
    ].join("\n"); 

    var fn= (function(){ 
     with(empty) 
     { 
      return new Function("self","window",fnText); 
     } 
    })(); 

    return function(){ 
     return fn.call(Object.create(null));  //self,window undefined 
     return fn.call(empty,empty,empty); //self,window are objects w/o properties 
    }; 
} 

risultati di output (da Firefox scartafaccio):

quarantinedFunction("return location.href;")(); 
/* 
Exception: location is undefined 
*/ 
quarantinedFunction("someGlobalVar=15;")(); 
/* 
Exception: assignment to undeclared variable someGlobalVar 
*/ 
quarantinedFunction("return 9*9;")(); 
/* 
81 
*/ 
quarantinedFunction("return console;")(); 
/* 
undefined 
*/ 

E un jsfiddle con alcuni risultati.

Nota: alcuni risultati imprevisti vengono visualizzati nel violino ma non in altri strumenti (ad es.la variabile location restituisce l'URL della pagina quando il violino viene visualizzato da firefox aurora, ma non su chrome né sullo scratchpad devtool - probabilmente il meccanismo di Firefox __noSuchMethod__ o simile 'late-binding', con conseguente aggiunta di proprietà solo quando si accede).

Problemi correlati