2013-10-31 207 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

相關問題