尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
MooTool Next Compose Vault MCP 授权路径 `realpath` 对齐 Electron 解析:从符号链接误授权到 canonical 路径强制校验
桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载导读在 MooTool Next 系列产品的 Composenext-compose桌面端中Vault MCPModel Context Protocol只读工具通过access.json读取已授权 Vault 根目录的绝对路径而 Electron 端在setDataAccess时用realpath()持久化 canonical 路径、并在读取时拒绝realpath(path) ! path的授权。DIFF-467 将这两套安全语义在 Compose 端完全对齐杜绝符号链接别名symlink alias造成的授权绕过。本文以该差分文档为骨架结合VaultMcpReadService.kt、AiIntegrationService.kt、VaultMcpTools.kt及对应单测源码逐层拆解写入侧 canonical 化、读取侧 real 校验、测试断言同步这一条完整的安全链路并给出可复现的验证命令。背景为什么要对齐 Electron 的realpath语义Electron 端的行为基线定义在两处见 next/electron/main/aiIntegrationService.ts 与 next/electron/mcp/vaultAccess.ts写入侧setDataAccess对 notes / json 两类 Vault 根调用await realpath(roots[kind])把解析符号链接后的真实路径而非用户选择的快捷方式/别名路径写入access.json读取侧VaultReadService.root在每次工具调用时执行三重校验——isAbsolute(path)、await realpath(path) ! path符号链接别名授权拒绝、lstat(path).isDirectory()必须是目录。其安全动机很直接access.json中持久化的授权路径代表用户显式同意 AI 可以读取的目录集合。如果授权路径本身是符号链接别名攻击者或用户本人都可以在不重新授权的情况下替换链接目标让 AI 工具实际访问的目录悄然变化而realpath(path) ! path则强制授权记录里只能出现文件的物理真实路径任何移花接木都会在运行时被识别并拒绝。Compose 端此前并不具备这条语义DIFF-467 之前的实现只校验绝对路径与目录存在性存在与 Electron 不一致的授权边界。该差分即为此而来关联文档。变更一AiIntegrationService.setDataAccess持久化 canonical 路径Compose 端的写入侧改造落在 AiIntegrationService.ktfun setDataAccess(request: AiDataAccessRequest): AiDataAccess { val notesRoot container.noteVault().root() val jsonRoot container.jsonVault.root() if (request.notes !notesRoot.isDirectory()) { throw IllegalArgumentException(The MooTool vault directory is unavailable) } if (request.json !jsonRoot.isDirectory()) { throw IllegalArgumentException(The MooTool vault directory is unavailable) } val access AiDataAccess( notes if (request.notes) canonicalVaultPath(notesRoot) else null, json if (request.json) canonicalVaultPath(jsonRoot) else null, ) atomicWrite(accessFile, prettyJson.encodeToString(AccessFile(version 1, notes access.notes, json access.json))) return access } /** 对齐 Electron setDataAccessaccess.json 持久化 realpath 后的 Vault 根。 */ private fun canonicalVaultPath(root: Path): String root.toRealPath().toString()关键点canonical 写入canonicalVaultPath用root.toRealPath().toString()得到不含符号链接的物理路径。因此无论用户通过设置面板选择 Vault 时点击的是链接别名还是真实目录落盘到access.json的永远只有 canonical 形式原子落盘atomicWrite保证access.json不会出现写一半的中间状态schema 恒定AccessFile(version 1, notes, json)对应VaultMcpAccessFile的version/notes/json三字段结构与 ElectronJSON.stringify({ version: 1, ...access })格式完全一致见 VaultMcpAccess.kt。该代码注释也明确写有对齐 ElectronsetDataAccess直接呼应差分文档的第一条变更。变更二VaultMcpReadService.root拒绝real ! path的符号链接别名授权读取侧的核心改造在 VaultMcpReadService.ktroot(kind)成为所有 search / read 调用的入口防线private fun root(kind: VaultMcpKind): Path { val access VaultMcpAccess.read(accessFile) val raw when (kind) { VaultMcpKind.Notes - access.notes VaultMcpKind.Json - access.json } ?: throw IllegalArgumentException( Read access to ${kind.label()} is disabled. Enable it in MooTool Settings → AI integration. ) val path Path.of(raw).normalize() if (!path.isAbsolute) { throw IllegalArgumentException(The granted vault moved. Grant access again in MooTool settings.) } val real runCatching { path.toRealPath() }.getOrElse { throw IllegalArgumentException(The granted vault moved. Grant access again in MooTool settings.) } // 对齐 Electron realpath(path) ! path授权路径须为真实目录不能是符号链接别名。 if (real ! path) { throw IllegalArgumentException(The granted vault moved. Grant access again in MooTool settings.) } if (!real.isDirectory()) { throw IllegalArgumentException(The granted vault moved. Grant access again in MooTool settings.) } return real }这套校验链条可以拆成四道闸门每一步失败都返回统一的The granted vault moved. Grant access again in MooTool settings.错误文案与 Electron 完全同字授权存在性access.json中对应 kind 的字段为null时提示未在设置中启用读取权限而不是静默放行绝对路径强制Path.of(raw).normalize()后必须isAbsolute。这条由 DIFF-468 补齐见 468-vault-mcp-absolute-grant-path.mdCompose 曾先toAbsolutePath()再校验会把access.json中的相对路径解析为进程 CWD 下的目录构成误授权风险canonical 强校验本差分核心toRealPath()成功且必须满足real path。任何符号链接别名授权如notes-link - /home/u/vault都会在这里被拦截目录语义real 必须是真实目录若授权指向已删除的目录toRealPath()本身就会失败同样落入vault moved拒绝分支。需要特别说明的是第 3 步的校验对象是授权根而非搜索过程中遇到的单个符号链接文件。对于后者search的目录遍历和resolveUnderRoot已经单独处理遍历时跳过Files.isSymbolicLink(target)的条目VaultMcpReadService.kt读取时对路径中任一段符号链接抛出Symbolic links are not exposed to AI同文件 L188-L196document还用toRealPath(LinkOption.NOFOLLOW_LINKS)inside(root, canonical)双重确认文档真实物理位置在授权根内。即根必须 canonical文档必须真实落在 canonical 根之内两层防护互不替代。变更三单测与 fixture 同步断言写入侧语义差分文档中的测试改造涉及三个测试文件正好对应写入侧、读取侧、集成侧三处1.VaultMcpReadServiceTest.rejectsSymlinkVaultRootGrant读取侧拒绝新增测试位于 VaultMcpReadServiceTest.ktTest fun rejectsSymlinkVaultRootGrant() { if (!supportsSymlinks()) return val home Files.createTempDirectory(mootool-vault-mcp-root-link-) try { val vault home.resolve(vault) vault.createDirectories() vault.resolve(note.md).writeText(body) val link home.resolve(notes-link) Files.createSymbolicLink(link, vault) val policy home.resolve(access.json) policy.writeText({version:1,notes:${link.toAbsolutePath()},json:null}) val service VaultMcpReadService(policy) assertFailsWithIllegalArgumentException { service.search(VaultMcpKind.Notes, , 10, 0) } } finally { home.toFile().deleteRecursively() } }该用例刻意构造授权记录指向符号链接别名notes-link的场景然后断言search直接抛出IllegalArgumentException——正是real ! path分支的触发点。注意if (!supportsSymlinks()) return文件系统不支持符号链接如部分受限环境时跳过与本仓库其他符号链接用例的策略一致。2. fixture 写入路径改用 canonicalrealpath写入侧语义固化同一测试文件中定义了辅助函数同文件 L227-L228/** 与 AiIntegrationService.setDataAccess 一致写入 canonical realpath。 */ private fun Path.accessPolicyPath(): String toRealPath().toString()所有测试 fixture 的access.json写入一律改为accessPolicyPath()保证测试用的授权文件与真实setDataAccess产物同构——否则测试读到的授权路径如果是createTempDirectory返回的别名路径macOS 上常见/var与/private/var的符号链接映射也会在real ! path校验下误拒绝。这行注释即差分文档fixture 写入路径改用 canonicalrealpath的源码落点。3.VaultPathAiAccessTest断言同步集成侧闭环VaultPathAiAccessTest.kt 通过真实的AppContainer全链路验证路径变更即撤销授权container.aiIntegration.setDataAccess(AiDataAccessRequest(notes true, json true)) assertEquals(container.noteVault().root().toRealPath().toString(), container.aiIntegration.getDataAccess().notes) container.updateSettings { current - current.copy(vault current.vault.copy(jsonPath productRoot.resolve(other-json-vault).toString())) } val access container.aiIntegration.getDataAccess() assertNull(access.notes) assertNull(access.json)两个断言点值得注意getDataAccess().notes必须等于root().toRealPath().toString()将写入侧canonical 持久化固化为不可回归的契约变更 Vault 目录或数据目录后notes/json全部回落为nullAiDataAccess()说明授权绑定的是具体目录的 canonical 路径目录一换、授权即失效。这是 DIFF-467 在真实产品语义上的最终闭环用户移动或更换 Vault 后AI 读取权限不会被陈旧授权自动延续必须重新在设置中授权。纵深access.json读取侧与 MCP 工具接线授权文件的实际解析在 VaultMcpAccess.kt与 DIFF-462 / DIFF-463 等差分共同构成读取侧防线version必须为 1未知字段拒绝ignoreUnknownKeys false对齐 Electronz.strictObject文件必须是普通文件且非符号链接Files.isSymbolicLink(accessFile)直接拒绝大小上限 16_000 字节解析失败统一抛Cannot read MooTool access settings. Check AI integration settings.。工具入口VaultMcpTools.callVaultMcpTools.kt将四个工具mootool_notes_search/mootool_notes_read/mootool_json_documents_search/mootool_json_documents_read统一路由到VaultMcpReadService(accessFile)因此root()的 realpath 校验会在每个工具调用中执行而不是只在授权时执行一次——授权时校验与每次调用时复验正是与 ElectronvaultAccess.ts完全对齐的运行时语义。这也解释了为什么 DIFF-467 选择写入侧 canonical 读取侧复验双管齐下如果只做写入侧 canonical攻击者仍可手工改写access.json注入符号链接路径如果只做读取侧复验就无法保证历史授权文件与 UI 展示、测试断言的一致。两条链路合流后access.json的不可变性、授权语义与 Electron 完全一致相关用例也已登记进跨实现对照表 electron-next-vaultAccess-vitest.mdcanonical-grant一行。验证与运行方式差分文档给出的验证命令需在next-compose/目录执行./gradlew :composeApp:desktopTest --offline该命令跑完整桌面测试集其中VaultMcpReadServiceTest含rejectsSymlinkVaultRootGrant、VaultMcpToolsTest、VaultPathAiAccessTest、AiIntegrationVaultMcpConnectionTeststdio 子进程端到端见 DIFF-463都会被执行。DIFF-463 记录到 627/627 全部通过后续迭代如 DIFF-669实测为 1342/1344仅 2 个跳过项为需公网连接的 smoke 用例与本差分无涉。需要留意的是--offline意味着所有依赖已在本机构建缓存中若首次运行需要联网解析 Gradle 依赖可去掉该参数。符号链接相关的用例rejectsSymlinkVaultRootGrant、rejectsUnsafePathsAndGitignoredFiles中的 link 分支在文件系统不支持符号链接的平台上会被supportsSymlinks()探测自动跳过不影响其余断言。小结一条授权路径的完整安全闭环DIFF-467 表面是一次路径规范化对齐实质是在 MooTool Next Compose 的 Vault MCP 授权模型里补上了最后一块拼图写入侧AiIntegrationService.setDataAccess用toRealPath()持久化 canonical 路径任何别名、快捷方式、软链在落盘前即被归一读取侧VaultMcpReadService.root以isAbsolute→toRealPath()成功 →real path→isDirectory四道闸门复验每次调用统一的The granted vault moved错误文案指引用户重新授权文档读取配合resolveUnderRoot逐段拒绝符号链接、document的NOFOLLOW_LINKScanonical 双保险文档必须物理位于 canonical 根之内测试侧rejectsSymlinkVaultRootGrant锁死读取拒绝行为fixture 的accessPolicyPath()与VaultPathAiAccessTest的断言把写入侧 canonical 语义固化为契约Vault 路径/数据目录一变授权立即归零。这条链路在 Compose 与 Electron 两个实现中行为一致对使用 Vault MCP 工具的开发者和 Agent 而言实际效果就是AI 只能读到用户在当前 canonical 目录下显式授权的内容任何通过符号链接别名、相对路径或目录迁移制造的漂移授权都会被瞬间识别并拒绝。若需进一步深挖参数边界search 的limit/offset、read 的length上限 50 KB 等可对照 VaultMcpTools.kt 与 DIFF-469 的 Zod 对齐说明。赞分享桌面应用开发工具【免费下载链接】MooToolHandy tool set for developers项目地址https://gitcode.com/gh_mirrors/mo/MooTool点击查看免费下载相关推荐OpenRig 技能库镜像与变更治理机制从 skills/CHANGELOG.md 读懂 _canonical 技能集的发布与漂移防护OpenRig 技能库镜像与变更治理机制从 skills/CHANGELOG.md 读懂 _canonical 技能集的发布与漂移防护 导读 OpenRig桌面应用开发工具MooTool Next Compose 的 Git Remote URL 安全校验从 Electron 对齐到 Compose 实现解析MooTool Next Compose 的 Git Remote URL 安全校验从 Electron 对齐到 Compose 实现解析 导读 本文以仓库内桌面应用开发工具SerenityOS realpath 命令完全指南从符号链接解析到内核路径解析原理SerenityOS realpath 命令完全指南从符号链接解析到内核路径解析原理 导读 本文围绕 SerenityOS 用户态工具 realpath 展开操作系统上一篇Composio Python SDK 集成测试全指南从环境搭建到 MCP 功能验证下一篇CopilotKit × AG2 集成实战Shared State 只读模式的完整 QA 验证指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

10分钟跑通第一条查询:internship-warehouse数据集快速上手教程(HF datasets流式+DuckDB双方案实战)

10分钟跑通第一条查询:internship-warehouse数据集快速上手教程(HF datasets流式+DuckDB双方案实战)

数据集数据工程数据分析 【免费下载链接】internship-warehouse 项目地址: https://ai.gitcode.com/hf_mirrors/FlyRank/internship-warehouse 点击查看 免费下载 想快速跑通 internship-warehouse 数据集的第一条查询吗?这是一个由 FlyRank 发布的、约…

📅 2026/10/16 2:51:33
老 Mac 装新 macOS:用 OpenCore Legacy Patcher 四步完成系统升级与显卡补丁

老 Mac 装新 macOS:用 OpenCore Legacy Patcher 四步完成系统升级与显卡补丁

老 Mac 装新 macOS:用 OpenCore Legacy Patcher 四步完成系统升级与显卡补丁 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 老 Intel Mac 被 App…

📅 2026/10/16 2:51:33
PEEK与PEKK选型指南:从分子结构到工艺窗口的工程实践

PEEK与PEKK选型指南:从分子结构到工艺窗口的工程实践

简介:这份PDF是基础化工行业专题研究报告,聚焦聚醚醚酮(PEEK)与聚醚酮酮(PEKK)两类高性能热塑性材料,面向化工、新材料领域的研究人员、投资分析者及产业从业者,帮助系统理解二者的结…

📅 2026/10/16 2:46:33
MORE NEWS

更多资讯

📰

AI影片制作全流程拆解:从分镜脚本到批量渲染的完整工作流

“分心不是我的错”这条短片,核心不是某个模型多强,而是把 ADHD 这种非常抽象的主观体验,用 AI 视频工具链转成了具象的镜头语言。作者打上“AI全民制作人”的标签,说明它走的是一条全流程 AI 创作路线:文案、分镜、画…

📰

WebSocket抓包分析实战:从协议帧到真实问题排查

连着几天被线上问题追着跑,我算是彻底认清了一件事:调试WebSocket不能只靠看业务日志。真要定位连接假死、消息乱序、握手失败这类问题,得把抓包工具用起来,一层一层把协议过程扒开看。这篇就围绕"抓包工具分析WebSocket&quo…

📰

C++节点编辑器实战:命令模式、编组与命中检测优化

最近在给自研的 C 节点编辑器做迭代时,明显感觉到第三方节点库已经不太能满足需求了。最开始贪省事接入过开源的节点图框架,结果越用越别扭:想改一个连线风格要动它的底层绘制,想加一种端口类型要改它的核心数据结构,更…

📰

AI多模态创作:把ADHD抽象感受拆解成可视化视频

“分心不是我的错”这个题目,看起来像一个情绪化短片的名字,但作为技术内容,它真正值得讨论的,不是ADHD本身,而是一个更实际的问题:当你想把一个高度主观、难以言说的内在感受,比如注意力涣散、…

📰

基于窗函数反求法的NLFM信号设计与脉压验证

简介:这份资源围绕传统基于逗留相位原理的窗函数反求法,讲解非线性调频(NLFM)信号设计与脉冲压缩的实现思路,面向雷达、通信信号处理方向的学习者与工程人员,帮助理解如何在高分辨率与低旁瓣之间取得平衡。…

📰

Microsoft Remote Desktop for macOS 深度指南:RDP 协议、ARM64 原生与 macOS 系统集成

简介:Microsoft Remote Desktop 10.2.1 for MAC 是微软官方推出的 macOS 原生远程桌面客户端,专为需跨平台访问 Windows 系统的开发者、IT 运维人员及远程办公用户设计,解决 Mac 设备无法原生运行 Windows 应用、调试环境或管理远程主机的核心…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬