ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Insomnia 模板标签沙箱权限模型深度解析:读懂 `insomnia.permissions` 的模块与能力授权机制

Insomnia 模板标签沙箱权限模型深度解析:读懂 `insomnia.permissions` 的模块与能力授权机制 Insomnia 模板标签沙箱权限模型深度解析读懂insomnia.permissions的模块与能力授权机制【免费下载链接】insomniaThe open-source, cross-platform API client for GraphQL, REST, WebSockets, SSE and gRPC. With Cloud, Local and Git storage.项目地址: https://gitcode.com/GitHub_Trending/in/insomnia本文基于 Insomnia 官方文档 PERMISSIONS.md结合仓库中沙箱源码、守卫脚本与测试用例完整讲解模板标签插件在 QuickJS 沙箱中的权限声明语法、双轴授权模型、模块注册表与能力网关机制。读完本文你将掌握如何为插件声明insomnia.permissions、理解require()与context.*各自受控于哪一套规则以及如何在多文件插件中安全组织代码。背景为什么模板标签需要一个默认拒绝的沙箱在 Insomnia 中当用户开启Run template tags in sandbox路径Preferences → Scripting后插件模板标签的run()将不再于宿主进程中直接执行而是进入一个隔离的 QuickJS 沙箱运行。核心执行入口位于 plugin-tag-sandbox.ts其中runTagInSandbox()每次调用都会创建全新的 QuickJS 上下文QuickJS.newContext()并在运行结束后整体释放。在该沙箱中插件代码采用默认拒绝default-deny策略它只能触达被显式授予的东西。授予内容通过插件package.json中insomnia.permissions字段声明由两个相互独立的维度组成{ insomnia: { permissions: { modules: [events], // 决定 require(x) 能返回什么 capabilities: [network] // 决定 context.* 能调用哪些宿主桥接分组 } } }真实的授权计算并不是“声明了就有”而是基线 ∪ (声明 ∩ 所在表面的上限)。这一公式在 surface-profiles.ts 中有精确定义插件声明无法超出其所在“表面”surface如模板标签的 ceiling即清单无法自我提权。轴线一modules—— 你能require()什么modules定义沙箱内允许require()的模块名。关键在于每个可用的名字解析到的都是 Insomnia 提供的经审查的安全等价物纯 JS 重实现或宿主背书的 shim而绝不是原始 Node 内置模块。全部可用模块的单一事实来源是 module-registry.ts 中的SANDBOX_MODULES数组。基线模块无需清单声明path、crypto属于基线模块任何插件即使完全不声明permissions也能使用。从源码看path由一段纯 JS 工厂实现PATH_FACTORY提供sep、basename、extname、join而crypto的工厂CRYPTO_FACTORY则返回对宿主真实加密能力如hash/hmac/randomBytes/randomUUID的同步包装见 plugin-tag-sandbox.ts 的HostCrypto定义。可授予模块注册表中的其余条目任何出现在沙箱注册表中的其他模块都可以通过声明加入授权。目前注册表包括模块来源类型说明events纯 JS 重实现提供EventEmitteron/once/emit/removeListener等常用表面uuid经审查的 npm 库预打包真正被 bundle 进沙箱的第三方库ajv经审查的 npm 库预打包真正被 bundle 进沙箱的第三方库这些经审查的 npm 库由 Insomnia 固定版本exact-pinned并预先打包。它们只会在插件声明时才被加载在module-registry.ts中以heavy: true标记这样不使用这些库的渲染无需为解析数百 KB 的 bundle 付出代价。隔离的 vendored 安装与守卫不变式uuid与ajv各自来自 vendored/pkg/package.json 中的隔离、精确固定版本安装当前为ajv8.18.0、uuid11.1.1并非应用自身依赖的ajv/uuid后者为应用内部使用可能更新。这一隔离设计保证沙箱内的审查版本可以独立审计永远不会随应用日常依赖升级而“无声漂移”。这里存在一条不变式沙箱内的 pin 绝不能超过应用自身解析到的同库版本沙箱可以滞后于应用但绝不能超前。该约束由 check-sandbox-vendor-guardrail.ts 强制对应 npm 命令npm run sandbox:vendored:guardrail -w insomnia该命令同样会在 CI 中运行。新增一个审查库需要遵循 generate-sandbox-vendored.ts 头部注释给出的刻意流程先把精确固定版本依赖写入vendored/pkg/package.json再在 sandbox-vendored-libs-list.ts 的VENDORED_LIBS中登记入口表达式随后运行npm run sandbox:vendored:generate -w insomnia并把生成的条目注册进module-registry.ts的SANDBOX_MODULES最后补上回归测试并更新本文档。对已有库升版时优先使用升级命令而不是手工编辑npm run sandbox:vendored:upgrade -w insomnia -- libversion它会校验请求的版本与实际安装、重新生成的 bundle 完全一致检查上述守卫不变式并在提交前运行该库的name.regression.test.ts测试套件。仓库中对应的回归测试位于 vendored/uuid.regression.test.ts 与 vendored/ajv.regression.test.ts。两条用户可见的拒绝错误__require的解析是默认拒绝且两阶段式的见 in-sandbox-bootstrap.ts两条错误消息本身即是面向插件作者的契约require(X)而X未被授予→Module X not permitted by manifestrequire(X)而X已被授予但 Insomnia 并未提供 →Module X not available in sandbox此时应请求把该模块加入注册表。两类错误都携带稳定的判别字段code分别为SANDBOX_MODULE_NOT_PERMITTED/SANDBOX_MODULE_NOT_AVAILABLE并附moduleName见 plugin-tag-sandbox.ts 的SandboxModuleDenialError。这些消息被 plugin-tag-sandbox.test.ts 等单测与 smoke 测试逐字断言例如Module fs not permitted by manifest、Module left-pad not available in sandbox。值得注意由于别名机制canonicalizeModulenode:events会被规范化为events再做授权判断。轴线二capabilities—— 你能通过context.*做什么capabilities定义插件标签可通过context.*执行的宿主侧动作。未持有的能力意味着context中对应分支直接缺失context.network undefined插件可以用特性检测优雅降级如if (context.network) { … }而不是遇到一个“存在但拒绝”的 stub。Capability授权内容基线rendercontext.util.render✅models.read只读的context.util.models.*查询✅utilcontext.util.nodeOS/decode/encode✅cryptorequire(crypto)宿主函数✅networkcontext.network.*— 需声明storagecontext.store.*— 需声明fs-readcontext.util.readFile— 需声明appcontext.app.*、context.util.openInBrowser— 需声明credentials云服务商凭据读写为第一方 bundle 插件保留社区模板标签插件即使声明也无法获得——它位于模板标签表面能力的 ceiling 之上。capability 与桥接路径的映射每个可被沙箱调用的桥接路径都精确归属一个能力组映射表集中在 host-bridge.ts 的BRIDGE_PATH_CAPABILITIESutil.render→renderrequest.getById、workspace.getById、oAuth2Token.getByRequestId、cookieJar.*、response.getLatestForRequestId、settings.get等 →models.read注意response.setBody因属于有界的、仅由宿主指定 bodyPath 的写操作也被归入models.read并不存在一般意义的fs-writenodeOS/decode/encode→utilnetwork.sendRequest/network.sendRequestWithoutSideEffects→networkpluginData.*→storagereadFile→fs-readcloudCredential.*→credentialsapp.alert/app.dialog/app.prompt/app.getPath/app.clipboard.*/openInBrowser→app。该映射与in-sandbox-bootstrap.ts中__bridge(…)的调用点由一条完备性单测强制同步任何新增桥接路径若未登记能力将直接导致测试失败fail-closed绝不出现未设防路径。两层纵深防御权限在两层同时生效C1 C2宿主侧网关C1filterByCapabilities()为每个 handler 包装守卫未获授权的路径调用会抛出Capability network not granted — add it to insomnia.permissions.capabilities。无能力映射的路径同样失败关闭。沙箱内 API 形状C2in-sandbox-bootstrap.ts 的__buildContext()构建出完整的context后再按__hasCap()删除未授权分支因此context.network等表现为undefined插件在自己代码中做特性检测即可不必等到异步拒绝。基线的四项render、models.read、util、crypto始终被授予对应分支恒存在crypto是唯一的例外——它是为清单/profile 一致性而保留的并不对应桥接路径见host-bridge.ts中crypto的注释。授权如何被计算基线 ∪ (声明 ∩ 天花板)surface-profiles.ts 定义了“表面 profile”沙箱计划 P1。一个 profile 由四项组成moduleCeiling、capabilityCeiling所在表面上可被授予的上限超集、baselineModules与baselineCapabilities无清单时必定获得的底线。有效授权公式为floor ∪ (declared ∩ ceiling)模板标签 profileTEMPLATE_TAG_PROFILE的模块天花板是整个注册表每个注册模块都是经审查的安全等价物能力天花板则明确排除了credentials。两个导出函数体现了差异resolveTemplateTagModules()把声明的模块名规范化后并入基线不做解析期天花板过滤——注册表本身就是有效闸门声明了但未注册的模块会透传给require从而给出精确的 “not available in sandbox” 而非误导性的 “not permitted by manifest”resolveTemplateTagCapabilities()严格执行基线 ∪ (声明 ∩ 天花板)。迁移从无清单到声明权限一个没有声明任何permissions的插件运行在基线授权之上模块仅path、crypto能力仅render、models.read、util、crypto。当它试图触碰非基线模块例如require(events)时会看到一次性通知其中会指出需要添加的具体授权项。此时把上文示例中的insomnia.permissions块加入package.json并重载插件即可。仓库内的真实示例 insomnia-plugin-sandbox-demo 正是这样声明modules声明events、uuidcapabilities声明storage——你可以将其作为最小可运行的模板直接对照。多文件插件相对路径解析与“投毒”防护插件可以拆分为多个文件require(./util)、require(../shared)、require(./lib)解析到./lib/index.js都会从插件自身源码目录内解析读取。但关键约束是插件自身的node_modules永远不会被咨询——裸require(uuid)永远解析到受审查的注册表副本因此插件无法通过自带一份同名实现来“顶替”被授予的模块源码中称之为 “poison” 防护。相对加载器只允许插件目录内的.js/.json文件跳过node_modules与点目录且路径若试图上溯越过插件根目录如从浅层目录require(../../util)会被判定为不可解析。在 in-sandbox-bootstrap.ts 中可见完整的实现细节宿主仅把插件目录内的源码读入moduleFiles键为 POSIX 相对路径相对 require 经由__resolveRelative在本模块映射中查找而裸说明符仍走授权闸门__require。相对加载编译使用new Function源码是信封数据不在宿主侧 eval.json文件则直接JSON.parse。重要审查库要放在run()里 require而非顶层Insomnia 仍通过宿主进程加载插件入口来发现标签因此顶层的require(uuid)/require(ajv)在宿主环境中无法解析这些库只存在于沙箱注册表磁盘上并不在插件旁边。正确做法是把对审查库的 require 放在标签的run()内部——当标签在沙箱中执行时它们即可解析。相对的./util这类 require 在顶层没有问题。沙箱运行时的额外防护边界除了权限授权沙箱还施加了若干硬性运行边界见 plugin-tag-sandbox.ts执行超时单次标签运行默认上限timeoutMs 10_000镜像 LiquidJS 渲染限制并通过轮询中断处理器防止紧循环绕过超时内存上限ctx.runtime.setMemoryLimit(32 * 1024 * 1024)即 32MB WASM 堆防止插件无限分配导致宿主 OOM全局加锁bootstrap 在插件代码运行前捕获信封__envelopeJSON、__tagName并删除对应全局随后把__require、__buildContext、__invoke等入口点设为只读模块注册表填充完毕后__registerModule同样被删除——插件代码既不能改写授权集合也不能注册/替换工厂因此factorySource只能是在仓库内的可信字符串字面量见module-registry.ts中的安全不变式与registerCall的守卫浏览器 API polyfill沙箱为 QuickJS 缺失的 Web APIbtoa/atob/TextEncoder/TextDecoder和console转发到宿主 sink提供实现见 in-sandbox-bootstrap.ts。补充说明与验证方法Bundle第一方插件是受信任的直接获得全部模块 全部能力绕过 profile 天花板profile 仅约束用户插件格式错误的permissions例如modules不是数组会降级为基线并在 Preferences → Plugins 的插件卡片上显示警告插件本身仍然加载验证途径上述拒绝错误、授权并集与分支缺失行为均在 plugin-tag-sandbox.test.ts、multi-file-plugins.test.ts、surface-profiles.test.ts 等测试中被逐字断言是理解本文档约定的最佳配套阅读材料守卫与打包命令在packages/insomnia/package.json中以sandbox:vendored:ci / generate / guardrail / upgrade形式暴露。小结理解insomnia.permissions的关键在于把握三层心智模型modules管“代码里能 require 到什么”两阶段拒绝先查授权、再查注册表capabilities管“context 上能调用宿主的哪些动作”未授权分支直接缺失可特性检测两者之上还叠加表面天花板、注册表白名单与基线 ∪ (声明 ∩ 天花板)的授权公式。对所有模板标签插件作者而言遵守“审查库在run()内 require、裸依赖永远交给注册表、格式非法即退回基线”这三条即可安全、正确地发布运行于沙箱的插件。【免费下载链接】insomniaThe open-source, cross-platform API client for GraphQL, REST, WebSockets, SSE and gRPC. With Cloud, Local and Git storage.项目地址: https://gitcode.com/GitHub_Trending/in/insomnia创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表