如何在浏览器扩展中使用 eval 函数

发布时间 2023-08-07 21:08:12作者: Higurashi-kagome

思路是把 iframe 作为沙箱环境,让 eval 在 iframe 中执行。

以 Chrome Manifest V2 为例。V3 可参考 Using eval in Chrome extensions - Chrome Developers

1. 在 manifest 文件中列出沙箱 html

{
  ...,
  "sandbox": {
     "pages": ["sandbox.html"]
  },
  ...
}

2. 加载沙箱 html

比如在 Manifest V2 中,可以将其放在背景页中(Manifest V3 取消了背景页):

<!-- background.html -->
<body>
    <iframe id="theFrame" src="sandbox.html" style="display: none"></iframe>
</body>

3. 在沙箱 html 中执行 eval 等特殊操作

<!DOCTYPE html>
<html>
<body>
    <script>
        // 接收消息
        window.addEventListener('message', async function (event) {
            const message = event.data
            let result = ''
            if (message) {
                switch (message.command) {
                    // 根据所收到消息的 command 值决定执行流程
                    case 'eval':
                        try {
                            result = eval(message.expression)
                            console.log(result)
                        } catch (error) {
                            event.source?.postMessage({ error, command: message.command }, event.origin)
                            return;
                        }
                    break
                }
            }
            // 传回消息给消息发起方
            event.source?.postMessage({ result, command: message.command }, event.origin)
        });
        
    </script>
</body>
</html>

4. 传递消息给沙箱 html

因为 sandbox 放在了 background.html 中,所以在 background.html 的 js 脚本中添加如下内容:

// 发送 eval 消息到 iframe
function sandboxEval(expression) {
    const iframe = document.getElementById('theFrame')
    const message = {
        command: 'eval',
        expression
    };
    iframe?.contentWindow?.postMessage(message, '*')
}

// 接收消息
window.addEventListener('message', function(event){
	const message = event.data
	if (message) {
		switch (message.command) {
			// 接收来自 iframe 的 eval 结果
			case 'eval':
				if (message.error) {
					console.error(message.error)
				} else {
					console.log(message.result)
				}
			break
		}
	}
})

// 调用函数进行测试
sandboxEval('1 + 1')

这样就间接实现了调用 eval 函数。

在模板引擎比如 NunjucksHandlebars 中,通常会使用到 eval 等类似比较“危险”的函数,遇到无法在浏览器扩展中使用这些库时可以类似解决。

参考:Using eval in Chrome extensions - Chrome Developersjavascript - JS templating in google chrome extension(manifest v2) - Stack Overflow