chainlesschain 0.163.1 → 0.163.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-Cn7quRgL.js → AIOps-DIY6KxV-.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-YqMX3ueH.js → ActionButton-BXH0muPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Bxmy1b7Y.js → Analytics-D39w8vmg.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-DBOw69jE.js → AppLayout-CUU1zvLs.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-mq1nyzN1.js → Artifacts-D8azHO3T.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-mMiKtZ0D.js → Audit-D0tAwXTg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-WBUjpYVh.js → BackgroundAgents-C29mhJll.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-c25OoJgs.js → Backup-D6Aa1Gro.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BbfWhZ4-.js → BaseInput-B1uoHbPQ.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CVVzsTwU.js → Chat-Bs4DLOR5.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CVCjZJ04.js → ChatBubbleRenderer-DUYIBSle.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-C9o2HK-1.js → Checkbox-CNcBPEa4.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B7X5LfKc.js → Codegen-CIY89763.js} +1 -1
- package/src/assets/web-panel/assets/{Col-jpPj2Pur.js → Col-CsPYFmgo.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Dti6bHxN.js → Community-BBocEge4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-S4ZsLt3W.js → Compact-TQ8Ux0Lf.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bge0sQ3O.js → Compliance-DfBsZTft.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-C4XyzVI7.js → Cowork-Cg-I6FVH.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CPR0v2xy.js → Cron-DTQzfRTp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DAX8lDaj.js → Crosschain-DGxEKGpR.js} +1 -1
- package/src/assets/web-panel/assets/{DID-fBHPa0QH.js → DID-DLZ1RDSr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-vvaUTafU.js → Dashboard-XneICKNI.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DyaQM63v.js → Dropdown-BRU00J1-.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CAF0wiU2.js → EmailListRenderer-BzCbJuAy.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-ia-WzDRs.js → FamilyGuardDashboard-D7BRyNBG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Cz3gHoT5.js → Federation-D9DeFIhW.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CJQDJr4Q.js → FormItemContext-6rAtA9Ko.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B05BZlQp.js → GenericCardRenderer-8Hqy80pU.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DT0YcQnI.js → Git-Cmvkj1rt.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Bqbl6m5Q.js → Governance-BRiQUjjw.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BdebLR0N.js → Inference-CKN_EzVf.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-M36b8YYV.js → KnowledgeGraph-BCDHwpwQ.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-5LN_XL43.js → Logs-DK9ovhaB.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BCb1Dm8l.js → MarkdownRenderer-D53pHiHy.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BkIin-QY.js → Marketplace-BILPGakp.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bkppx8Uj.js → McpTools-DTVCOr9-.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-Qk9wg_Ib.js → Memory-Hzc1iow4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BsTDU3Aj.js → MobileBridge-GXfgnGYV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CrZW5I28.js → MobileProjects-B-3TJWsS.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BMkmgtsA.js → Mtc-DwSJMWtK.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-6m4CC0Yn.js → MtcAudit-D2uxf-DA.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BZ8yB7QV.js → Multisig-DG6kbKK0.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CxitAMIO.js → NLProgramming-f5p49EDU.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Bv0msnHu.js → Notes-FUgHyzSD.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DeVMqM3P.js → NotificationSettings-DCHGee7e.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CXBSWa3R.js → OrderTableRenderer-Dm6LlIoa.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-b_G5Vs1Q.js → Organization-BawA7qAg.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DW7riOmA.js → Overflow-D6a5wZU1.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CtpQNYjL.js → P2P-K8WavwnA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Ba63yT4l.js → PdhVaultBrowser-4JAJYaqx.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DnzyeB67.js → Permissions-BDWvi8PD.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DbPZ7HWw.js → PersonalDataHub-DF_K4E9i.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-gs_HOThJ.js → Pipeline-BYeZlesI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CZlQ1EjE.js → Privacy-BIHlgnIc.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DQp__Ccb.js → ProjectInit-Cvf-6B5P.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C7h2sOWZ.js → ProjectSettings-CnwfJrRz.js} +2 -2
- package/src/assets/web-panel/assets/Projects-b-jPXzF4.js +1 -0
- package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-B_8M1-FC.js → QrScannerModal-VLf3gAfL.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bpycr8WD.js → QuickAsk-B5vGjRiF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Z4TrcNzs.js → Recommend--UW4iYs-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BD6TsJlS.js → RemoteSession-DMSPcfKR.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-IKxDXHEu.js → Reputation-kQXlEHmj.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DbmR20tJ.js → Row-CxGu3pit.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CLjA5PPf.js → RssFeed-Bi5lo8MZ.js} +2 -2
- package/src/assets/web-panel/assets/{Search-Bm3owbIU.js → Search-Cq_H5RTh.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CY4RyOuq.js → Security-YFbmkaQS.js} +2 -2
- package/src/assets/web-panel/assets/{Services-CYO9qrd_.js → Services-DLOGdaLm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ClF51Go6.js → Skeleton-CteIxb3D.js} +1 -1
- package/src/assets/web-panel/assets/Skills-CccopaIZ.js +1 -0
- package/src/assets/web-panel/assets/{Sla-zqpDUxzP.js → Sla-AzfEQKX1.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DPSi8Qco.js → SpeechSettings-ekGo5l77.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CqABopsQ.js → SyncSettings-BmZmAgYH.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-D7q3Z4uL.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Dzl6f0M2.js → Templates-CX1Tgeka.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-XeZkw40a.js → Tenant-Zc59jF4i.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CYw2YzPm.js → Terminal-CXOD7y83.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Bc2pko4i.js → TimelineRenderer-B1aBHPBo.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CfCenOHx.js → Tokens-zALlOq_U.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DTz-_YqV.js → Trigger-CQfJMXaq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CB3tt3Z6.js → Trust-BU0RcM-w.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-y1ykmMvU.js → UkeySign-IFZ3HsbH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-UlZ_lX0e.js → VideoEditing-BzslgW0v.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DMBaHTha.js → Wallet-BZAcQNS6.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Cksm-hBI.js → WebAuthn-CBK1DMi0.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BhOa40sX.js → WorkflowEditor-BlMTPzO-.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DNbeSTDO.js → chat-B51-8QVn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-9zz8gBmW.js → colors-504RTByU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7aoVv-5.js → compact-item-C-GP6IQM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DYg7B8nd.js → createContext-DOkIj7ID.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BNbhkcoI.js → hasIn-DN7V-_FW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeD1wWA8.js → index--n-ZciV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da0SysnX.js → index-7jIqHbzw.js} +1 -1
- package/src/assets/web-panel/assets/{index--TvoQ6UC.js → index-B2-x9bS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-m2pXS3W2.js → index-BFIFJ5Dr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzDDeZBo.js → index-B_vGU-Js.js} +1 -1
- package/src/assets/web-panel/assets/{index-gNKj00Fx.js → index-Bkxhaeta.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8LC0nvw.js → index-By6u5IV1.js} +1 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +1 -0
- package/src/assets/web-panel/assets/{index-B8irlIUK.js → index-C0k_Nqg9.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9zUawcE.js → index-C3TSohzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPojutcZ.js → index-C4DV_5uA.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZPDbEh-L.js → index-C8UltbXK.js} +1 -1
- package/src/assets/web-panel/assets/{index-jS6NASar.js → index-C9_48OzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-67GdrUx2.js → index-CKTfZyfU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYu-F-gp.js → index-CP2A226L.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cq3l0L7C.js → index-CUhQpiYv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtQzs0hH.js → index-CX78k-qf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bdxe0nrD.js → index-CYopNjcJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-uc8Rz96X.js → index-Cao5DZy7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnTM8FWK.js → index-CfiEM64n.js} +3 -3
- package/src/assets/web-panel/assets/{index-DHskwmwI.js → index-ConjaJro.js} +1 -1
- package/src/assets/web-panel/assets/{index-CA72qB1n.js → index-CqcLo5WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dn-JFqEi.js → index-CySTJKeq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUWmZ059.js → index-D1phyca8.js} +1 -1
- package/src/assets/web-panel/assets/{index-_-Vt1Wqk.js → index-D9LyiN-L.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIljkSt9.js → index-DA2f1UV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9TzVW7i.js → index-DAOpQvl_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRKukX1W.js → index-DE-hmI2l.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIMVe3HJ.js → index-DEMPUVa3.js} +1 -1
- package/src/assets/web-panel/assets/index-D_FWpDZM.js +1 -0
- package/src/assets/web-panel/assets/{index-FlBuITVG.js → index-DaA__-q1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cqy-hqD9.js → index-FcV8J_Qz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpEIwp4A.js → index-P2mfY27Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-nnmo3ipi.js → index-et5JIPHO.js} +1 -1
- package/src/assets/web-panel/assets/{index-SmEIFaRm.js → index-f4Bo4KXm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMrqEsOS.js → index-kAqQXfP6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CV06YdPO.js → index-kt_5rfcZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN6-OEgS.js → index-so8eIIxF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRd1zkLB.js → index-yYTQZypF.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D0mTMUGz.js → initDefaultProps-CPYEIdpV.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DK16p4-o.js → motion-Cjim9pxs.js} +1 -1
- package/src/assets/web-panel/assets/{move-DalC20HC.js → move-DuU-cgz3.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B5RusszF.js → mtc-parser-DJHNAkta.js} +1 -1
- package/src/assets/web-panel/assets/{omit-C9Mhitht.js → omit-DSQBgdBj.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-51xQLgwM.js → pickAttrs-CTmT_j5m.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CJzIqp8u.js → placementArrow-Lfm6v-r4.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-HQvC19u4.js → responsiveObserve-CgrcPcDi.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C6hhabOp.js → slide-CuE3110i.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Q1r48-oc.js → statusUtils-DiK5nbpf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-UakVWrHs.js → styleChecker-DPEJdQaZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dmv94cxx.js → useFlexGapSupport-CnnodGmp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DrxOnt_V.js → useFs-DWF84Yav.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BVf2M-Hg.js → usePersonalDataHub-a5b5Dw2f.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BzzW9qKH.js → vnode-DAluzXOq.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C1ZvMgAZ.js → zoom-Dokg7uAZ.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agenda.js +6 -5
- package/src/commands/agent.js +237 -75
- package/src/commands/agents.js +6 -0
- package/src/commands/background-session.js +11 -1
- package/src/commands/mcp.js +245 -30
- package/src/data/changelog.json +28 -2
- package/src/harness/mcp-client.js +256 -43
- package/src/harness/worktree-isolator.js +26 -3
- package/src/lib/agent-worktree.js +41 -2
- package/src/lib/background-agent-supervisor.js +790 -186
- package/src/lib/background-command-argv.js +318 -0
- package/src/lib/background-launch-profile.js +1 -0
- package/src/lib/background-phase-reporter.js +22 -2
- package/src/lib/background-worktree-policy.js +109 -0
- package/src/lib/cowork-mcp-tools.js +52 -6
- package/src/lib/cowork-task-runner.js +1 -0
- package/src/lib/cowork-task-templates.js +1 -0
- package/src/lib/mcp-headers-helper.js +76 -8
- package/src/lib/mcp-sandbox-policy.js +155 -0
- package/src/lib/mcp-stdio-executable-identity.js +75 -11
- package/src/lib/mcp-stdio-execution-authority.js +168 -0
- package/src/lib/mcp-stdio-workspace-authority.js +252 -0
- package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
- package/src/lib/process-execution-broker/index.js +495 -126
- package/src/lib/process-execution-broker/platform-sandbox.js +1424 -189
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +5 -4
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/skill-mcp.js +74 -3
- package/src/lib/web-fetch.js +4 -4
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/mcp-config.js +238 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/Projects-DdCpNRNt.js +0 -1
- package/src/assets/web-panel/assets/Providers-CyvtUkUf.js +0 -1
- package/src/assets/web-panel/assets/Skills-CCCEmSf5.js +0 -1
- package/src/assets/web-panel/assets/Tasks-4dbAJng4.js +0 -1
- package/src/assets/web-panel/assets/devWarning-uadaGF_y.js +0 -1
- package/src/assets/web-panel/assets/index-C7zhie9M.js +0 -1
- package/src/assets/web-panel/assets/index-Cmbg0Wbr.js +0 -1
|
@@ -11,7 +11,14 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import crypto from "node:crypto";
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
14
16
|
import { isProxy } from "node:util/types";
|
|
17
|
+
import {
|
|
18
|
+
currentHostHooksV2WorkspaceBinding,
|
|
19
|
+
resolveHostHooksV2WorkspaceBinding,
|
|
20
|
+
} from "./hooks-v2-workspace-context.js";
|
|
21
|
+
import { resolvePluginWorkspaceAuthority } from "./plugin-runtime/sandbox-policy.js";
|
|
15
22
|
|
|
16
23
|
export const MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE =
|
|
17
24
|
"CC_MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED";
|
|
@@ -155,6 +162,10 @@ function invocationSnapshot(serverName, config) {
|
|
|
155
162
|
command,
|
|
156
163
|
args: Object.freeze(args),
|
|
157
164
|
env: Object.freeze(env.map((entry) => Object.freeze(entry))),
|
|
165
|
+
runtimeKind: scalar(
|
|
166
|
+
dataProperty(config, "runtimeKind", null),
|
|
167
|
+
"runtimeKind",
|
|
168
|
+
),
|
|
158
169
|
transport: scalar(dataProperty(config, "transport", null), "transport"),
|
|
159
170
|
origin: scalar(dataProperty(config, "origin", null), "origin"),
|
|
160
171
|
policy: scalar(dataProperty(config, "policy", null), "policy"),
|
|
@@ -171,6 +182,7 @@ function invocationSnapshot(serverName, config) {
|
|
|
171
182
|
dataProperty(config, "projectPath", null),
|
|
172
183
|
"projectPath",
|
|
173
184
|
),
|
|
185
|
+
cwd: scalar(dataProperty(config, "cwd", null), "cwd"),
|
|
174
186
|
pluginId: scalar(dataProperty(config, "pluginId", null), "pluginId"),
|
|
175
187
|
pluginVersion: scalar(
|
|
176
188
|
dataProperty(config, "pluginVersion", null),
|
|
@@ -201,6 +213,8 @@ function comparableSnapshot(snapshot) {
|
|
|
201
213
|
projectMcpWorkspaceAuthority: _projectAuthority,
|
|
202
214
|
...plain
|
|
203
215
|
} = snapshot;
|
|
216
|
+
void _pluginAuthority;
|
|
217
|
+
void _projectAuthority;
|
|
204
218
|
return plain;
|
|
205
219
|
}
|
|
206
220
|
|
|
@@ -220,11 +234,122 @@ function sameSnapshot(expected, actual) {
|
|
|
220
234
|
);
|
|
221
235
|
}
|
|
222
236
|
|
|
237
|
+
function directoryGeneration(stat) {
|
|
238
|
+
return stat.birthtimeNs > 0n
|
|
239
|
+
? `birth:${stat.birthtimeNs.toString()}`
|
|
240
|
+
: `ctime:${stat.ctimeNs.toString()}`;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function captureDirectoryIdentity(value, base, label) {
|
|
244
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
|
|
245
|
+
throw new TypeError(`${label} must be a non-empty directory path`);
|
|
246
|
+
}
|
|
247
|
+
const requestedPath = path.isAbsolute(value)
|
|
248
|
+
? path.resolve(value)
|
|
249
|
+
: path.resolve(base, value);
|
|
250
|
+
let canonicalRoot;
|
|
251
|
+
let stat;
|
|
252
|
+
try {
|
|
253
|
+
const implementation = fs.realpathSync.native || fs.realpathSync;
|
|
254
|
+
canonicalRoot = implementation.call(fs.realpathSync, requestedPath);
|
|
255
|
+
stat = fs.statSync(canonicalRoot, { bigint: true });
|
|
256
|
+
} catch (cause) {
|
|
257
|
+
throw new TypeError(`${label} must resolve to an existing directory`, {
|
|
258
|
+
cause,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (!stat.isDirectory()) {
|
|
262
|
+
throw new TypeError(`${label} must resolve to a directory`);
|
|
263
|
+
}
|
|
264
|
+
return Object.freeze({
|
|
265
|
+
requestedPath,
|
|
266
|
+
canonicalRoot,
|
|
267
|
+
device: stat.dev,
|
|
268
|
+
inode: stat.ino,
|
|
269
|
+
generation: directoryGeneration(stat),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function sameDirectoryIdentity(expected, actual) {
|
|
274
|
+
return (
|
|
275
|
+
expected.canonicalRoot === actual.canonicalRoot &&
|
|
276
|
+
expected.device === actual.device &&
|
|
277
|
+
expected.inode === actual.inode &&
|
|
278
|
+
expected.generation === actual.generation
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function verifyDirectoryIdentity(binding) {
|
|
283
|
+
try {
|
|
284
|
+
return sameDirectoryIdentity(
|
|
285
|
+
binding,
|
|
286
|
+
captureDirectoryIdentity(
|
|
287
|
+
binding.requestedPath,
|
|
288
|
+
process.cwd(),
|
|
289
|
+
"approved MCP stdio cwd",
|
|
290
|
+
),
|
|
291
|
+
);
|
|
292
|
+
} catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function approvedWorkspaceRoot(snapshot, workspaceRoot, workspaceBinding) {
|
|
298
|
+
if (snapshot.origin === "plugin:mcp") {
|
|
299
|
+
const pluginRoot = resolvePluginWorkspaceAuthority(
|
|
300
|
+
snapshot.pluginWorkspaceAuthority,
|
|
301
|
+
{
|
|
302
|
+
origin: snapshot.origin,
|
|
303
|
+
pluginId: snapshot.pluginId,
|
|
304
|
+
pluginVersion: snapshot.pluginVersion,
|
|
305
|
+
pluginSource: snapshot.pluginSource,
|
|
306
|
+
},
|
|
307
|
+
);
|
|
308
|
+
if (pluginRoot) return pluginRoot;
|
|
309
|
+
}
|
|
310
|
+
if (workspaceBinding != null) {
|
|
311
|
+
const resolvedBinding =
|
|
312
|
+
resolveHostHooksV2WorkspaceBinding(workspaceBinding);
|
|
313
|
+
if (!resolvedBinding?.workspaceRoot) {
|
|
314
|
+
throw new TypeError(
|
|
315
|
+
"approved MCP stdio workspace binding is invalid or stale",
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
return resolvedBinding.workspaceRoot;
|
|
319
|
+
}
|
|
320
|
+
if (typeof workspaceRoot === "string" && workspaceRoot.length > 0) {
|
|
321
|
+
return workspaceRoot;
|
|
322
|
+
}
|
|
323
|
+
const hostRoot = currentHostHooksV2WorkspaceBinding()?.workspaceRoot;
|
|
324
|
+
if (hostRoot) return hostRoot;
|
|
325
|
+
if (typeof snapshot.projectPath === "string" && snapshot.projectPath) {
|
|
326
|
+
return snapshot.projectPath;
|
|
327
|
+
}
|
|
328
|
+
return process.cwd();
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function captureApprovedCwd(snapshot, workspaceRoot, workspaceBinding) {
|
|
332
|
+
if (snapshot.sandboxPolicy == null || snapshot.cwd == null) return null;
|
|
333
|
+
return captureDirectoryIdentity(
|
|
334
|
+
snapshot.cwd,
|
|
335
|
+
approvedWorkspaceRoot(snapshot, workspaceRoot, workspaceBinding),
|
|
336
|
+
"approved MCP stdio cwd",
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function staleDirectoryError(serverName) {
|
|
341
|
+
return authorityError(
|
|
342
|
+
MCP_STDIO_EXECUTION_AUTHORITY_STALE_CODE,
|
|
343
|
+
`MCP stdio working directory for "${String(serverName)}" changed after approval`,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
223
347
|
function materializeSnapshot(snapshot) {
|
|
224
348
|
return {
|
|
225
349
|
command: snapshot.command,
|
|
226
350
|
args: [...snapshot.args],
|
|
227
351
|
env: Object.fromEntries(snapshot.env),
|
|
352
|
+
runtimeKind: snapshot.runtimeKind,
|
|
228
353
|
transport: snapshot.transport,
|
|
229
354
|
origin: snapshot.origin,
|
|
230
355
|
policy: snapshot.policy,
|
|
@@ -232,6 +357,7 @@ function materializeSnapshot(snapshot) {
|
|
|
232
357
|
configScope: snapshot.configScope,
|
|
233
358
|
configSource: snapshot.configSource,
|
|
234
359
|
projectPath: snapshot.projectPath,
|
|
360
|
+
cwd: snapshot.cwd,
|
|
235
361
|
pluginId: snapshot.pluginId,
|
|
236
362
|
pluginVersion: snapshot.pluginVersion,
|
|
237
363
|
pluginSource: snapshot.pluginSource,
|
|
@@ -249,6 +375,8 @@ export function issueMcpStdioExecutionAuthority({
|
|
|
249
375
|
config,
|
|
250
376
|
approvalKind,
|
|
251
377
|
approvalSource,
|
|
378
|
+
workspaceRoot,
|
|
379
|
+
workspaceBinding,
|
|
252
380
|
}) {
|
|
253
381
|
if (!APPROVAL_KINDS.has(approvalKind)) {
|
|
254
382
|
throw new TypeError("MCP stdio approval kind is not recognized");
|
|
@@ -256,6 +384,11 @@ export function issueMcpStdioExecutionAuthority({
|
|
|
256
384
|
const source = scalar(approvalSource, "approvalSource", { nullable: false });
|
|
257
385
|
if (!source.trim()) throw new TypeError("approvalSource must not be empty");
|
|
258
386
|
const snapshot = invocationSnapshot(serverName, config);
|
|
387
|
+
const cwdIdentity = captureApprovedCwd(
|
|
388
|
+
snapshot,
|
|
389
|
+
workspaceRoot,
|
|
390
|
+
workspaceBinding,
|
|
391
|
+
);
|
|
259
392
|
const token = Object.freeze({});
|
|
260
393
|
issuedAuthorities.set(
|
|
261
394
|
token,
|
|
@@ -264,6 +397,7 @@ export function issueMcpStdioExecutionAuthority({
|
|
|
264
397
|
approvalSource: source,
|
|
265
398
|
fingerprint: snapshotDigest(snapshot),
|
|
266
399
|
snapshot,
|
|
400
|
+
cwdIdentity,
|
|
267
401
|
}),
|
|
268
402
|
);
|
|
269
403
|
return token;
|
|
@@ -304,6 +438,9 @@ export function consumeMcpStdioExecutionAuthority(
|
|
|
304
438
|
`MCP stdio invocation for "${String(serverName)}" changed after approval`,
|
|
305
439
|
);
|
|
306
440
|
}
|
|
441
|
+
if (issued.cwdIdentity && !verifyDirectoryIdentity(issued.cwdIdentity)) {
|
|
442
|
+
throw staleDirectoryError(serverName);
|
|
443
|
+
}
|
|
307
444
|
const approval = Object.freeze({
|
|
308
445
|
approvalKind: issued.approvalKind,
|
|
309
446
|
approvalSource: issued.approvalSource,
|
|
@@ -331,6 +468,9 @@ export function renewMcpStdioExecutionAuthority(
|
|
|
331
468
|
`MCP stdio reconnect invocation for "${String(serverName)}" changed after approval`,
|
|
332
469
|
);
|
|
333
470
|
}
|
|
471
|
+
if (issued.cwdIdentity && !verifyDirectoryIdentity(issued.cwdIdentity)) {
|
|
472
|
+
throw staleDirectoryError(serverName);
|
|
473
|
+
}
|
|
334
474
|
const token = Object.freeze({});
|
|
335
475
|
issuedAuthorities.set(token, issued);
|
|
336
476
|
return token;
|
|
@@ -347,6 +487,34 @@ export function materializeApprovedMcpStdioInvocation(approval) {
|
|
|
347
487
|
return materializeSnapshot(issued.snapshot);
|
|
348
488
|
}
|
|
349
489
|
|
|
490
|
+
export function verifyMcpStdioApprovedWorkingDirectory(
|
|
491
|
+
approval,
|
|
492
|
+
workingDirectory,
|
|
493
|
+
) {
|
|
494
|
+
const issued = renewableApprovals.get(approval);
|
|
495
|
+
if (!issued) {
|
|
496
|
+
throw authorityError(
|
|
497
|
+
MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE,
|
|
498
|
+
"MCP stdio working directory requires a consumed execution approval",
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
if (!issued.cwdIdentity) return true;
|
|
502
|
+
let actual;
|
|
503
|
+
try {
|
|
504
|
+
actual = captureDirectoryIdentity(
|
|
505
|
+
workingDirectory,
|
|
506
|
+
process.cwd(),
|
|
507
|
+
"materialized MCP stdio cwd",
|
|
508
|
+
);
|
|
509
|
+
} catch {
|
|
510
|
+
throw staleDirectoryError(issued.snapshot.serverName);
|
|
511
|
+
}
|
|
512
|
+
if (!sameDirectoryIdentity(issued.cwdIdentity, actual)) {
|
|
513
|
+
throw staleDirectoryError(issued.snapshot.serverName);
|
|
514
|
+
}
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
|
|
350
518
|
/**
|
|
351
519
|
* Return only the content-free provenance needed to key a durable executable
|
|
352
520
|
* identity. The approval object itself remains an unforgeable WeakMap-backed
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isProxy } from "node:util/types";
|
|
4
|
+
import { resolveHostHooksV2WorkspaceBinding } from "./hooks-v2-workspace-context.js";
|
|
5
|
+
import { normalizeMcpSandboxPolicy } from "./mcp-sandbox-policy.js";
|
|
6
|
+
import { resolvePluginWorkspaceAuthority } from "./plugin-runtime/sandbox-policy.js";
|
|
7
|
+
import { resolveProjectMcpWorkspaceAuthority } from "./project-mcp-trust.js";
|
|
8
|
+
|
|
9
|
+
export const MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE =
|
|
10
|
+
"CC_MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED";
|
|
11
|
+
export const MCP_STDIO_SANDBOX_CWD_INVALID_CODE =
|
|
12
|
+
"CC_MCP_STDIO_SANDBOX_CWD_INVALID";
|
|
13
|
+
|
|
14
|
+
function workspaceError(code, message, cause) {
|
|
15
|
+
const error = new Error(message, cause ? { cause } : undefined);
|
|
16
|
+
error.code = code;
|
|
17
|
+
return error;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function dataProperty(owner, key, fallback) {
|
|
21
|
+
if (!owner || typeof owner !== "object" || isProxy(owner)) {
|
|
22
|
+
throw workspaceError(
|
|
23
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
24
|
+
"MCP stdio sandbox config must be a non-Proxy object",
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
const descriptor = Object.getOwnPropertyDescriptor(owner, key);
|
|
28
|
+
if (!descriptor) return fallback;
|
|
29
|
+
if (!("value" in descriptor)) {
|
|
30
|
+
throw workspaceError(
|
|
31
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
32
|
+
`MCP stdio sandbox config.${key} must be an own data property`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return descriptor.value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function canonicalDirectory(value, label) {
|
|
39
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\0")) {
|
|
40
|
+
throw workspaceError(
|
|
41
|
+
MCP_STDIO_SANDBOX_CWD_INVALID_CODE,
|
|
42
|
+
`${label} must be a non-empty directory path`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const canonical = fs.realpathSync.native(path.resolve(value));
|
|
47
|
+
if (!fs.statSync(canonical).isDirectory()) {
|
|
48
|
+
throw new Error("not a directory");
|
|
49
|
+
}
|
|
50
|
+
return canonical;
|
|
51
|
+
} catch (cause) {
|
|
52
|
+
throw workspaceError(
|
|
53
|
+
MCP_STDIO_SANDBOX_CWD_INVALID_CODE,
|
|
54
|
+
`${label} must resolve to an existing directory`,
|
|
55
|
+
cause,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isPathInside(root, candidate) {
|
|
61
|
+
const relative = path.relative(root, candidate);
|
|
62
|
+
return (
|
|
63
|
+
relative === "" ||
|
|
64
|
+
(relative !== ".." &&
|
|
65
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
66
|
+
!path.isAbsolute(relative))
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function sandboxPolicyFromConfig(config, serverName) {
|
|
71
|
+
const raw = dataProperty(config, "sandboxPolicy", null);
|
|
72
|
+
return normalizeMcpSandboxPolicy(raw, {
|
|
73
|
+
label: `MCP stdio server "${serverName}" sandboxPolicy`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function pluginRootFromConfig(config, deps) {
|
|
78
|
+
if (dataProperty(config, "origin", null) !== "plugin:mcp") return null;
|
|
79
|
+
const resolvePlugin =
|
|
80
|
+
deps.resolvePluginWorkspaceAuthority || resolvePluginWorkspaceAuthority;
|
|
81
|
+
return resolvePlugin(dataProperty(config, "pluginWorkspaceAuthority", null), {
|
|
82
|
+
origin: "plugin:mcp",
|
|
83
|
+
pluginId: dataProperty(config, "pluginId", null),
|
|
84
|
+
pluginVersion: dataProperty(config, "pluginVersion", null),
|
|
85
|
+
pluginSource: dataProperty(config, "pluginSource", null),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isProjectFileSource(configScope, configSource) {
|
|
90
|
+
return (
|
|
91
|
+
configScope === "project" &&
|
|
92
|
+
typeof configSource === "string" &&
|
|
93
|
+
path.basename(configSource).toLowerCase() === ".mcp.json"
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function requirePublicWorkspaceRoot(
|
|
98
|
+
serverName,
|
|
99
|
+
config,
|
|
100
|
+
workspaceBinding,
|
|
101
|
+
deps,
|
|
102
|
+
) {
|
|
103
|
+
const resolveHost =
|
|
104
|
+
deps.resolveHostWorkspaceBinding || resolveHostHooksV2WorkspaceBinding;
|
|
105
|
+
const binding = resolveHost(workspaceBinding);
|
|
106
|
+
if (!binding?.workspaceRoot) {
|
|
107
|
+
throw workspaceError(
|
|
108
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
109
|
+
`Policy-bearing MCP stdio server "${serverName}" requires a trusted host workspace`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
const hostRoot = canonicalDirectory(
|
|
113
|
+
binding.workspaceRoot,
|
|
114
|
+
`MCP stdio server "${serverName}" host workspace`,
|
|
115
|
+
);
|
|
116
|
+
const configScope = dataProperty(config, "configScope", null);
|
|
117
|
+
const projectPath = dataProperty(config, "projectPath", null);
|
|
118
|
+
const configSource = dataProperty(config, "configSource", null);
|
|
119
|
+
const projectAuthority = dataProperty(
|
|
120
|
+
config,
|
|
121
|
+
"projectMcpWorkspaceAuthority",
|
|
122
|
+
null,
|
|
123
|
+
);
|
|
124
|
+
const projectFileSource = isProjectFileSource(configScope, configSource);
|
|
125
|
+
|
|
126
|
+
if (
|
|
127
|
+
configScope === "project" &&
|
|
128
|
+
(projectFileSource || projectAuthority) &&
|
|
129
|
+
!projectPath
|
|
130
|
+
) {
|
|
131
|
+
throw workspaceError(
|
|
132
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
133
|
+
`Project MCP stdio server "${serverName}" is missing its project workspace`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if ((configScope === "local" || configScope === "project") && projectPath) {
|
|
138
|
+
const projectRoot = canonicalDirectory(
|
|
139
|
+
projectPath,
|
|
140
|
+
`MCP stdio server "${serverName}" project workspace`,
|
|
141
|
+
);
|
|
142
|
+
if (!isPathInside(projectRoot, hostRoot)) {
|
|
143
|
+
throw workspaceError(
|
|
144
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
145
|
+
`MCP stdio server "${serverName}" does not belong to the trusted host workspace`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (projectAuthority || projectFileSource) {
|
|
150
|
+
const resolveProject =
|
|
151
|
+
deps.resolveProjectMcpWorkspaceAuthority ||
|
|
152
|
+
resolveProjectMcpWorkspaceAuthority;
|
|
153
|
+
const authorityRoot = resolveProject(projectAuthority, {
|
|
154
|
+
configSource,
|
|
155
|
+
serverName,
|
|
156
|
+
url: dataProperty(config, "url", null),
|
|
157
|
+
transport: dataProperty(config, "transport", null),
|
|
158
|
+
headersHelper: dataProperty(config, "headersHelper", null),
|
|
159
|
+
});
|
|
160
|
+
let canonicalAuthorityRoot = null;
|
|
161
|
+
try {
|
|
162
|
+
canonicalAuthorityRoot = authorityRoot
|
|
163
|
+
? canonicalDirectory(
|
|
164
|
+
authorityRoot,
|
|
165
|
+
`MCP stdio server "${serverName}" project authority`,
|
|
166
|
+
)
|
|
167
|
+
: null;
|
|
168
|
+
} catch {
|
|
169
|
+
canonicalAuthorityRoot = null;
|
|
170
|
+
}
|
|
171
|
+
if (canonicalAuthorityRoot !== projectRoot) {
|
|
172
|
+
throw workspaceError(
|
|
173
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
174
|
+
`Project MCP stdio server "${serverName}" is missing its trusted file authority`,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return hostRoot;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function requestedWorkingDirectory(config, trustedRoot, serverName) {
|
|
184
|
+
const requested = dataProperty(config, "cwd", null);
|
|
185
|
+
if (requested == null || requested === "") return trustedRoot;
|
|
186
|
+
if (typeof requested !== "string" || requested.includes("\0")) {
|
|
187
|
+
throw workspaceError(
|
|
188
|
+
MCP_STDIO_SANDBOX_CWD_INVALID_CODE,
|
|
189
|
+
`MCP stdio server "${serverName}" cwd must be a path`,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
const candidate = canonicalDirectory(
|
|
193
|
+
path.isAbsolute(requested)
|
|
194
|
+
? requested
|
|
195
|
+
: path.resolve(trustedRoot, requested),
|
|
196
|
+
`MCP stdio server "${serverName}" cwd`,
|
|
197
|
+
);
|
|
198
|
+
if (!isPathInside(trustedRoot, candidate)) {
|
|
199
|
+
throw workspaceError(
|
|
200
|
+
MCP_STDIO_SANDBOX_CWD_INVALID_CODE,
|
|
201
|
+
`MCP stdio server "${serverName}" cwd escapes its trusted workspace`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
return candidate;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Resolve source-declared MCP isolation against an opaque host/plugin
|
|
209
|
+
* workspace capability. A policy-bearing launch never derives authority from
|
|
210
|
+
* process.cwd(); the selected cwd is canonical, existing, and contained by the
|
|
211
|
+
* trusted root before executable identity or Broker contract issuance.
|
|
212
|
+
*/
|
|
213
|
+
export function resolveMcpStdioSandboxContext(
|
|
214
|
+
{ serverName, config, workspaceBinding },
|
|
215
|
+
deps = {},
|
|
216
|
+
) {
|
|
217
|
+
const name = String(serverName || "unknown");
|
|
218
|
+
const sandboxPolicy = sandboxPolicyFromConfig(config, name);
|
|
219
|
+
const pluginWorkspaceRoot = pluginRootFromConfig(config, deps);
|
|
220
|
+
|
|
221
|
+
if (!sandboxPolicy) {
|
|
222
|
+
return Object.freeze({
|
|
223
|
+
sandboxPolicy: null,
|
|
224
|
+
pluginWorkspaceRoot,
|
|
225
|
+
workingDirectory: pluginWorkspaceRoot,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (
|
|
230
|
+
dataProperty(config, "origin", null) === "plugin:mcp" &&
|
|
231
|
+
!pluginWorkspaceRoot
|
|
232
|
+
) {
|
|
233
|
+
throw workspaceError(
|
|
234
|
+
MCP_STDIO_SANDBOX_WORKSPACE_REQUIRED_CODE,
|
|
235
|
+
`Plugin MCP stdio server "${name}" is missing its trusted workspace authority`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const trustedRoot = pluginWorkspaceRoot
|
|
240
|
+
? canonicalDirectory(
|
|
241
|
+
pluginWorkspaceRoot,
|
|
242
|
+
`Plugin MCP stdio server "${name}" workspace`,
|
|
243
|
+
)
|
|
244
|
+
: requirePublicWorkspaceRoot(name, config, workspaceBinding, deps);
|
|
245
|
+
const workingDirectory = requestedWorkingDirectory(config, trustedRoot, name);
|
|
246
|
+
|
|
247
|
+
return Object.freeze({
|
|
248
|
+
sandboxPolicy,
|
|
249
|
+
pluginWorkspaceRoot,
|
|
250
|
+
workingDirectory,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
@@ -20,6 +20,28 @@ const SUPPORTED_BOUNDARIES = new Set(PLUGIN_SANDBOX_BOUNDARIES);
|
|
|
20
20
|
const PLUGIN_WORKSPACE_AUTHORITY_KIND = "trusted-plugin-workspace";
|
|
21
21
|
const pluginWorkspaceAuthorities = new WeakMap();
|
|
22
22
|
|
|
23
|
+
function directoryGeneration(stat) {
|
|
24
|
+
return stat.birthtimeNs > 0n
|
|
25
|
+
? `birth:${stat.birthtimeNs.toString()}`
|
|
26
|
+
: `ctime:${stat.ctimeNs.toString()}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function directoryIdentity(stat) {
|
|
30
|
+
return Object.freeze({
|
|
31
|
+
device: stat.dev,
|
|
32
|
+
inode: stat.ino,
|
|
33
|
+
generation: directoryGeneration(stat),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function sameDirectoryIdentity(left, right) {
|
|
38
|
+
return (
|
|
39
|
+
left.device === right.dev &&
|
|
40
|
+
left.inode === right.ino &&
|
|
41
|
+
left.generation === directoryGeneration(right)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
23
45
|
function invalidPolicy(message) {
|
|
24
46
|
const error = new Error(message);
|
|
25
47
|
error.code = "ERR_PLUGIN_SANDBOX_POLICY_INVALID";
|
|
@@ -69,7 +91,7 @@ export function issuePluginWorkspaceAuthority(value = {}) {
|
|
|
69
91
|
try {
|
|
70
92
|
const implementation = fs.realpathSync.native || fs.realpathSync;
|
|
71
93
|
canonicalRoot = implementation.call(fs.realpathSync, requestedRoot);
|
|
72
|
-
stat = fs.statSync(canonicalRoot);
|
|
94
|
+
stat = fs.statSync(canonicalRoot, { bigint: true });
|
|
73
95
|
} catch {
|
|
74
96
|
throw invalidPolicy("trusted plugin workspace root is unavailable");
|
|
75
97
|
}
|
|
@@ -92,7 +114,11 @@ export function issuePluginWorkspaceAuthority(value = {}) {
|
|
|
92
114
|
});
|
|
93
115
|
pluginWorkspaceAuthorities.set(
|
|
94
116
|
authority,
|
|
95
|
-
Object.freeze({
|
|
117
|
+
Object.freeze({
|
|
118
|
+
root: canonicalRoot,
|
|
119
|
+
identity: directoryIdentity(stat),
|
|
120
|
+
provenance,
|
|
121
|
+
}),
|
|
96
122
|
);
|
|
97
123
|
return authority;
|
|
98
124
|
}
|
|
@@ -117,6 +143,20 @@ export function resolvePluginWorkspaceAuthority(authority, provenance = {}) {
|
|
|
117
143
|
) {
|
|
118
144
|
return null;
|
|
119
145
|
}
|
|
146
|
+
try {
|
|
147
|
+
const implementation = fs.realpathSync.native || fs.realpathSync;
|
|
148
|
+
const currentRoot = implementation.call(fs.realpathSync, binding.root);
|
|
149
|
+
const currentStat = fs.statSync(currentRoot, { bigint: true });
|
|
150
|
+
if (
|
|
151
|
+
currentRoot !== binding.root ||
|
|
152
|
+
!currentStat.isDirectory() ||
|
|
153
|
+
!sameDirectoryIdentity(binding.identity, currentStat)
|
|
154
|
+
) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
120
160
|
return binding.root;
|
|
121
161
|
}
|
|
122
162
|
|