12

我有一些在客户端(浏览器)和服务器(在 Java Rhino 上下文中)上运行的 JavaScript 函数。这些是小函数——基本上是定义良好且不依赖全局变量或闭包的小验证器——自包含且可移植。

这是一个例子:

function validPhoneFormat(fullObject, value, params, property) {
    var phonePattern = /^\+?([0-9\- \(\)])*$/;
    if (value && value.length && !phonePattern.test(value))
        return [ {"policyRequirement": "VALID_PHONE_FORMAT"}];
    else
        return [];
}

为了保持干燥,我的服务器代码获取每个函数的句柄并调用它们的 toString(),将它们作为 JSON 对象的一部分返回给浏览器。像这样的东西:

      { "name" : "phoneNumber",
        "policies" : [ 
            { "policyFunction" : "\nfunction validPhoneFormat(fullObject, value, params, property) {\n    var phonePattern = /^\\+?([0-9\\- \\(\\)])*$/;\n    if (value && value.length && !phonePattern.test(value)) {\n        return [{\"policyRequirement\":\"VALID_PHONE_FORMAT\"}];\n    } else {\n        return [];\n    }\n}\n"
            }
          ]
      }

然后,我的浏览器 JS 代码接受此响应并在该上下文中创建此函数的实例,如下所示:

eval("var policyFunction = " + this.policies[j].policyFunction);

policyFailures = policyFunction.call(this, form2js(this.input.closest("form")[0]), this.input.val(), params, this.property.name));

这一切都很好。但是,然后我通过 JSLint 运行这段代码,我得到了这条消息:

[错误] ValidatorsManager.js:142:37:eval 是邪恶的。

我经常意识到 eval 可能很危险。但是,我不知道如何在不使用它的情况下实现这样的机制。有什么办法可以做到这一点并通过 JSLint 验证器?

4

7 回答 7

13

我不会担心,因为您只是将这些函数字符串从服务器传递到客户端,因此可以控制要评估的内容。

另一方面,如果你走另一个方向并在服务器上对客户端传递的代码进行评估,那将是一个完全不同的故事......

更新:

由于在您的评论中禁用验证选项可能会导致您错过未来的错误,我建议您传递函数名称而不是整个函数,并将函数库镜像到服务器和客户端。因此,要调用该函数,您将使用以下代码:

var policyFunction = YourLibraryName[this.policies[j].policyFunctionName];
var policyArguments = this.policies[j].policyArguments;

policyFunction.apply(this, policyArguments); 

更新 2:

我能够使用 JSLint 成功验证以下代码,这基本上允许您在eval适当的情况下“关闭”绝大多数情况下的验证。同时,JSLint 仍然验证正常eval调用,并且该方法的所有使用都应该为未来的开发人员抛出标志,以避免使用它/在可能的情况下/在时间允许的情况下将其重构出来。

var EVAL_IS_BAD__AVOID_THIS = eval;
EVAL_IS_BAD__AVOID_THIS(<yourString>);
于 2012-10-30T20:51:56.423 回答
5

不要将函数编码为 JSON 中的字符串。JSON 用于内容,您将其与行为混淆。

相反,我想你可以返回 JS 文件,它允许真正的功能:

 { name : "phoneNumber",
    policies : [ 
        { policyFunction : function() {
              whateverYouNeed('here');
          }
        }
      ]
  }

但是,虽然这解决了技术问题,但它仍然不是一个好主意。


这里真正的解决方案是将您的逻辑完全移出您的内容。dataType导入一个包含小验证功能的 JS 文件,并根据您的 JSON 中的属性或其他内容根据需要调用它们。如果此功能如您所说的那样小巧便携,那么这应该是微不足道的。

让你的数据与你的代码纠缠在一起通常会导致痛苦。您应该静态地包含您的 JS,然后动态地请求/导入/查询您的 JSON 数据以通过您的静态包含的代码运行。

于 2012-10-30T20:58:20.587 回答
4

我会避免在所有情况下使用 eval 。没有理由你不能围绕它进行编码。无需将代码发送到客户端,只需将其托管在服务器上的一个包含的脚本文件中即可。

如果这不可行,您还可以拥有一个动态生成的 javascript 文件,然后通过响应传入必要的参数,然后在客户端动态加载脚本。真的没有理由使用 eval。

希望有帮助。

于 2012-10-30T20:57:32.160 回答
1

您可以使用

setInterval("code to be evaluated", 0);

在内部,如果您将 setInterval 传递给一个字符串,它会执行类似于 eval() 的功能。

不过,我不会担心的。如果您知道 eval() 是邪恶的,并采取适当的预防措施,那并不是真正的问题。eval 类似于 GoTo;你只需要小心并意识到你在做什么才能正确使用它们。

于 2012-10-30T20:52:10.217 回答
1

只需很少的解析,您就可以像这样:

var body = this.policies[j].policyFunction.substr;
body = body.substr(body.indexOf("(") + 1);
var arglist = body.substr(1, body.indexOf(")"));
body = body.substr(arglist.length + 1);
var policyFunction = new Function(arglist, body);

这将提供一些验证,避免直接使用eval代码并与代码同步工作。但它肯定eval是变相的,而且很容易受到 XSS 攻击。如果恶意的人可以通过这种方式加载和评估他们的代码 - 它不会拯救你。所以,真的,不要这样做。添加<script>带有正确 URL 的标签,这肯定会更安全。好吧,你知道,最好安全而不是抱歉。

PS。如果上面的代码不起作用,我深表歉意,它只显示了意图,我没有测试它,如果我在计算括号或类似的错误时犯了错误 - 好吧,你应该明白了,我不是在做广告无论如何。

于 2012-10-30T22:20:01.497 回答
1

DRY 绝对是我同意的,但是在某种程度上,复制+粘贴比引用同一段代码更有效且易于维护。

您无需编写的代码似乎相当于一个干净的界面和简单的样板。如果服务器和客户端都使用相同的代码,您可以简单地传递函数的公共部分,而不是整个函数。

Payload:
{
    "name": "phoneNumber",
    "type": "regexCheck",
    "checkData": "/^\\+?([0-9\\- \\(\\)])*$/"
}
if(payload.type === "regexCheck"){
    const result = validPhoneFormat(fullObject, value, payload.checkData)
}

function validPhoneFormat(fullObject, value, regexPattern) {

    if (value && value.length && !regexPattern.test(value))
        return [ {"policyRequirement": "VALID_PHONE_FORMAT"}];
    else
        return [];
}

这将使您能够从单个位置更新正则表达式。如果界面发生变化,它确实需要在 2 个地方进行更新,但我不认为这是一件坏事。如果客户端正在运行代码,为什么要隐藏结构?

如果您真的非常想将对象结构和模式都保存在一个地方 - 将其提取到单个 API。有一个“ValidatePhoneViaRegex”api 端点,您将传递此序列化函数的所有地方都会调用该端点。

如果所有这些看起来都太费力了,请将 jslint 设置为忽略您的代码:

“在 JSHint 1.0.0 及更高版本中,您可以使用特殊选项语法忽略任何警告。此警告的标识符是 W061。这意味着您可以告诉 JSHint 不要使用 /*jshint -W061 */ 发出此警告指示。

在 ESLint 中,产生这个警告的规则被命名为 no-eval。您可以通过将其设置为 0 来禁用它,或者通过将其设置为 1 来启用它。”

https://github.com/jamesallardice/jslint-error-explanations/blob/master/message-articles/eval.md

我更愿意看到复制+粘贴的代码,一个通用的 api,或者接收参数和复制+粘贴的样板,而不是从服务器传入要执行的神奇函数。

如果您在使用这些共享功能之一时遇到跨浏览器兼容性错误,会发生什么情况?

于 2019-08-20T21:41:07.073 回答
0

嗯,首先要记住的是,jsLint 确实指出“它会伤害你的感情”。它旨在指出您没有遵循最佳实践的地方——但不完美的代码仍然可以正常工作;没有强迫您遵循 jsLint 的建议。

话虽如此, eval邪恶的,几乎在所有情况下,总有办法使用它。

在这种情况下,您可以使用诸如 require.js、yepnope.js 之类的库或其他旨在单独加载脚本的库。这将允许您动态包含您需要的 javascript 函数,但不必包含eval()它们。

可能还有其他几种解决方案,但这是我想到的第一个。

希望有帮助。

于 2012-10-30T20:55:05.477 回答