- 归档 OnlyOffice live bridge、Page AI、mindmap、design governance 与相关 bug 条目 - 补齐 MinerU OCR 后端 runtime 合同与 smoke/test 基线 - 收口 ChatOnly/Doubao、ObjectIdentity、Page Aggregate compat 与 runtime owner 文档口径 验证: - cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr -- --test-threads=1 - cargo test --manifest-path rust/Cargo.toml -p mnote-web onlyoffice_bridge -- --test-threads=1 - git diff --check - git diff --cached --check - codegraph index . --force && codegraph status . - codegraph sync . && codegraph status .
6.2 KiB
6.2 KiB
7-46 ONLYOFFICE live tool 缺少 resource scope 绑定
状态
- 状态:done
- Owner:07-ai / Hermes tools / OnlyOffice live bridge
- 发现时间:2026-05-31
现象
ONLYOFFICE live 写工具当前主要检查 idempotencyKey、dryRun、aiAccessScope.permissionLevel 和 command context 写权限,但没有把 bridge session 的 documentId / assetId 与 aiAccessScope.allowedResourceIds 绑定。调用方如果显式传入另一个已注册 Office session,存在越过当前 target resource scope 写入非授权资源的风险。
证据
rust/crates/mnote-web/src/routes/hermes_tools.rs注册了多组mnote.onlyoffice.*读写工具。rust/crates/mnote-web/src/hermes_tools/onlyoffice_live.rs的run_write_action()调用ensure_write_authorized()后立即解析 session 并执行 bridge command。rust/crates/mnote-web/src/hermes_tools/onlyoffice_live.rs的resolve_session_id()只解析显式 session 或全局 current session,没有验证该 session 对应的 resource 是否在allowedResourceIds内。- 对照
rust/crates/mnote-web/src/hermes_tools/resource.rs,mindmap/resource 工具已有ensure_resource_scope_allowed()校验allowedResourceIds/objectIdentity。
影响
- Page AI 当前 target 是资源 A 时,模型或恶意调用可传入资源 B 的
bridgeSessionId,尝试写入 B。 - 写入审计会显示工具有写权限,但缺少“写的是哪个 resource、是否被 allowedResourceIds 授权”的闭环。
- 多 tab session fallback 与本 bug 叠加时,错误写入更难被用户发现。
最小复现建议
- 构造
aiAccessScope.allowedResourceIds = [A]。 - 注册两个 ONLYOFFICE bridge session:A 和 B。
- 调用
mnote.onlyoffice.sheet.set_value或mnote.onlyoffice.document.insert_text,显式传 B 的bridgeSessionId。 - 期望:返回 403;当前风险:只要通用写权限通过就可能执行。
修复建议
BridgeSessionInfo暴露稳定documentId、assetId、objectIdentity。- ONLYOFFICE live 工具增加与 resource 工具等价的 scope 校验。
- 写工具返回 receipt 时包含
resourceKind=office、documentId、assetId、onlyofficeSessionId与 permission decision。 - dry-run 也必须执行 scope 校验,不能只返回 wouldWrite。
本轮进展
- 2026-05-31:
onlyoffice_live.rs已在读/写 action 执行前校验 explicit session 对应的sessionId/documentId/assetId/resource:office:{documentId}:{assetId}是否包含在aiAccessScope.allowedResourceIds。- 缺失
aiAccessScope或空allowedResourceIds现在返回 403,不再兼容放行;dry-run 同样执行该 scope 校验。 manifest.rs已为 OnlyOffice live 工具声明aiAccessScope.allowedResourceIds和onlyofficeSessionId/bridgeSessionId的anyOf合同。- 已补 Rust 定点测试覆盖
allowedResourceIds=[asset_a]时禁止写入 sessionasset_b,allowedResourceIds=[asset_allowed]时允许生成 dry-run plan,缺 scope 时返回 403,以及 manifest 合同。 - 已补并通过
scripts/task515-onlyoffice-live-scope-http-smoke.js:HTTP 层覆盖缺 explicit session 400、scope 不匹配 403、缺 scope 403、授权 scope dry-run 200。
- 2026-06-01:
- resource scope candidate 已同时接受
resource:office:{documentId}:{assetId}与 FileTree / resource 对象侧使用的resource:onlyoffice:{documentId}:{assetId},避免真实 Page AI target 使用 OnlyOffice object identity 时被误拒。 - 已补并通过
scripts/task518-onlyoffice-real-iframe-session-scope-smoke.js:真实 ONLYOFFICE iframe / DocumentServer 下,显式传入 Office B 的bridgeSessionId但aiAccessScope.allowedResourceIds=[Office A]时返回 403;授权 B 的assetId/resource:onlyoffice:{documentId}:{assetId}时 dry-run 返回 200。 document-resource-tab-runtime.js/sidebar-page-ai-runtime.js/hermes_client.rs已补最小 target scope 链路:Office resource target 可把onlyofficeSessionId写入 target package,服务端 sanitize 不再丢弃primaryTargetId、targets[]、assetId、onlyofficeSessionId,本地 run instructions 的aiAccessScope.allowedResourceIds会包含 Office asset、session 和resource:onlyoffice:{documentId}:{assetId}。新增hermes_client_run_body_preserves_onlyoffice_target_scope证明服务端不再把 Office target scope 降级为当前页面 id。- 本地 agent instructions 已明确要求从
agentRunEnvelope.targetPackage.onlyofficeSessionId或对应 target 取值传给mnote.onlyoffice.*工具,不允许 fallback 到最近活跃 Office session。 task518已扩展并通过授权 B session 的非 dry-run 写入验证:写入后document.export证明 B 包含唯一 marker,A 不包含,补齐真实 iframe 层写入落点证据。- 已补并通过
scripts/task523-page-ai-onlyoffice-real-target-session-smoke.js:真实 Page AI UI 选择 Office resource target 后,run payload 中editorTarget.onlyofficeSessionId、targetPackage.onlyofficeSessionId和targetPackage.targets[0].onlyofficeSessionId均等于 iframe live bridge session;allowedFiles只包含选中 Office 文件路径,且无 buffer-state 404 / console error。
- resource scope candidate 已同时接受
当前 task518 已覆盖真实 iframe session + 工具层 resource scope + 非 dry-run 写入落点,task523 补齐真实 Page AI UI target picker 到 live Office session 的 run payload 绑定;组合后覆盖“UI 只授权选中 Office target,工具层拒绝未授权 session”的端到端安全边界。
验收
- Rust 单测覆盖 allowedResourceIds 不包含 session resource 时写工具返回 403。
- Rust 单测覆盖 allowedResourceIds 包含
assetId或objectIdentity时允许执行。 - Rust 单测覆盖缺失 / 空 resource scope 时拒绝。
- HTTP smoke 覆盖工具层 session/scope 边界。
- Browser smoke 覆盖真实 ONLYOFFICE iframe session 的工具层 A/B resource scope 边界。
- Rust 单测覆盖 Page AI Office target package 生成的 aiAccessScope 包含 Office asset/session/object identity。
- Browser smoke 覆盖真实 Page AI UI 选择 Office target 后只把该 Office 的 live session 与 relativePath 冻结进 run payload;工具层 A/B session 越权拒写由
task518覆盖。