解决使用 selenium 调用 metamask 进行 dapp 自动化测试报错问题
author: jwensh
date: 20230517
unknown error: Runtime.callFunctionOn threw exception: Error: LavaMoat - property “JSON” of globalThis is inaccessible under scuttling mode. To learn more visit https://github.com/LavaMoat/LavaMoat/pull/360.
问题
- java 1.8
- selenium 3
- chrome 113
- chrome ext metamask
重现步骤
- 在浏览器上安装 Metamask 插件,Chrome App Store:
https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn
- 然后使用Selenium打开Metamask,代码:
System.setProperty("webdriver.chrome.driver","webdriverPath");System.setProperty("webdriver.chrome.silentOutput","true");ChromeOptions chromeOptions =newChromeOptions();
chromeOptions.setExperimentalOption("debuggerAddress","127.0.0.1:8080");
chromeOptions.addArguments("−−lang=en-US");finalChromeDriver driver =newChromeDriver(chromeOptions);
driver.get("chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/home.html");
driver.find_element(By.css_selector,"#password");
- 执行用例 java 代码打开 metamask 页面,进行输入密码登录的时候报错
unknown error: Runtime.callFunctionOn threw exception: Error: LavaMoat - property "JSON" of globalThis is inaccessible under scuttling mode. To learn more visit https://github.com/LavaMoat/LavaMoat/pull/360.
- 从报错上来看,都以为是 selenium 的问题,并尝试通过各种定位元素的方式进行解决,然而让人抓狂的结果是没有用 (参考)
解决问题
修改 metamask 中的安全机制逻辑
- 安装 CRX Extractor/Downloader
- 打开页面 https://chrome.google.com/webstore/detail/metamask/nkbihfbeogaeaoehlefnkodbefgpgknn
- 页面中,点击工具栏中
CRX Extractor/Downloader
使用插件下载 zip 格式的metamask
包 - 将下载的 zip 解压,vscode 打开,找到并将
runtime-lavamoat.js
文件第93
行的scuttleGlobalThis
改为false
保存 - 在将 metamask 压缩成 zip (就是普通的压缩)
- 打开
chrome://extensions/
页面,将 zip 文件拖进来,重新安装; - 如果上一步报错了,可使用
加载已解压的扩展程序
, 手动选择下载的修改过 metamask 文件夹,进行加载
需要注意的是这种方式安装的 ext ,对应的 ID 会有变化的, 也就是
chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/home.html
driver.get() 要换成新的
关于问题 LavaMoat 开发的解释
我看到关于此功能存在一些混淆,所以我将尝试更好地解释为什么如果您在 MetaMask 之上构建基于外部网络驱动程序的应用程序,如果您不自己编译 MetaMask,那么您会遇到困难而是使用官方版本。
此功能使应用程序的全局对象无法使用 - 是故意的。
我们可以允许自己这样做,因为 LavaMoat 使应用程序的 90-99% 的全局对象变得不敏感。
这是一项安全功能——LavaMoat 本质上是一个沙箱工具,用于对 JavaScript 代码进行沙箱处理。如果某些 JavaScript 代码设法逃离 LavaMoat 沙箱并到达全局对象,它将获得对沙箱试图从中撤销的 API 的访问权限。
例如,如果 LavaMoat 沙箱化了一个负责日志记录的依赖项,它只会授予它访问权限console.log而不是fetch- 但如果依赖项中的 JavaScript 代码以某种方式设法逃脱沙箱并到达全局对象,它将获得对fetchAPI 由全局对象提供。
尽管逃脱沙箱的可能性不大,但我们在 MetaMask 中努力实施多个安全层,以防某一层以某种方式被破坏。
[这正是#360](https://github.com/LavaMoat/LavaMoat/pull/360)的含义——第二次喜欢防守。
我们使全局对象不可用,因此如果 JavaScript 代码逃离沙箱并到达全局对象,它将无法访问全局对象通常提供的任何内容,从而保护应用程序免受这种情况的影响。
最终这意味着任何试图在全局范围内(在 LavaMoat 沙箱之外)运行的 JavaScript 代码如果依赖于访问全局对象必须提供的内容,都会失败。
所以const a = 1+1; const b = []; b.push(2);会工作,因为它不包括对全局对象的访问,但是像这样的东西const x = new Array(); const y = fetch('/'); const z = new Proxy(...)会失败,因为这些不再可以通过全局对象访问。
如果您使用外部 webdriver 来操作 MetaMask,您将面临这个问题,因为您通过 webdriver 代码在应用程序中运行的任何代码都会尝试访问全局对象上的内容并且会失败。
更进一步 - 网络驱动程序本身将代码注入到被操纵的应用程序中,并且该代码也依赖于全局对象才能正常运行,因此也会失败。换句话说,无论您编写什么代码,webdriver 本身都没有准备好处理这种独特的情况。
这是我们目前感觉良好的权衡(与人们能够使用网络驱动程序操纵 MetaMask 相比,安全性要好得多)。
因此,目前您唯一的选择仍然是通过构建您自己的禁用此安全功能的 MetaMask 版本来完成您想要的。
在 MetaMask 存储库中找到scuttleGlobalThis并将其设置false为 然后使用 构建应用程序yarn dist。您最终会得到一个不再损害全局对象的本地 MetaMask 版本。
当心 - 这会降低 MetaMask 的安全级别 - 使用风险自负。
版权归原作者 jwensh 所有, 如有侵权,请联系我们删除。