Nanote 架构更迭

关于 Nanote 的思考

结构模型

思想斗争共进行了四个版本,它们的图示如下

版本 1

插件与 Electron 直接通信,导致 ipc 接口泄露

version1

版本 2

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

version2

版本 3 <current>

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

version3

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

version4

nanote-api 在插件开发时可引入,打包时必须去除,运行时动态引入

⭐ 版本 5 <future>

考虑直接使用 iframe 而不是 qiankun,因为它原生的 JS、CSS 隔离机制是最好的,之前使用 qiankun 是因为安全问题,
现在可以模仿类似机制,通过 fetch 方法获取它的资源引用,最后把 nanote-api 注入,创建虚拟环境并执行

既创建了良好的隔离环境,又避免了接口被篡改,同时取到了 qiankuniframe 的优点

简要描述:

首先通过 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
  }
})