在JavaScript程序中重构动态加载的脚本以避免eval

whitzsjs  于 2023-05-05  发布在  Java
关注(0)|答案(1)|浏览(117)

这可能是一个特定的问题(研究项目),但我正在寻找一个好方法,使这个程序显着“安全”比它目前。
目标是接受任意用户代码(在它通过单元测试和手动检查之后),并将其合并到正在运行的程序中,而无需重新加载应用程序。
具体的例子是,它是一个连续执行的绘图应用程序,我希望学生/其他用户能够提交脚本(遵循特定的格式/指南)。
我目前的实现是要求用户指定他们的类名与他们的文件名相匹配(例如,myclass.js内部有一个const myclass = class { ... }块)。然后,当我看到服务器(一个检查特定目录内容的Flask应用程序)中存在一个新文件时,JavaScript应用程序将加载该特定文件。
目前,我有一个promise来确保脚本被加载,但我所做的一般方法是将它带入内存:

function loadNewScript(scriptName) {
  let s = document.createElement("script");
  s.setAttribute("type", "text/javascript");
  s.setAttribute("src", `/static/techniques/${scriptName}`);
  let nodes = document.getElementsByTagName("*");
  let node = nodes[nodes.length - 1].parentNode;
  node.appendChild(s);
}

然而,我预期的安全问题(除了我假设的任意代码)是我目前正在使用eval将类带入内存:

// technique comes in as 'myclass.js'
function loadObject(technique) {
    try {
        let obj = eval(technique.split(".")[0]);
        let _activeObj = new obj();
        if (typeof _activeObj != "undefined") {
            return _activeObj;
        }
        return null;
    } 
    catch (e) { // will check for syntax errors, but this usually trips when the script isn't loaded yet
        return null;
    }
}

目前这是可行的,但是我有点担心这里使用的eval。我看过关于使用windowthis命名空间的帖子,但据我所知,您不能将动态加载的脚本添加到全局命名空间中?无论如何,使用window['myclass']this['myclass']在加载后无法工作。(How to execute a JavaScript function when I have its name as a string

8ehkhllq

8ehkhllq1#

实际上,如果您已经将“任意”(已审查)代码加载到动态<script>中,那么为了安全性而避免eval是没有意义的。window['myclass']不工作的问题是因为const does declare a global variable but create a property of the global object。您需要更改脚本以使用var myclass = class { ... };
但是,我建议您更改您的实现,以要求遵循ES6模块格式的文件:

export default class myclass { ... }

然后你可以用

function loadNewScript(scriptName) {
    return import(`/static/techniques/${scriptName}`);
}
async function loadObject(technique) {
    const module = await loadNewScript(technique);
    const obj = module.default;
    return new obj();
}

如果你需要loadObject方法是同步的(而不是返回一个promise),你可以use an object or Map as a class registry

const registry = {};
async function loadNewScript(scriptName) {
    const module = await import(`/static/techniques/${scriptName}`);
    registry[scriptName] = module.default;
}
function loadObject(technique) {
    const obj = registry[technique];
    return obj ? new obj() : null;
}

相关问题