安全地parsing和评估用户input

我正在开发一个基本上是模板领域特定语言的项目。 在我的项目中,我接受以下forms的用户input行:

'{{index(1, 5)}}' '{{firstName()}} X. {{lastName()}}' '{{floating(-0.5, 0.5)}}' '{{text(5, "words")}}' 

双花括号( {{ }} )之间的任何命令都有相应的Javascript方法,应该在遇到该命令时调用它。 (例如, function index(min, max) {...}在第一个的情况下)。

我很难搞清楚如何安全地接受input并调用适当的function。 我知道我现在做的方式是不安全的。 我简单地eval()两组大括号之间的任何东西。

我怎样才能parsing这些inputstring,以便我可以灵活地匹配花括号之间的函数调用,并用给定的参数执行该函数,而不是盲目地用代码调用eval()

我已经考虑做一个映射(如果命令是index() ,调用function index() {} ),但这似乎不是很灵活; 如何收集和传递任何参数(如{{index(2, 5)}} )?

这是写在Node.js.

这个问题分解成:

  1. parsingstring

  2. 评估结果函数图

  3. 调度到每个function(作为上面#2的一部分)

parsingstring

不幸的是,根据您的要求,parsing{{...}}string非常复杂。 你至less有这些问题需要处理:

  1. 函数可以嵌套{{function1(function2(), 2, 3)}}

  2. string可以包含(转义)引号,并且可以包含逗号,所以即使没有第一个要求,寻找离散参数(在逗号分割)的琐碎的方法将不起作用。

所以…你需要一个适当的parsing器。 你可以尝试一起临时拼凑一个,但这是parsing器生成器的地方,如PEG.js或Jison (这些只是例子,不一定是build议 – 我碰巧注意到其中一个Jison的例子是一个JSONparsing器 ,这将是大约一半的战斗)。 写一个parsing器不在回答关于这个问题的范围。 🙂

评估结果函数图

根据你使用的工具,你的parsing器生成器可能会为你处理。 (例如,我很确定PEG.js和Jison都是。)

如果不是的话,那么在parsing之后,你可能会得到一个某种types的对象图,它给出了函数和它们的参数(可能是带有参数的函数……可能是…)。

  • 泛函
    • 1
    • “二”
    • functionB
      • “一个”
      • functionC
        • 42
    • functionD
    • 27

函数有五个参数,其中三个是带有两个参数的函数B,依此类推。

那么你的下一个任务是首先评估那些最深的函数(在同一深度,从左到右),并把它们replace成相关的参数列表和结果,所以你需要一个深度优先的遍历algorithm 。 通过最深的第一个和从左到右(上面的项目符号列表从上到下)我的意思是在上面的列表中,你必须先调用functionC,然后调用functionB,然后调用functionD,最后调用functionA。

调度到每个function

再次取决于你使用的工具,它也可以处理这一点。 我再次怀疑PEG.js的确如此,如果Jison做得好,我也不会感到惊讶。

在准备调用函数(不再有)函数调用作为参数的地方,大概会有函数名和参数数组。 假设你将你的函数存储在地图中:

 var functions = { index: function() { /* ... */ }, firstName: function() { /* ... */ }, // ... }; 

…叫他们是容易的一点:

 functionResult = functions[functionName].apply(undefined, functionArguments); 

我很抱歉不能说“只要做X,你就在那里”,但这确实不是一个微不足道的问题。 我会扔工具,我不会自己发明这个轮子。

你可以尝试这样的事情:

假设你有这样的function:

 '{{floating(-0.5, 0.5)}}' 

而所有的实际function都是在一个对象中引用的,如下所示:

 var myFunctions = { 'index': function(){/* Do stuff */}, 'firstName': function(){} } 

那么,这应该工作:

 function parse(var input){ var temp = input.replace('{{','').replace(')}}','').split('('), fn = temp[0]; arguments = temp[1].split(','); myFunctions[fn].apply(this, arguments); } 

请注意,这只适用于没有函数嵌套作为参数的简单函数调用。 它还将所有参数作为string传递,而不是可能的types(数字,布尔值等)。

如果你想处理更复杂的string,你需要使用正确的parsing器或模板引擎,如评论中build议的 @TJ Crowder 。

  1. 如果可能的话,不要评估用户input。
  2. 如果您需要评估,请在受控的范围和环境中进行评估。

最后一个意思是不使用eval()使用new Function()或专门devise的库,如https://github.com/dtao/lemming.js

有关eval vs new Function()的更多信息,请参阅http://www.2ality.com/2014/01/eval.html


更复杂的方法尝试创build自己的parsing器,请查看https://stackoverflow.com/a/2630085/481422

https://github.com/douglascrockford/JSLint/blob/master/jslint.js中search评论// ECMAScript parser