2013-10-31 206 views
3

我知道很多人认为“eval是邪恶的”,但我必须完成某些事情,而且我很难找出如何在没有eval()的情况下做到这一点。有没有一种方法来操纵包含JS没有eval()?

情况是这样的:一个外部文件(我无法控制它 - 编辑:但它不是用户生成的。它来自一个值得信赖的来源!我想这是重要的)正在吐出JavaScript供我使用。这个JavaScript包含了一些很好的JSON数据(这是我需要得到的),但它的两侧是普通的JavaScript语句,声明变量和调用函数等。它看起来有点像这样:

var foo = new Object(); 
foo['KEY'] = {Field1: 'Value1', Field2: 'Value2'}; 
eval('fooFunction(foo)'); 

如果我的eval()这样,我就可以解析富[“关键”],并用它做。我可以认为没有eval()的唯一方法就是使用一堆令人讨厌的replace()方法,这似乎不太好。我错过了一些明显的方式来做到这一点?大多数“你不必使用eval()”替代方案我通常会看到假设我完全控制了一切,但在这种情况下,我必须解决这个现有的代码。

编辑:我应该补充说,这段代码是通过代理脚本(跨域的东西)的AJAX调用获得的,所以没有任何变量可以访问。如果他们是,我显然只能解析foo ['KEY']并且快乐。

第二次编辑:没有定论!我越来越危险地认为eval()是要走的路。你能忍受这个结果吗?我即将屈服于邪恶()。有人阻止我,因为这看起来是唯一的方法。

+1

'如果我的eval()this' - 你想EVAL东西是已经evaling别的东西吗? O.o此外,“替换”肯定更安全(因此:更好),尤其是当您无法控制脚本时。 – freakish

+0

我知道这似乎很愚蠢,但嘿,这就是为什么我在这里。 Re:替换。这将涉及五到六个替代品,很可能包括替换\ r和\ n以及其他类似的东西。超乎笨重。总得有个更好的方法。 –

+0

为什么不只是在'{'和'}'之间寻找任何东西并尝试解析为JSON?用正则表达式应该很简单,不需要替换。 :) –

回答

2

外部代码更好地发回有效的JSON。您的示例中的值不是有效的JSON,因为键必须用双引号包装。

我想出了一个小的纯JavaScript解析器,它可以通过自己添加双引号来处理简单的无效JSON。它目前不支持非字符串值。

function ParseRawJSON(rawCode) { 
    var arrCandidates = []; 
    var lastOpenBracketIndex = -1; 
    for (var i = 0; i < rawCode.length; i++) { 
     var curChar = rawCode.charAt(i); 
     if (curChar === "}") { 
      if (lastOpenBracketIndex >= 0) { 
       arrCandidates.push(rawCode.substr(lastOpenBracketIndex, i - lastOpenBracketIndex + 1)); 
       lastOpenBracketIndex = -1; 
      } 
     } else if (curChar === "{") { 
      lastOpenBracketIndex = i; 
     } 
    } 

    var arrJsonObjects = []; 
    for (var i = 0; i < arrCandidates.length; i++) { 
     var currentJSON = null; 
     try { 
      currentJSON = JSON.parse(arrCandidates[i]); 
     } catch (e) { 
      //try fixing 
      var fixedCandidate = TryFixJSON(arrCandidates[i]); 
      if (fixedCandidate) { 
       try { 
        currentJSON = JSON.parse(fixedCandidate); 
       } catch (e) { 
        currentJSON = null; 
       } 
      } 
     } 
     if (currentJSON != null) { 
      var keys = []; 
      for (var key in currentJSON) 
       keys.push(key); 
      if (keys.length > 0) 
       arrJsonObjects.push(currentJSON); 
     } 
    } 
    return arrJsonObjects; 

    function Trim(s, c) { 
     if (c instanceof Array) { 
      for (var i = 0; i < c.length; i++) 
       s = Trim(s, c[i]); 
      return s; 
     } 
     if (typeof c === "undefined") 
      c = " "; 
     while (s.length > 0 && s.charAt(0) === c) 
      s = s.substr(1, s.length - 1); 
     while (s.length > 0 && s.charAt(s.length - 1) === c) 
      s = s.substr(0, s.length - 1); 
     return s; 
    } 

    function TryFixJSON(strBlock) { 
     if (strBlock.indexOf(":") <= 0) 
      return false; 
     strBlock = strBlock.replace("{", "").replace("}", ""); 
     var mainParts = strBlock.split(","); 
     for (var i = 0; i < mainParts.length; i++) { 
      var currentPart = Trim(mainParts[i]); 
      if (currentPart.indexOf(":") <= 0) 
       return false; 
      var subParts = currentPart.split(":"); 
      if (subParts.length !== 2) 
       return false; 
      var currentKey = Trim(subParts[0], [" ", "'", "\""]); 
      var currentValue = Trim(subParts[1], [" ", "'", "\""]); 
      if (currentKey.length === 0) 
       return false; 
      subParts[0] = "\"" + currentKey + "\""; 
      subParts[1] = "\"" + currentValue + "\""; 
      mainParts[i] = subParts.join(":"); 
     } 
     return "{" + mainParts.join(", ") + "}"; 
    } 
} 

这将只是看{}之间的任何东西,并试图解析为JSON。没有评估,如果失败,它会忽略无效块。成功?太棒了,它会返回它找到的有效JSON的普通数组。

用例:

var rawCode = "var foo = new Object(); { dummy here }}} function boo() {}" + 
"foo['KEY'] = { \"Field1\": \"Value1\", \"Field2\": \"Value2\"}; hello {\"foo\": \"bar\"} and it's over "; 
var jsonObjects = ParseRawJSON(rawCode); 
for (var i = 0; i < jsonObjects.length; i++) { 
    for (var key in jsonObjects[i]) { 
     var value = jsonObjects[i][key]; 
     //got key and value... 
    } 
} 

Live test case,使用的示例代码固定的版本。

+0

首先:感谢你这个令人难以置信的工作量。其次,这是我得到它的格式,所以我不知道解析器是否能与它一起工作,这让我感觉很糟糕,因为你对你的时间非常慷慨。我应该注意的是,如果我使用eval(),那么我可以使用each()来循环访问数据,即使它不是有效的JSON,在我的结尾也是如此。任何数组/ JSON /无论我可以操纵到任何需要的格式。但假设它确实有效,这看起来好像是eval()有很大意义的情况,不是吗?我的意思是,如果另一种选择是这个错综复杂的定制解析器。 –

+0

@Chris不是所有的都会丢失,会尝试改进解析器来支持你的例子中的值。 –

+0

非常感谢任一方式。但是,我肯定会有兴趣听到你对有关哲学问题的看法。如果有eval()被证明是合理的,那么这不就是一行代码和30行之间的区别吗? –

0

由于该方法已被放置到全球,那么你可以做

window["fooFunction"](foo) 
+0

这实际上并不在全球范围内。它包含在代理文件中用于跨域目的。我正在使用AJAX请求来获取它。 –

1

一个普遍安全的使用eval是创造一个new Function,并传递给它的字符串函数体替代。这样(除非明确指出window对象),您将无法访问全局范围,并可以将其封装在函数范围中。

假设您示例代码的前两行是您想要评估的JavaScript,如果您知道要作为JSON对象检索的变量的名称,则可以在检索结束时将其返回创建函数,然后调用它:

var js = "var foo = {}; foo['KEY'] = {Field1: 'Value1', Field2: 'Value2'};"; 
var fn = new Function(js + ';return foo;'); 
var result = fn(); 

console.log(JSON.stringify(result)); 

这也是什么MDN建议做在documentation for eval

更重要的是,第三方的代码可以在其中看到的eval范围()被调用,这可能会导致类似的功能i的方式可能的攻击不易受影响。

+1

然后eval更安全吗?邪恶的黑客肯定会使用'window'编写代码。 – freakish

+0

攻击者仍然可以访问DOM和其他东西.... – Vishwanath

+0

是,但你必须在范围更多的控制,更容易覆盖您想要从正在使用(如'alert'等)在本地,以防止事情。 – Daff

相关问题