安全地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.
这个问题分解成:
-
parsingstring
-
评估结果函数图
-
调度到每个function(作为上面#2的一部分)
parsingstring
不幸的是,根据您的要求,parsing{{...}}
string非常复杂。 你至less有这些问题需要处理:
-
函数可以嵌套
{{function1(function2(), 2, 3)}}
。 -
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 。
- 如果可能的话,不要评估用户input。
- 如果您需要评估,请在受控的范围和环境中进行评估。
最后一个意思是不使用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