关于 Nanote 的思考
结构模型
思想斗争共进行了四个版本,它们的图示如下
版本 1
插件与 Electron 直接通信,导致 ipc 接口泄露

版本 2
接入 qiankun 沙盒模型,但插件可以控制 props,伪造 appName

版本 3 <current>
nanote-api 做中间代理掌握 props,但插件仍可以通过篡改源码的方式伪造 appName,甚至直接无视 nanote-api

版本 4 不再考虑,将跳过此版本

nanote-api 在插件开发时可引入,打包时必须去除,运行时动态引入
⭐ 版本 5 <future>
考虑直接使用 iframe 而不是 qiankun,因为它原生的 JS、CSS 隔离机制是最好的,之前使用 qiankun 是因为安全问题,
现在可以模仿类似机制,通过 fetch 方法获取它的资源引用,最后把 nanote-api 注入,创建虚拟环境并执行
既创建了良好的隔离环境,又避免了接口被篡改,同时取到了 qiankun 和 iframe 的优点
简要描述:
首先通过 import-html-entry 获取页面的 html 模板、样式表、脚本,然后随即把样式表和页面模板注入,并注释掉之前的引入如:
<!-- <link href="main.css" rel="stylesheet"></link> replaced by nanote-->
<style>
// injected by nanote
<style>随后调用 bootstrap 生命周期函数(由插件导出),在其执行完成之后(如果是 Promise,则在 then 中),通过上述 import-html-entry 的一个函数 execScripts 开始执行上面收集到的脚本,向其传递提前准备好的 proxyWindow,这样脚本就无法通过 window.parent 访问主页面
保持 IPC_API 私密性
原接口名称固定为 IPC_API,现在把接口作为 Symbol 属性的值,对外暴露一个一次性接口来让主页面获得该值
const key = {
// 利用 Symbol 不可遍历的特性
value: Symbol('secret'),
visited: false
}
contextBridge.exposeInMainWorld('api', {
getKey() {
if (!key.visited) {
key.visited = true
return key.value;
}
return null
},
[key.value]: {
// some api
}
})