程序员人生 网站导航

用JScript RuntimeObject检测全局污染

栏目:jscript时间:2013-12-30 12:14:58

这是一篇关于使用JScriptRuntimeObject(MSDN)调试的文章。虽然这些例子中的大多数在其他浏览器中不能运行,但在IE 5.5+中都能运行。

泄露的全局标识符

比如说你一不小心创建了一个全局属性,如:

function playRugby(players) {
var items,
i;
len = items.length; // Global.
}
function kick() {
var x = 10
y = 11; // As I makes y global.
}

当playRugby被调用时,全局属性len被创建,如果它尚未存在,那么就将items.length的值赋给它。同样,当kick被调用时,全局属性y被创建。

这些全局变量都不是故意的。它们破坏了封装并泄露了执行的细节。这可能会导致冲突和棘手的依赖问题。

要检测这些不经意间创建的全局标识符,我们可以使用for in循环全局对象。Firebug的“DOM”标签提供了这个实用的全局检测。

遗憾的是,在IE中,for in不能枚举任何全局变量和函数声明。看看下面的例子:

// Property of global variable object.
var EX1_GLOBAL_VARIABLE = 10;
// Property of global object.
this.EX1_GLOBAL_PROPERTY = 11;
// Property of global variable object.
function EX1_GLOBAL_FUNCTION(){}
(function(){
var results = [];
for(var p in this) {
results.push(p);
}
alert("Leaked:" + results.join(""));
})();// Property of global variable object.
var EX1_GLOBAL_VARIABLE = 10;
// Property of global object.
this.EX1_GLOBAL_PROPERTY = 11;
// Property of global variable object.
function EX1_GLOBAL_FUNCTION(){}
(function(){
var results = [];
for(var p in this) {
results.push(p);
}
alert(“Leaked:” + results.join(“”));
})();

在IE中,结果包含一个window属性组合和用户定义的四个属性之一:EX1_GLOBAL_PROPERTY 。

那么,其它三个用户定义的属性发生了什么?为什么它们不能在for in循环中显示出来。

事实证明,枚举全局对象时将枚举已赋值的全局对象属性,而不枚举全局变量。

为什么全局属性能枚举而全局变量不能。经验告诉我们,JScript 给全局变量(用var声明)打上了DontEnum标记。由于全局对象是作为全局变量对象来定义的,这看起来似乎是一个合理的解释。这并不标准,但能解释IE中的行为。不过,Eric Lippert 提出了另一种解释:全局对象和全局变量对象是IE中两个不同的对象。

根据MS-ES3:

JScript 5.x 中的变量声明创建了全局对象的属性,该属性拥有DontEnum 特性。

枚举方案:JScript RuntimeObject

使用JScript RuntimeObject方法枚举全局属性与枚举全局对象不同,你将使用一个正常的执行,枚举由全局RuntimeObject方法返回的一个对象。

var GLOBAL_VAR1,
GLOBAL_VAR2,
GLOBAL_VAR3 = 1;
GLOBAL_PROP1 = 12;
function GLOBAL_FUNCTION(){}
if(this.RuntimeObject){
void function() {
var ro = RuntimeObject(),
results = [],
prop;
for(prop in ro) {
results.push(prop);
}
alert("leaked:" + results.join(""));
}();
}var GLOBAL_VAR1,
GLOBAL_VAR2,
GLOBAL_VAR3 = 1;
GLOBAL_PROP1 = 12;
function GLOBAL_FUNCTION(){}
if(this.RuntimeObject){
void function() {
var ro = RuntimeObject(),
results = [],
prop;
for(prop in ro) {
results.push(prop);
}
alert(“leaked:” + results.join(“”));
}();
}

IE中的结果

在IE8和其它较低版本中,结果包括GLOBAL_FUNCTION,GLOBAL_VAR3,GLOBAL_PROP1 (除此之外,还包括window)。注意GLOBAL_VAR1 和GLOBAL_VAR2 并不包含其中。看来RuntimeObject并不收集未被赋值的任何变量,根据微软的文档,这不是指定的行为(以下有更多这方面的信息)。

微软的RuntimeObject文档

RuntimeObject是JScript 内置的扩展,JScript定义了七个附加的内置全局方法:ScriptEngine,ScriptEngineBuildVersion,ScriptEngineMajorVersion,ScriptEngineMinorVersion,CollectGarbage,RuntimeObject和GetObject。这些对象都是本地JScript对象,不要与宿主对象混淆。

对于RuntimeObject,微软的JScript扩展MS-ES3EX声明如下:

RuntimeObject用来寻找一个全局变量的属性,这些带有名称的属性匹配特定的模式。这个函数只寻找全局对象中通过VariableStatement 或 FunctionDeclaration方式显式创建的属性,或是位于运算符左侧作为标识符隐式创建的属性。不能寻找通过访问全局对象显式创建的属性。

粗略的测试结果表明微软的文档是不靠谱的。

返回的对象不包括添加到变量对象之上的所有标识符,只是那些被赋值的标识符。无论它们是通过VariableDeclaration,FunctionDeclaration来创建,还是作为全局属性声明来创建都无关紧要。

查找通过FunctionBindingList创建的标识符示例

在JScriptFunction的FunctionBindingList 中,所有标识符将成为包含的变量对象的属性,如:

var foo = {}, undef, ro;
(function(){ function foo.bar, baz(){} })();
ro = RuntimeObject();
alert([ro.foo.bar, "undef" in ro].join(""));var foo = {}, undef, ro;
(function(){ function foo.bar, baz(){} })();
ro = RuntimeObject();
alert([ro.foo.bar, "undef" in ro].join(“”)); 

IE elerts

function foo.bar(){}
false在除了IE的浏览器中运行JScript,在解析JScriptFunction 的FunctionBindingList时会如期地抛出SyntaxError(语法错误)。这是意料之中的,因为它是一个语法扩展。

书签

javascript:(function() {var ro=RuntimeObject(),r=[],i=0,p;for(p in ro){r[i++]=p;}alert('leaked:'+r.join(''));})();JScript语法扩展
在较早的“查找由FunctionBindingList创建的标识符”的例子中提到过JScript扩展--JScriptFunction。这个名字不是一个破绽,它是JScript语言的扩展,JScriptFunction 如下:

JScriptFunction : function FunctionBindingList ( FormalParameterListopt ) { FunctionBody }

RuntimeObject(filterString): The filterString Parameter

RuntimeObject方法接受一个可选的过滤字符去匹配标识符。遗憾的是,filterString 不能转换成正则表达式,而是用可选的leftWild和rightWild来进行子匹配默认为“*”。

这意味着filterString = “a*” 将匹配标识符a和a1,而不是ba 。

结论

抛开文档的bug和缺点来说,RuntimeObject 提供了一个有用的替代方案来解决JScript中枚举全局属性的问题。RuntimeObject的优点在于它仅包括用户定义的属性,全局的window属性除外。

前面提到的书签提供了一个简便的方法去查看一个页面中不经意间创建的全局属性(它也表明这个站点也不是全局对象保持清洁的光辉典范)。

RuntimeObject的其它应用

跨浏览器标识泄露书签

相对于IE中标识泄露检测,写一个跨浏览器标识泄露检测是下一个逻辑步骤。

自动标识符泄露检测

意外的全局标识符检测应该是自动匹配的。

YUI Test 的单元测试框架为TEST_CASE_BEGIN_EVENT 和TEST_CASE_COMPLETE_EVENT 提供了钩子。这些事件用来检测RuntimeObject,并在程序代码运行期间捕捉泄露的标识符。

在TEST_CASE_BEGIN_EVENT 中检测RuntimeObject 并存储结果。在TEST_CASE_COMPLETE_EVENT 中再次检测RuntimeObject 并与TEST_CASE_BEGIN_EVENT中存储的结果比较。接下来,对于每一个出现在TEST_CASE_COMPLETE_EVENT中而并没有出现在TEST_CASE_BEGIN_EVENT中的每属性,一个全局标识符已被泄露,一个测试案例的警告就会记录下来。

参考

[MS-ES3EX]: Microsoft JScript Extensions to the ECMAScript Language Specification Third Edition.
原文地址:http://dhtmlkitchen.com/?category=/Browsers/&date=2010/04/11/&entry=Detecting-Global-Pollution-with-the-JScript-RuntimeObject

转载地址:http://www.denisdeng.com/?p=1043

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐