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
|
@@ -108,6 +108,13 @@ const WINDOWS_APPCONTAINER_BACKEND =
|
|
|
108
108
|
"windows-appcontainer-job-restricted-token";
|
|
109
109
|
const MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND = "strict-mcp-node-capsule";
|
|
110
110
|
const WINDOWS_ADAPTER_IDLE_TTL_MS = 60_000;
|
|
111
|
+
const WINDOWS_ADAPTER_HELPER_NAME = "windows-sandbox-helper.exe";
|
|
112
|
+
const WINDOWS_ADAPTER_IDLE_TTL_ENV = "CC_WINDOWS_SANDBOX_ADAPTER_IDLE_TTL_MS";
|
|
113
|
+
const WINDOWS_ADAPTER_TEMP_ROOT_ENV = "CC_WINDOWS_SANDBOX_ADAPTER_TEMP_ROOT";
|
|
114
|
+
const WINDOWS_ADAPTER_TEMP_ROOT_UNTRUSTED_REASON =
|
|
115
|
+
"windows_adapter_temp_root_untrusted";
|
|
116
|
+
const WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON =
|
|
117
|
+
"windows_adapter_temp_root_changed";
|
|
111
118
|
// Every native helper operation starts a trusted System32 PowerShell host and
|
|
112
119
|
// byte-loads the checked-in assembly. Hosted Windows runners can spend well
|
|
113
120
|
// over ten seconds in that bootstrap before the managed operation begins.
|
|
@@ -169,11 +176,18 @@ const LINUX_EXECUTABLE_SNAPSHOT_SOURCE_MODE = 0o500;
|
|
|
169
176
|
const LINUX_MCP_CAPSULE_BACKEND = "linux-fd-code-snapshot";
|
|
170
177
|
const LINUX_MCP_RUNTIME_SNAPSHOT_SCOPE = "mcp-node-runtime-executable";
|
|
171
178
|
const LINUX_MCP_CAPSULE_SNAPSHOT_MECHANISM =
|
|
172
|
-
"verified-o_tmpfile-copy-inherited-fd-
|
|
179
|
+
"verified-o_tmpfile-copy-inherited-fd-module-compile-v1";
|
|
180
|
+
const MACOS_MCP_CAPSULE_BACKEND = "macos-fd-code-snapshot";
|
|
181
|
+
export const MCP_STDIO_FD_ENTRY_BOOTSTRAP =
|
|
182
|
+
'const fs=require("node:fs");const Module=require("node:module");const filename="/chainlesschain/mcp-capsule.cjs";const source=fs.readFileSync(4,"utf8");process.argv.splice(1,0,filename);const target=new Module(filename,module);target.filename=filename;target.paths=[];target._compile(source,filename);';
|
|
183
|
+
export const MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256 = crypto
|
|
184
|
+
.createHash("sha256")
|
|
185
|
+
.update(MCP_STDIO_FD_ENTRY_BOOTSTRAP)
|
|
186
|
+
.digest("hex");
|
|
173
187
|
const LINUX_NODE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-source";
|
|
174
188
|
const LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-executable";
|
|
175
|
-
const
|
|
176
|
-
const
|
|
189
|
+
const LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE = "all-pinned-plugin-regular-files";
|
|
190
|
+
const LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY = "per-file-pin-to-launch";
|
|
177
191
|
const LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE = "0400";
|
|
178
192
|
const LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE = "0500";
|
|
179
193
|
const LINUX_ELF64_HEADER_BYTES = 64;
|
|
@@ -283,6 +297,14 @@ const windowsAdapterCacheEntries = new Set();
|
|
|
283
297
|
const windowsTemporaryCleanupBacklog = new Set();
|
|
284
298
|
let windowsTemporaryCleanupRetryTimer = null;
|
|
285
299
|
const windowsAppContainerCleanupBacklog = new Set();
|
|
300
|
+
const issuedWindowsMcpCodeSnapshotPlans = new WeakMap();
|
|
301
|
+
// Only the unified production adapter can register a Windows MCP launch
|
|
302
|
+
// capability. Direct callers of the exported platform-specific helper (and
|
|
303
|
+
// callers that inject a test/runtime facade into applySandbox) may exercise
|
|
304
|
+
// plan construction, but cannot mint a Broker-admissible capability.
|
|
305
|
+
const WINDOWS_MCP_CODE_SNAPSHOT_ISSUER = Symbol(
|
|
306
|
+
"windows-mcp-code-snapshot-issuer",
|
|
307
|
+
);
|
|
286
308
|
// Each AppContainer retry starts a synchronous, digest-attested PowerShell
|
|
287
309
|
// helper. Bound automatic retries so a permanently unsupported Windows host
|
|
288
310
|
// cannot repeatedly block a long-lived CLI; explicit cleanup/reset and process
|
|
@@ -296,6 +318,325 @@ function resolveRuntime(overrides = {}) {
|
|
|
296
318
|
return { ...DEFAULT_RUNTIME, ...overrides };
|
|
297
319
|
}
|
|
298
320
|
|
|
321
|
+
function windowsNativeRealpath(runtime, candidate) {
|
|
322
|
+
const nativeRealpath = runtime.fs.realpathSync?.native;
|
|
323
|
+
if (typeof nativeRealpath !== "function") {
|
|
324
|
+
throw new Error("Native Windows realpath is unavailable");
|
|
325
|
+
}
|
|
326
|
+
const nativePath = nativeRealpath.call(runtime.fs, candidate);
|
|
327
|
+
const withoutExtendedPrefix = String(nativePath).replace(
|
|
328
|
+
/^\\\\\?\\(?=[A-Za-z]:\\)/,
|
|
329
|
+
"",
|
|
330
|
+
);
|
|
331
|
+
return path.win32.resolve(withoutExtendedPrefix);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function windowsPathHasReparseSemantics(stat) {
|
|
335
|
+
return (
|
|
336
|
+
stat.isSymbolicLink?.() ||
|
|
337
|
+
stat.isReparsePoint?.() ||
|
|
338
|
+
stat.reparsePoint === true
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function windowsLocalPathAncestors(candidate) {
|
|
343
|
+
const resolved = path.win32.resolve(candidate);
|
|
344
|
+
const volumeRoot = path.win32.parse(resolved).root;
|
|
345
|
+
if (!/^[A-Za-z]:\\$/.test(volumeRoot)) return null;
|
|
346
|
+
const relative = path.win32.relative(volumeRoot, resolved);
|
|
347
|
+
if (
|
|
348
|
+
path.win32.isAbsolute(relative) ||
|
|
349
|
+
relative === ".." ||
|
|
350
|
+
relative.startsWith(`..${path.win32.sep}`)
|
|
351
|
+
) {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
const ancestors = [volumeRoot];
|
|
355
|
+
let current = volumeRoot;
|
|
356
|
+
for (const segment of relative.split(path.win32.sep).filter(Boolean)) {
|
|
357
|
+
current = path.win32.join(current, segment);
|
|
358
|
+
ancestors.push(current);
|
|
359
|
+
}
|
|
360
|
+
return ancestors;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function windowsDirectoryIdentity(stat) {
|
|
364
|
+
const birthtimeNs =
|
|
365
|
+
stat.birthtimeNs !== undefined
|
|
366
|
+
? String(stat.birthtimeNs)
|
|
367
|
+
: String(Math.trunc(Number(stat.birthtimeMs || 0) * 1_000_000));
|
|
368
|
+
if (
|
|
369
|
+
stat.dev === undefined ||
|
|
370
|
+
stat.ino === undefined ||
|
|
371
|
+
stat.mode === undefined
|
|
372
|
+
) {
|
|
373
|
+
throw new Error("Windows directory identity is unavailable");
|
|
374
|
+
}
|
|
375
|
+
return Object.freeze({
|
|
376
|
+
dev: String(stat.dev),
|
|
377
|
+
ino: String(stat.ino),
|
|
378
|
+
mode: String(stat.mode),
|
|
379
|
+
birthtimeNs,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function sameWindowsDirectoryIdentity(left, right) {
|
|
384
|
+
return (
|
|
385
|
+
left?.dev === right?.dev &&
|
|
386
|
+
left?.ino === right?.ino &&
|
|
387
|
+
left?.mode === right?.mode &&
|
|
388
|
+
left?.birthtimeNs === right?.birthtimeNs
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function inspectWindowsAdapterTempRoot(runtime, candidate) {
|
|
393
|
+
if (
|
|
394
|
+
typeof candidate !== "string" ||
|
|
395
|
+
candidate.length === 0 ||
|
|
396
|
+
candidate.includes("\0") ||
|
|
397
|
+
!path.win32.isAbsolute(candidate)
|
|
398
|
+
) {
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
const sourcePath = path.win32.resolve(candidate);
|
|
402
|
+
const sourceAncestors = windowsLocalPathAncestors(sourcePath);
|
|
403
|
+
if (!sourceAncestors) return null;
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
// Keep the caller-visible path in the trust decision even when Windows
|
|
407
|
+
// exposes it through a legitimate 8.3 alias (for example RUNNER~1 on a
|
|
408
|
+
// hosted runner). Every raw ancestor must be a plain directory before the
|
|
409
|
+
// native real path is accepted as the path used for mutations.
|
|
410
|
+
let sourceRootStat = null;
|
|
411
|
+
for (const ancestor of sourceAncestors) {
|
|
412
|
+
const stat = runtime.fs.lstatSync(ancestor, { bigint: true });
|
|
413
|
+
if (windowsPathHasReparseSemantics(stat) || !stat.isDirectory?.()) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
sourceRootStat = stat;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const rootPath = windowsNativeRealpath(runtime, sourcePath);
|
|
420
|
+
const rootAncestors = windowsLocalPathAncestors(rootPath);
|
|
421
|
+
if (!rootAncestors) return null;
|
|
422
|
+
let canonicalRootStat = null;
|
|
423
|
+
for (const ancestor of rootAncestors) {
|
|
424
|
+
const stat = runtime.fs.lstatSync(ancestor, { bigint: true });
|
|
425
|
+
if (windowsPathHasReparseSemantics(stat) || !stat.isDirectory?.()) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
canonicalRootStat = stat;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const sourceIdentity = windowsDirectoryIdentity(sourceRootStat);
|
|
432
|
+
const identity = windowsDirectoryIdentity(canonicalRootStat);
|
|
433
|
+
if (!sameWindowsDirectoryIdentity(sourceIdentity, identity)) return null;
|
|
434
|
+
|
|
435
|
+
const sourceAfter = runtime.fs.lstatSync(sourcePath, { bigint: true });
|
|
436
|
+
const rootAfter = runtime.fs.lstatSync(rootPath, { bigint: true });
|
|
437
|
+
if (
|
|
438
|
+
windowsPathHasReparseSemantics(sourceAfter) ||
|
|
439
|
+
!sourceAfter.isDirectory?.() ||
|
|
440
|
+
windowsPathHasReparseSemantics(rootAfter) ||
|
|
441
|
+
!rootAfter.isDirectory?.() ||
|
|
442
|
+
windowsCanonicalPathKey(windowsNativeRealpath(runtime, sourcePath)) !==
|
|
443
|
+
windowsCanonicalPathKey(rootPath) ||
|
|
444
|
+
windowsCanonicalPathKey(windowsNativeRealpath(runtime, rootPath)) !==
|
|
445
|
+
windowsCanonicalPathKey(rootPath) ||
|
|
446
|
+
!sameWindowsDirectoryIdentity(
|
|
447
|
+
sourceIdentity,
|
|
448
|
+
windowsDirectoryIdentity(sourceAfter),
|
|
449
|
+
) ||
|
|
450
|
+
!sameWindowsDirectoryIdentity(
|
|
451
|
+
identity,
|
|
452
|
+
windowsDirectoryIdentity(rootAfter),
|
|
453
|
+
)
|
|
454
|
+
) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
return Object.freeze({
|
|
458
|
+
sourcePath,
|
|
459
|
+
sourceCanonicalPathKey: windowsCanonicalPathKey(sourcePath),
|
|
460
|
+
rootPath,
|
|
461
|
+
canonicalPathKey: windowsCanonicalPathKey(rootPath),
|
|
462
|
+
volumeRoot: path.win32.parse(rootPath).root.toLowerCase(),
|
|
463
|
+
identity,
|
|
464
|
+
attestation: Object.freeze({
|
|
465
|
+
kind: "windows-adapter-temp-root-path-reattestation-v1",
|
|
466
|
+
localVolume: true,
|
|
467
|
+
nativeRealpathMatched: true,
|
|
468
|
+
sourceAliasCanonicalized:
|
|
469
|
+
windowsCanonicalPathKey(sourcePath) !==
|
|
470
|
+
windowsCanonicalPathKey(rootPath),
|
|
471
|
+
ancestorReparsePointsRejected: true,
|
|
472
|
+
rootIdentityBound: true,
|
|
473
|
+
criticalOperationReattestation: true,
|
|
474
|
+
descriptorRelativeOperations: false,
|
|
475
|
+
handleAtomic: false,
|
|
476
|
+
residualRace:
|
|
477
|
+
"same-principal-path-swap-between-reattestation-and-filesystem-call",
|
|
478
|
+
residualHandling: "fail-closed-on-detected-path-or-identity-change",
|
|
479
|
+
}),
|
|
480
|
+
});
|
|
481
|
+
} catch {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function sameWindowsAdapterTempRootBinding(left, right) {
|
|
487
|
+
return (
|
|
488
|
+
left?.sourceCanonicalPathKey === right?.sourceCanonicalPathKey &&
|
|
489
|
+
left?.canonicalPathKey === right?.canonicalPathKey &&
|
|
490
|
+
left?.volumeRoot === right?.volumeRoot &&
|
|
491
|
+
sameWindowsDirectoryIdentity(left?.identity, right?.identity)
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function verifyWindowsAdapterTempRootBinding(runtime, binding) {
|
|
496
|
+
const current = inspectWindowsAdapterTempRoot(
|
|
497
|
+
runtime,
|
|
498
|
+
binding?.sourcePath || binding?.rootPath,
|
|
499
|
+
);
|
|
500
|
+
return Boolean(
|
|
501
|
+
current && sameWindowsAdapterTempRootBinding(binding, current),
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function windowsAdapterTempRootChangedError() {
|
|
506
|
+
const error = new Error(
|
|
507
|
+
"Windows sandbox adapter temp root changed after attestation",
|
|
508
|
+
);
|
|
509
|
+
error.code = "ERR_WINDOWS_SANDBOX_ADAPTER_TEMP_ROOT_CHANGED";
|
|
510
|
+
error.adapterReason = WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON;
|
|
511
|
+
return error;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function assertWindowsAdapterTempRoot(runtime) {
|
|
515
|
+
const binding = runtime.windowsAdapterTempRootBinding;
|
|
516
|
+
if (!binding || !verifyWindowsAdapterTempRootBinding(runtime, binding)) {
|
|
517
|
+
throw windowsAdapterTempRootChangedError();
|
|
518
|
+
}
|
|
519
|
+
return binding;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
function inspectWindowsAdapterBoundPath(
|
|
523
|
+
runtime,
|
|
524
|
+
targetPath,
|
|
525
|
+
expectedKind,
|
|
526
|
+
{ allowMissing = false } = {},
|
|
527
|
+
) {
|
|
528
|
+
const binding = assertWindowsAdapterTempRoot(runtime);
|
|
529
|
+
if (
|
|
530
|
+
typeof targetPath !== "string" ||
|
|
531
|
+
targetPath.includes("\0") ||
|
|
532
|
+
!path.win32.isAbsolute(targetPath)
|
|
533
|
+
) {
|
|
534
|
+
throw windowsAdapterTempRootChangedError();
|
|
535
|
+
}
|
|
536
|
+
const resolved = path.win32.resolve(targetPath);
|
|
537
|
+
const relative = path.win32.relative(binding.rootPath, resolved);
|
|
538
|
+
if (
|
|
539
|
+
!relative ||
|
|
540
|
+
path.win32.isAbsolute(relative) ||
|
|
541
|
+
relative === ".." ||
|
|
542
|
+
relative.startsWith(`..${path.win32.sep}`)
|
|
543
|
+
) {
|
|
544
|
+
throw windowsAdapterTempRootChangedError();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const segments = relative.split(path.win32.sep).filter(Boolean);
|
|
548
|
+
let current = binding.rootPath;
|
|
549
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
550
|
+
current = path.win32.join(current, segments[index]);
|
|
551
|
+
const isTarget = index === segments.length - 1;
|
|
552
|
+
let stat;
|
|
553
|
+
try {
|
|
554
|
+
stat = runtime.fs.lstatSync(current, { bigint: true });
|
|
555
|
+
} catch (error) {
|
|
556
|
+
if (isTarget && allowMissing && error?.code === "ENOENT") {
|
|
557
|
+
return Object.freeze({ exists: false, resolvedPath: resolved });
|
|
558
|
+
}
|
|
559
|
+
throw error;
|
|
560
|
+
}
|
|
561
|
+
if (
|
|
562
|
+
windowsPathHasReparseSemantics(stat) ||
|
|
563
|
+
windowsCanonicalPathKey(windowsNativeRealpath(runtime, current)) !==
|
|
564
|
+
windowsCanonicalPathKey(current)
|
|
565
|
+
) {
|
|
566
|
+
throw windowsAdapterTempRootChangedError();
|
|
567
|
+
}
|
|
568
|
+
if (!isTarget && !stat.isDirectory?.()) {
|
|
569
|
+
throw windowsAdapterTempRootChangedError();
|
|
570
|
+
}
|
|
571
|
+
if (isTarget) {
|
|
572
|
+
if (
|
|
573
|
+
(expectedKind === "file" && !stat.isFile?.()) ||
|
|
574
|
+
(expectedKind === "directory" && !stat.isDirectory?.())
|
|
575
|
+
) {
|
|
576
|
+
throw windowsAdapterTempRootChangedError();
|
|
577
|
+
}
|
|
578
|
+
return Object.freeze({
|
|
579
|
+
exists: true,
|
|
580
|
+
resolvedPath: resolved,
|
|
581
|
+
identity:
|
|
582
|
+
expectedKind === "directory"
|
|
583
|
+
? windowsDirectoryIdentity(stat)
|
|
584
|
+
: windowsFileIdentity(stat),
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
throw windowsAdapterTempRootChangedError();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function assertWindowsAdapterBoundPathIdentity(
|
|
592
|
+
runtime,
|
|
593
|
+
targetPath,
|
|
594
|
+
expectedKind,
|
|
595
|
+
expectedIdentity,
|
|
596
|
+
) {
|
|
597
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
598
|
+
runtime,
|
|
599
|
+
targetPath,
|
|
600
|
+
expectedKind,
|
|
601
|
+
);
|
|
602
|
+
const matches =
|
|
603
|
+
expectedKind === "directory"
|
|
604
|
+
? sameWindowsDirectoryIdentity(inspected.identity, expectedIdentity)
|
|
605
|
+
: sameWindowsFileIdentity(inspected.identity, expectedIdentity);
|
|
606
|
+
if (!matches) throw windowsAdapterTempRootChangedError();
|
|
607
|
+
return inspected;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function resolveWindowsAdapterTempRoot(runtime, runtimeOverrides = {}) {
|
|
611
|
+
const hasExplicitTmpdir = Object.prototype.hasOwnProperty.call(
|
|
612
|
+
runtimeOverrides,
|
|
613
|
+
"tmpdir",
|
|
614
|
+
);
|
|
615
|
+
const hasExplicitAdapterRoot = Object.prototype.hasOwnProperty.call(
|
|
616
|
+
runtimeOverrides,
|
|
617
|
+
"windowsAdapterTempRoot",
|
|
618
|
+
);
|
|
619
|
+
let candidate;
|
|
620
|
+
try {
|
|
621
|
+
if (hasExplicitTmpdir) {
|
|
622
|
+
if (typeof runtime.tmpdir !== "function") return null;
|
|
623
|
+
candidate = runtime.tmpdir();
|
|
624
|
+
} else if (hasExplicitAdapterRoot) {
|
|
625
|
+
candidate = runtimeOverrides.windowsAdapterTempRoot;
|
|
626
|
+
} else {
|
|
627
|
+
const environmentRoot =
|
|
628
|
+
runtime.env?.[WINDOWS_ADAPTER_TEMP_ROOT_ENV] ??
|
|
629
|
+
process.env[WINDOWS_ADAPTER_TEMP_ROOT_ENV];
|
|
630
|
+
candidate =
|
|
631
|
+
environmentRoot === undefined ? runtime.tmpdir() : environmentRoot;
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return inspectWindowsAdapterTempRoot(runtime, candidate);
|
|
638
|
+
}
|
|
639
|
+
|
|
299
640
|
function normalizeSandboxRequest(profileOrRequest, explicitRequest) {
|
|
300
641
|
const legacyRequest =
|
|
301
642
|
profileOrRequest &&
|
|
@@ -453,6 +794,8 @@ export function generateMacSeatbeltProfile(opts = {}, runtimeOverrides = {}) {
|
|
|
453
794
|
allowNetwork = false,
|
|
454
795
|
allowExec = true,
|
|
455
796
|
} = opts;
|
|
797
|
+
const seatbeltLiteral = (value) =>
|
|
798
|
+
String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
456
799
|
|
|
457
800
|
const lines = [
|
|
458
801
|
"(version 1)",
|
|
@@ -485,12 +828,12 @@ export function generateMacSeatbeltProfile(opts = {}, runtimeOverrides = {}) {
|
|
|
485
828
|
|
|
486
829
|
// Allow read/write to specific paths
|
|
487
830
|
for (const p of allowRead) {
|
|
488
|
-
const abs = runtime.resolvePath(p);
|
|
831
|
+
const abs = seatbeltLiteral(runtime.resolvePath(p));
|
|
489
832
|
lines.push(`(allow file-read* (subpath "${abs}"))`);
|
|
490
833
|
}
|
|
491
834
|
|
|
492
835
|
for (const p of allowWrite) {
|
|
493
|
-
const abs = runtime.resolvePath(p);
|
|
836
|
+
const abs = seatbeltLiteral(runtime.resolvePath(p));
|
|
494
837
|
lines.push(`(allow file-read* file-write* (subpath "${abs}"))`);
|
|
495
838
|
}
|
|
496
839
|
|
|
@@ -511,6 +854,144 @@ export function generateMacSeatbeltProfile(opts = {}, runtimeOverrides = {}) {
|
|
|
511
854
|
return lines.join("\n");
|
|
512
855
|
}
|
|
513
856
|
|
|
857
|
+
function validateMacMcpCapsuleContract(
|
|
858
|
+
command,
|
|
859
|
+
args,
|
|
860
|
+
spawnOpts,
|
|
861
|
+
contract,
|
|
862
|
+
runtime,
|
|
863
|
+
sync,
|
|
864
|
+
) {
|
|
865
|
+
if (
|
|
866
|
+
contract?.kind !== MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND ||
|
|
867
|
+
contract.contractVersion !== 1 ||
|
|
868
|
+
(sync !== true && sync !== false) ||
|
|
869
|
+
spawnOpts?.shell !== false ||
|
|
870
|
+
!linuxStdioIsNarrow(spawnOpts?.stdio) ||
|
|
871
|
+
spawnOpts?.serialization !== undefined ||
|
|
872
|
+
spawnOpts?.argv0 !== undefined ||
|
|
873
|
+
spawnOpts?.uid !== undefined ||
|
|
874
|
+
spawnOpts?.gid !== undefined ||
|
|
875
|
+
!Array.isArray(args) ||
|
|
876
|
+
args.length < 1 ||
|
|
877
|
+
args.some((value) => typeof value !== "string" || value.includes("\0")) ||
|
|
878
|
+
command !== contract.runtimePath ||
|
|
879
|
+
command !== contract.runtimeIdentity?.realPath ||
|
|
880
|
+
args[0] !== contract.entryIdentity?.realPath ||
|
|
881
|
+
![".js", ".cjs", ".mjs"].includes(
|
|
882
|
+
path.posix.extname(contract.entryIdentity?.realPath || "").toLowerCase(),
|
|
883
|
+
)
|
|
884
|
+
) {
|
|
885
|
+
return { ok: false, reason: "launch_identity_mismatch" };
|
|
886
|
+
}
|
|
887
|
+
for (const value of [
|
|
888
|
+
contract.pluginRoot,
|
|
889
|
+
contract.workingDirectory,
|
|
890
|
+
contract.rootIdentity?.realPath,
|
|
891
|
+
contract.entryIdentity?.realPath,
|
|
892
|
+
contract.runtimeIdentity?.realPath,
|
|
893
|
+
]) {
|
|
894
|
+
if (
|
|
895
|
+
typeof value !== "string" ||
|
|
896
|
+
!path.posix.isAbsolute(value) ||
|
|
897
|
+
value.includes("\0")
|
|
898
|
+
) {
|
|
899
|
+
return { ok: false, reason: "noncanonical_contract_path" };
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
try {
|
|
903
|
+
if (
|
|
904
|
+
linuxRealpath(runtime, contract.pluginRoot) !== contract.pluginRoot ||
|
|
905
|
+
linuxRealpath(runtime, contract.workingDirectory) !==
|
|
906
|
+
contract.workingDirectory ||
|
|
907
|
+
linuxRealpath(runtime, spawnOpts.cwd) !== contract.pluginRoot ||
|
|
908
|
+
contract.workingDirectory !== contract.pluginRoot ||
|
|
909
|
+
contract.rootIdentity?.realPath !== contract.pluginRoot ||
|
|
910
|
+
!linuxPathWithin(contract.pluginRoot, contract.entryIdentity.realPath)
|
|
911
|
+
) {
|
|
912
|
+
return { ok: false, reason: "noncanonical_contract_path" };
|
|
913
|
+
}
|
|
914
|
+
} catch {
|
|
915
|
+
return { ok: false, reason: "contract_path_unavailable" };
|
|
916
|
+
}
|
|
917
|
+
if (
|
|
918
|
+
!linuxDirectoryIdentityMatches(runtime, contract.rootIdentity) ||
|
|
919
|
+
!linuxIdentityMatches(runtime, contract.runtimeIdentity, {
|
|
920
|
+
executable: true,
|
|
921
|
+
}) ||
|
|
922
|
+
!linuxIdentityMatches(runtime, contract.entryIdentity)
|
|
923
|
+
) {
|
|
924
|
+
return { ok: false, reason: "execution_identity_changed" };
|
|
925
|
+
}
|
|
926
|
+
return { ok: true };
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function applyMacMcpCapsuleCodeSnapshot(
|
|
930
|
+
command,
|
|
931
|
+
args,
|
|
932
|
+
spawnOpts,
|
|
933
|
+
sandboxOpts,
|
|
934
|
+
runtime,
|
|
935
|
+
base,
|
|
936
|
+
) {
|
|
937
|
+
const contract = sandboxOpts.executionContract;
|
|
938
|
+
const validation = validateMacMcpCapsuleContract(
|
|
939
|
+
command,
|
|
940
|
+
args,
|
|
941
|
+
spawnOpts,
|
|
942
|
+
contract,
|
|
943
|
+
runtime,
|
|
944
|
+
sandboxOpts.sync,
|
|
945
|
+
);
|
|
946
|
+
const unavailable = (reason, runtimeProbe = null) =>
|
|
947
|
+
createSandboxPlan({
|
|
948
|
+
...base,
|
|
949
|
+
backend: null,
|
|
950
|
+
candidateBackend: MACOS_MCP_CAPSULE_BACKEND,
|
|
951
|
+
policyAttested: false,
|
|
952
|
+
runtimeProbe: runtimeProbe
|
|
953
|
+
? {
|
|
954
|
+
kind: "darwin-mcp-capsule-code-snapshot-v1",
|
|
955
|
+
attempted: runtimeProbe.attempted !== false,
|
|
956
|
+
probeRuntime: "node",
|
|
957
|
+
targetRuntime: "node",
|
|
958
|
+
...runtimeProbe,
|
|
959
|
+
}
|
|
960
|
+
: null,
|
|
961
|
+
reason,
|
|
962
|
+
guarantees: [],
|
|
963
|
+
});
|
|
964
|
+
if (!validation.ok) {
|
|
965
|
+
return unavailable("macos_mcp_capsule_execution_contract_invalid", {
|
|
966
|
+
attempted: false,
|
|
967
|
+
runnable: false,
|
|
968
|
+
reason: validation.reason,
|
|
969
|
+
contentSnapshot: false,
|
|
970
|
+
handleAtomic: false,
|
|
971
|
+
entrySnapshotAtomic: false,
|
|
972
|
+
runtimeLaunchAtomic: false,
|
|
973
|
+
sharedLibraryClosure: false,
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// Darwin exposes only pathname-based exec/spawn APIs to an unprivileged
|
|
978
|
+
// process. An inherited descriptor can bind the entry source, but public
|
|
979
|
+
// APIs cannot bind the verified Node runtime descriptor to the image that
|
|
980
|
+
// executes. Private temporary paths and Seatbelt still leave a same-UID
|
|
981
|
+
// pathname-replacement window, so they cannot satisfy CODE_SNAPSHOT.
|
|
982
|
+
return unavailable("macos_atomic_runtime_exec_unavailable", {
|
|
983
|
+
attempted: true,
|
|
984
|
+
runnable: false,
|
|
985
|
+
reason: "public_api_has_no_descriptor_bound_exec",
|
|
986
|
+
contentSnapshot: false,
|
|
987
|
+
handleAtomic: false,
|
|
988
|
+
entrySnapshotAtomic: false,
|
|
989
|
+
runtimeLaunchAtomic: false,
|
|
990
|
+
runtimeLaunchMechanism: "darwin-public-api-pathname-exec-only-v1",
|
|
991
|
+
sharedLibraryClosure: false,
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
|
|
514
995
|
/**
|
|
515
996
|
* Apply macOS sandbox via SBWritableProfile or sandbox-exec wrapper.
|
|
516
997
|
*
|
|
@@ -552,6 +1033,20 @@ export function applyMacSandbox(
|
|
|
552
1033
|
});
|
|
553
1034
|
}
|
|
554
1035
|
|
|
1036
|
+
if (
|
|
1037
|
+
Array.isArray(sandboxOpts.requiredBoundaries) &&
|
|
1038
|
+
sandboxOpts.requiredBoundaries.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT)
|
|
1039
|
+
) {
|
|
1040
|
+
return applyMacMcpCapsuleCodeSnapshot(
|
|
1041
|
+
command,
|
|
1042
|
+
args,
|
|
1043
|
+
spawnOpts,
|
|
1044
|
+
sandboxOpts,
|
|
1045
|
+
runtime,
|
|
1046
|
+
base,
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
555
1050
|
// A deny-by-default Seatbelt profile cannot safely infer the filesystem,
|
|
556
1051
|
// IPC, and inherited-descriptor access required by an arbitrary command.
|
|
557
1052
|
// Keep the boundary truthful for the broker's implicit default profile:
|
|
@@ -619,14 +1114,42 @@ function cleanupWindowsTemporaryPath(
|
|
|
619
1114
|
targetPath,
|
|
620
1115
|
attempts = 100,
|
|
621
1116
|
delayMs = 10,
|
|
1117
|
+
expectedIdentity = null,
|
|
622
1118
|
) {
|
|
623
1119
|
if (!targetPath) return true;
|
|
624
1120
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
625
1121
|
try {
|
|
1122
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
1123
|
+
runtime,
|
|
1124
|
+
targetPath,
|
|
1125
|
+
"file",
|
|
1126
|
+
{ allowMissing: true },
|
|
1127
|
+
);
|
|
1128
|
+
if (!inspected.exists) {
|
|
1129
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1130
|
+
return true;
|
|
1131
|
+
}
|
|
1132
|
+
if (
|
|
1133
|
+
!expectedIdentity ||
|
|
1134
|
+
!sameWindowsFileIdentity(inspected.identity, expectedIdentity)
|
|
1135
|
+
) {
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
626
1138
|
runtime.fs.unlinkSync(targetPath);
|
|
1139
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
627
1140
|
return true;
|
|
628
1141
|
} catch (error) {
|
|
629
|
-
if (error?.
|
|
1142
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
if (error?.code === "ENOENT") {
|
|
1146
|
+
try {
|
|
1147
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1148
|
+
return true;
|
|
1149
|
+
} catch {
|
|
1150
|
+
return false;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
630
1153
|
}
|
|
631
1154
|
if (attempt + 1 < attempts) runtime.sleepSync(delayMs);
|
|
632
1155
|
}
|
|
@@ -637,13 +1160,32 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
637
1160
|
runtime,
|
|
638
1161
|
targetPath,
|
|
639
1162
|
attempts = 100,
|
|
1163
|
+
entry = null,
|
|
1164
|
+
expectedIdentity = null,
|
|
640
1165
|
) {
|
|
641
|
-
if (
|
|
1166
|
+
if (
|
|
1167
|
+
cleanupWindowsTemporaryPath(
|
|
1168
|
+
runtime,
|
|
1169
|
+
targetPath,
|
|
1170
|
+
attempts,
|
|
1171
|
+
10,
|
|
1172
|
+
expectedIdentity,
|
|
1173
|
+
)
|
|
1174
|
+
) {
|
|
1175
|
+
const residualEntries = new Set();
|
|
642
1176
|
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
643
|
-
if (
|
|
1177
|
+
if (
|
|
1178
|
+
residual.kind === "file" &&
|
|
1179
|
+
residual.runtime === runtime &&
|
|
1180
|
+
residual.targetPath === targetPath
|
|
1181
|
+
) {
|
|
644
1182
|
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1183
|
+
if (residual.entry) residualEntries.add(residual.entry);
|
|
645
1184
|
}
|
|
646
1185
|
}
|
|
1186
|
+
for (const residualEntry of residualEntries) {
|
|
1187
|
+
finalizeWindowsTemporaryCleanupEntry(residualEntry);
|
|
1188
|
+
}
|
|
647
1189
|
if (
|
|
648
1190
|
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
649
1191
|
windowsTemporaryCleanupRetryTimer
|
|
@@ -653,13 +1195,25 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
653
1195
|
}
|
|
654
1196
|
return true;
|
|
655
1197
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
)
|
|
662
|
-
|
|
1198
|
+
const existing = [...windowsTemporaryCleanupBacklog].find(
|
|
1199
|
+
(residual) =>
|
|
1200
|
+
residual.kind === "file" &&
|
|
1201
|
+
residual.runtime === runtime &&
|
|
1202
|
+
residual.targetPath === targetPath,
|
|
1203
|
+
);
|
|
1204
|
+
if (existing) {
|
|
1205
|
+
if (entry) existing.entry = entry;
|
|
1206
|
+
if (!existing.expectedIdentity && expectedIdentity) {
|
|
1207
|
+
existing.expectedIdentity = expectedIdentity;
|
|
1208
|
+
}
|
|
1209
|
+
} else {
|
|
1210
|
+
windowsTemporaryCleanupBacklog.add({
|
|
1211
|
+
kind: "file",
|
|
1212
|
+
runtime,
|
|
1213
|
+
targetPath,
|
|
1214
|
+
entry,
|
|
1215
|
+
expectedIdentity,
|
|
1216
|
+
});
|
|
663
1217
|
}
|
|
664
1218
|
registerWindowsAdapterExitCleanup();
|
|
665
1219
|
scheduleWindowsTemporaryCleanupRetry();
|
|
@@ -669,14 +1223,25 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
669
1223
|
function retryWindowsTemporaryCleanupBacklog(attempts = 100) {
|
|
670
1224
|
let cleaned = true;
|
|
671
1225
|
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
1226
|
+
const residualCleaned =
|
|
1227
|
+
residual.kind === "directory"
|
|
1228
|
+
? cleanupWindowsTemporaryDirectory(
|
|
1229
|
+
residual.runtime,
|
|
1230
|
+
residual.targetPath,
|
|
1231
|
+
attempts,
|
|
1232
|
+
10,
|
|
1233
|
+
residual.expectedIdentity,
|
|
1234
|
+
)
|
|
1235
|
+
: cleanupWindowsTemporaryPath(
|
|
1236
|
+
residual.runtime,
|
|
1237
|
+
residual.targetPath,
|
|
1238
|
+
attempts,
|
|
1239
|
+
10,
|
|
1240
|
+
residual.expectedIdentity,
|
|
1241
|
+
);
|
|
1242
|
+
if (residualCleaned) {
|
|
679
1243
|
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1244
|
+
if (residual.entry) finalizeWindowsTemporaryCleanupEntry(residual.entry);
|
|
680
1245
|
} else {
|
|
681
1246
|
cleaned = false;
|
|
682
1247
|
}
|
|
@@ -691,6 +1256,18 @@ function retryWindowsTemporaryCleanupBacklog(attempts = 100) {
|
|
|
691
1256
|
return cleaned;
|
|
692
1257
|
}
|
|
693
1258
|
|
|
1259
|
+
function finalizeWindowsTemporaryCleanupEntry(entry) {
|
|
1260
|
+
if (
|
|
1261
|
+
[...windowsTemporaryCleanupBacklog].some(
|
|
1262
|
+
(residual) => residual.entry === entry,
|
|
1263
|
+
)
|
|
1264
|
+
) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
entry.cleaned = true;
|
|
1268
|
+
windowsAdapterCacheEntries.delete(entry);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
694
1271
|
function scheduleWindowsTemporaryCleanupRetry() {
|
|
695
1272
|
if (
|
|
696
1273
|
windowsTemporaryCleanupRetryTimer ||
|
|
@@ -713,17 +1290,109 @@ function cleanupWindowsTemporaryDirectory(
|
|
|
713
1290
|
targetPath,
|
|
714
1291
|
attempts = 100,
|
|
715
1292
|
delayMs = 10,
|
|
1293
|
+
expectedIdentity = null,
|
|
716
1294
|
) {
|
|
717
1295
|
if (!targetPath) return true;
|
|
718
1296
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
719
1297
|
try {
|
|
1298
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
1299
|
+
runtime,
|
|
1300
|
+
targetPath,
|
|
1301
|
+
"directory",
|
|
1302
|
+
{ allowMissing: true },
|
|
1303
|
+
);
|
|
1304
|
+
if (!inspected.exists) {
|
|
1305
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1306
|
+
return true;
|
|
1307
|
+
}
|
|
1308
|
+
if (
|
|
1309
|
+
!expectedIdentity ||
|
|
1310
|
+
!sameWindowsDirectoryIdentity(inspected.identity, expectedIdentity)
|
|
1311
|
+
) {
|
|
1312
|
+
return false;
|
|
1313
|
+
}
|
|
720
1314
|
runtime.fs.rmdirSync(targetPath);
|
|
1315
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
721
1316
|
return true;
|
|
722
1317
|
} catch (error) {
|
|
723
|
-
if (error?.
|
|
1318
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
1319
|
+
return false;
|
|
1320
|
+
}
|
|
1321
|
+
if (error?.code === "ENOENT") {
|
|
1322
|
+
try {
|
|
1323
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1324
|
+
return true;
|
|
1325
|
+
} catch {
|
|
1326
|
+
return false;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (attempt + 1 < attempts) runtime.sleepSync(delayMs);
|
|
1331
|
+
}
|
|
1332
|
+
return false;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function cleanupOrTrackWindowsTemporaryDirectory(
|
|
1336
|
+
runtime,
|
|
1337
|
+
targetPath,
|
|
1338
|
+
attempts = 100,
|
|
1339
|
+
entry = null,
|
|
1340
|
+
expectedIdentity = null,
|
|
1341
|
+
) {
|
|
1342
|
+
if (
|
|
1343
|
+
cleanupWindowsTemporaryDirectory(
|
|
1344
|
+
runtime,
|
|
1345
|
+
targetPath,
|
|
1346
|
+
attempts,
|
|
1347
|
+
10,
|
|
1348
|
+
expectedIdentity,
|
|
1349
|
+
)
|
|
1350
|
+
) {
|
|
1351
|
+
const residualEntries = new Set();
|
|
1352
|
+
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
1353
|
+
if (
|
|
1354
|
+
residual.kind === "directory" &&
|
|
1355
|
+
residual.runtime === runtime &&
|
|
1356
|
+
residual.targetPath === targetPath
|
|
1357
|
+
) {
|
|
1358
|
+
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1359
|
+
if (residual.entry) residualEntries.add(residual.entry);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
for (const residualEntry of residualEntries) {
|
|
1363
|
+
finalizeWindowsTemporaryCleanupEntry(residualEntry);
|
|
1364
|
+
}
|
|
1365
|
+
if (
|
|
1366
|
+
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
1367
|
+
windowsTemporaryCleanupRetryTimer
|
|
1368
|
+
) {
|
|
1369
|
+
clearTimeout(windowsTemporaryCleanupRetryTimer);
|
|
1370
|
+
windowsTemporaryCleanupRetryTimer = null;
|
|
724
1371
|
}
|
|
725
|
-
|
|
1372
|
+
return true;
|
|
1373
|
+
}
|
|
1374
|
+
const existing = [...windowsTemporaryCleanupBacklog].find(
|
|
1375
|
+
(residual) =>
|
|
1376
|
+
residual.kind === "directory" &&
|
|
1377
|
+
residual.runtime === runtime &&
|
|
1378
|
+
residual.targetPath === targetPath,
|
|
1379
|
+
);
|
|
1380
|
+
if (existing) {
|
|
1381
|
+
if (entry) existing.entry = entry;
|
|
1382
|
+
if (!existing.expectedIdentity && expectedIdentity) {
|
|
1383
|
+
existing.expectedIdentity = expectedIdentity;
|
|
1384
|
+
}
|
|
1385
|
+
} else {
|
|
1386
|
+
windowsTemporaryCleanupBacklog.add({
|
|
1387
|
+
kind: "directory",
|
|
1388
|
+
runtime,
|
|
1389
|
+
targetPath,
|
|
1390
|
+
entry,
|
|
1391
|
+
expectedIdentity,
|
|
1392
|
+
});
|
|
726
1393
|
}
|
|
1394
|
+
registerWindowsAdapterExitCleanup();
|
|
1395
|
+
scheduleWindowsTemporaryCleanupRetry();
|
|
727
1396
|
return false;
|
|
728
1397
|
}
|
|
729
1398
|
|
|
@@ -731,6 +1400,218 @@ function sha256(value) {
|
|
|
731
1400
|
return crypto.createHash("sha256").update(value).digest("hex");
|
|
732
1401
|
}
|
|
733
1402
|
|
|
1403
|
+
function sameStringArray(left, right) {
|
|
1404
|
+
return (
|
|
1405
|
+
Array.isArray(left) &&
|
|
1406
|
+
Array.isArray(right) &&
|
|
1407
|
+
left.length === right.length &&
|
|
1408
|
+
left.every((value, index) => value === right[index])
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
function captureWindowsStdio(stdio) {
|
|
1413
|
+
if (Array.isArray(stdio)) return Object.freeze([...stdio]);
|
|
1414
|
+
return stdio ?? null;
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
function sameWindowsStdio(left, right) {
|
|
1418
|
+
const normalizedRight = right ?? null;
|
|
1419
|
+
if (Array.isArray(left) || Array.isArray(normalizedRight)) {
|
|
1420
|
+
return (
|
|
1421
|
+
Array.isArray(left) &&
|
|
1422
|
+
Array.isArray(normalizedRight) &&
|
|
1423
|
+
left.length === normalizedRight.length &&
|
|
1424
|
+
left.every((entry, index) => Object.is(entry, normalizedRight[index]))
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
return Object.is(left, normalizedRight);
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
function windowsStdioDigestContract(stdio) {
|
|
1431
|
+
const normalizeEntry = (entry) => {
|
|
1432
|
+
if (entry === null || entry === undefined) return ["null"];
|
|
1433
|
+
if (typeof entry === "string") return ["string", entry];
|
|
1434
|
+
if (Number.isInteger(entry)) return ["fd", entry];
|
|
1435
|
+
return [typeof entry];
|
|
1436
|
+
};
|
|
1437
|
+
return Array.isArray(stdio)
|
|
1438
|
+
? ["array", ...stdio.map(normalizeEntry)]
|
|
1439
|
+
: ["scalar", ...normalizeEntry(stdio)];
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
function windowsEnvironmentDigest(env) {
|
|
1443
|
+
const entries = Object.entries(env || {})
|
|
1444
|
+
.map(([key, value]) => [key, String(value)])
|
|
1445
|
+
.sort(([leftKey, leftValue], [rightKey, rightValue]) => {
|
|
1446
|
+
if (leftKey !== rightKey) return leftKey < rightKey ? -1 : 1;
|
|
1447
|
+
if (leftValue === rightValue) return 0;
|
|
1448
|
+
return leftValue < rightValue ? -1 : 1;
|
|
1449
|
+
});
|
|
1450
|
+
return sha256(JSON.stringify(entries));
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
function windowsMcpBindingIdentity(identity) {
|
|
1454
|
+
if (!identity) return null;
|
|
1455
|
+
return {
|
|
1456
|
+
contractVersion: identity.contractVersion ?? null,
|
|
1457
|
+
realPath: identity.realPath ?? null,
|
|
1458
|
+
sha256: identity.sha256 ?? null,
|
|
1459
|
+
bytes: identity.bytes ?? null,
|
|
1460
|
+
fileId: identity.fileId
|
|
1461
|
+
? {
|
|
1462
|
+
dev: identity.fileId.dev ?? null,
|
|
1463
|
+
ino: identity.fileId.ino ?? null,
|
|
1464
|
+
}
|
|
1465
|
+
: null,
|
|
1466
|
+
mtimeMs: identity.mtimeMs ?? null,
|
|
1467
|
+
attestation: identity.attestation ?? null,
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
function windowsMcpBindingContract(contract) {
|
|
1472
|
+
return {
|
|
1473
|
+
contractVersion: contract?.contractVersion ?? null,
|
|
1474
|
+
kind: contract?.kind ?? null,
|
|
1475
|
+
pluginRoot: contract?.pluginRoot ?? null,
|
|
1476
|
+
workingDirectory: contract?.workingDirectory ?? null,
|
|
1477
|
+
runtimePath: contract?.runtimePath ?? null,
|
|
1478
|
+
rootIdentity: contract?.rootIdentity
|
|
1479
|
+
? {
|
|
1480
|
+
realPath: contract.rootIdentity.realPath ?? null,
|
|
1481
|
+
fileId: contract.rootIdentity.fileId
|
|
1482
|
+
? {
|
|
1483
|
+
dev: contract.rootIdentity.fileId.dev ?? null,
|
|
1484
|
+
ino: contract.rootIdentity.fileId.ino ?? null,
|
|
1485
|
+
}
|
|
1486
|
+
: null,
|
|
1487
|
+
attestation: contract.rootIdentity.attestation ?? null,
|
|
1488
|
+
}
|
|
1489
|
+
: null,
|
|
1490
|
+
entryIdentity: windowsMcpBindingIdentity(contract?.entryIdentity),
|
|
1491
|
+
runtimeIdentity: windowsMcpBindingIdentity(contract?.runtimeIdentity),
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
function createWindowsMcpCodeSnapshotPlanBinding({
|
|
1496
|
+
executionContract,
|
|
1497
|
+
command,
|
|
1498
|
+
args,
|
|
1499
|
+
spawnOpts,
|
|
1500
|
+
profile,
|
|
1501
|
+
requiredBoundaries,
|
|
1502
|
+
sync,
|
|
1503
|
+
backend,
|
|
1504
|
+
policyAttested,
|
|
1505
|
+
policyDigest,
|
|
1506
|
+
adapterSource,
|
|
1507
|
+
helperInvocation,
|
|
1508
|
+
helperOptions,
|
|
1509
|
+
postSpawn,
|
|
1510
|
+
postSpawnWindows,
|
|
1511
|
+
}) {
|
|
1512
|
+
const helperEnvDigest = windowsEnvironmentDigest(helperOptions.env);
|
|
1513
|
+
const helperStdio = captureWindowsStdio(helperOptions.stdio);
|
|
1514
|
+
const originalArgs = Object.freeze([...(args || [])]);
|
|
1515
|
+
const helperArgs = Object.freeze([...(helperInvocation.args || [])]);
|
|
1516
|
+
const normalizedPostSpawn = Object.freeze({
|
|
1517
|
+
required: postSpawn?.required === true,
|
|
1518
|
+
mode: postSpawn?.mode || "none",
|
|
1519
|
+
});
|
|
1520
|
+
const digestPayload = {
|
|
1521
|
+
version: 1,
|
|
1522
|
+
kind: "windows-mcp-code-snapshot-plan-binding-v1",
|
|
1523
|
+
executionContract: windowsMcpBindingContract(executionContract),
|
|
1524
|
+
originalLaunch: {
|
|
1525
|
+
command,
|
|
1526
|
+
args: originalArgs,
|
|
1527
|
+
cwd: spawnOpts?.cwd ?? null,
|
|
1528
|
+
shell: spawnOpts?.shell ?? null,
|
|
1529
|
+
detached: spawnOpts?.detached ?? null,
|
|
1530
|
+
profile,
|
|
1531
|
+
requiredBoundaries: [...requiredBoundaries],
|
|
1532
|
+
sync,
|
|
1533
|
+
},
|
|
1534
|
+
policy: { backend, policyAttested, policyDigest },
|
|
1535
|
+
adapter: {
|
|
1536
|
+
loaderMode: adapterSource.loaderMode,
|
|
1537
|
+
sourceDigest: adapterSource.sourceDigest,
|
|
1538
|
+
sourceContractDigest: adapterSource.sourceContractDigest,
|
|
1539
|
+
},
|
|
1540
|
+
helperInvocation: {
|
|
1541
|
+
command: helperInvocation.command,
|
|
1542
|
+
args: helperArgs,
|
|
1543
|
+
cwd: helperOptions.cwd ?? null,
|
|
1544
|
+
shell: helperOptions.shell ?? null,
|
|
1545
|
+
detached: helperOptions.detached ?? null,
|
|
1546
|
+
envDigest: helperEnvDigest,
|
|
1547
|
+
stdio: windowsStdioDigestContract(helperStdio),
|
|
1548
|
+
},
|
|
1549
|
+
postSpawn: normalizedPostSpawn,
|
|
1550
|
+
};
|
|
1551
|
+
return Object.freeze({
|
|
1552
|
+
executionContract,
|
|
1553
|
+
originalCommand: command,
|
|
1554
|
+
originalArgs,
|
|
1555
|
+
originalCwd: spawnOpts?.cwd ?? null,
|
|
1556
|
+
originalShell: spawnOpts?.shell ?? null,
|
|
1557
|
+
originalDetached: spawnOpts?.detached ?? null,
|
|
1558
|
+
profile,
|
|
1559
|
+
requiredBoundaries: Object.freeze([...requiredBoundaries]),
|
|
1560
|
+
sync,
|
|
1561
|
+
backend,
|
|
1562
|
+
policyAttested,
|
|
1563
|
+
policyDigest,
|
|
1564
|
+
helperCommand: helperInvocation.command,
|
|
1565
|
+
helperArgs,
|
|
1566
|
+
helperCwd: helperOptions.cwd ?? null,
|
|
1567
|
+
helperShell: helperOptions.shell ?? null,
|
|
1568
|
+
helperDetached: helperOptions.detached ?? null,
|
|
1569
|
+
helperEnvDigest,
|
|
1570
|
+
helperStdio,
|
|
1571
|
+
postSpawn: normalizedPostSpawn,
|
|
1572
|
+
postSpawnWindows: postSpawnWindows || null,
|
|
1573
|
+
planBindingDigest: sha256(JSON.stringify(digestPayload)),
|
|
1574
|
+
});
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/**
|
|
1578
|
+
* Consume the one-launch capability attached to a real built-in Windows MCP
|
|
1579
|
+
* code-snapshot plan. The issuer stays module-private so an injected adapter
|
|
1580
|
+
* cannot make a structurally similar helper payload authoritative.
|
|
1581
|
+
*/
|
|
1582
|
+
export function consumeWindowsMcpCodeSnapshotPlanBinding(plan, expected = {}) {
|
|
1583
|
+
const issued = issuedWindowsMcpCodeSnapshotPlans.get(plan);
|
|
1584
|
+
if (!issued) return false;
|
|
1585
|
+
issuedWindowsMcpCodeSnapshotPlans.delete(plan);
|
|
1586
|
+
return (
|
|
1587
|
+
issued.executionContract === expected.executionContract &&
|
|
1588
|
+
issued.originalCommand === expected.command &&
|
|
1589
|
+
sameStringArray(issued.originalArgs, expected.args) &&
|
|
1590
|
+
issued.originalCwd === (expected.cwd ?? null) &&
|
|
1591
|
+
issued.originalShell === (expected.shell ?? null) &&
|
|
1592
|
+
issued.originalDetached === (expected.detached ?? null) &&
|
|
1593
|
+
issued.profile === expected.profile &&
|
|
1594
|
+
sameStringArray(issued.requiredBoundaries, expected.requiredBoundaries) &&
|
|
1595
|
+
issued.sync === (expected.sync === true) &&
|
|
1596
|
+
plan.backend === issued.backend &&
|
|
1597
|
+
plan.policyAttested === issued.policyAttested &&
|
|
1598
|
+
plan.policyDigest === issued.policyDigest &&
|
|
1599
|
+
plan.command === issued.helperCommand &&
|
|
1600
|
+
sameStringArray(plan.args, issued.helperArgs) &&
|
|
1601
|
+
plan.options?.cwd === issued.helperCwd &&
|
|
1602
|
+
plan.options?.shell === issued.helperShell &&
|
|
1603
|
+
plan.options?.detached === issued.helperDetached &&
|
|
1604
|
+
windowsEnvironmentDigest(plan.options?.env) === issued.helperEnvDigest &&
|
|
1605
|
+
sameWindowsStdio(issued.helperStdio, plan.options?.stdio) &&
|
|
1606
|
+
plan.postSpawn?.required === issued.postSpawn.required &&
|
|
1607
|
+
plan.postSpawn?.mode === issued.postSpawn.mode &&
|
|
1608
|
+
(plan.postSpawnWindows || null) === issued.postSpawnWindows &&
|
|
1609
|
+
plan.runtimeProbe?.planBindingMechanism ===
|
|
1610
|
+
"windows-mcp-code-snapshot-plan-binding-v1" &&
|
|
1611
|
+
plan.runtimeProbe?.planBindingDigest === issued.planBindingDigest
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
734
1615
|
function windowsFileIdentity(stat) {
|
|
735
1616
|
const timestamp = (nanosecondsKey, millisecondsKey) => {
|
|
736
1617
|
if (stat[nanosecondsKey] !== undefined) {
|
|
@@ -762,15 +1643,11 @@ function sameWindowsFileIdentity(left, right) {
|
|
|
762
1643
|
}
|
|
763
1644
|
|
|
764
1645
|
function inspectWindowsAdapterAssembly(runtime, assemblyPath) {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
) {
|
|
771
|
-
throw new Error("Windows in-memory adapter is not a regular file");
|
|
772
|
-
}
|
|
773
|
-
}
|
|
1646
|
+
const boundBefore = inspectWindowsAdapterBoundPath(
|
|
1647
|
+
runtime,
|
|
1648
|
+
assemblyPath,
|
|
1649
|
+
"file",
|
|
1650
|
+
);
|
|
774
1651
|
const before = runtime.fs.statSync(assemblyPath, { bigint: true });
|
|
775
1652
|
if (typeof before.isFile === "function" && !before.isFile()) {
|
|
776
1653
|
throw new Error("Windows in-memory adapter is not a regular file");
|
|
@@ -783,6 +1660,15 @@ function inspectWindowsAdapterAssembly(runtime, assemblyPath) {
|
|
|
783
1660
|
if (!sameWindowsFileIdentity(beforeIdentity, afterIdentity)) {
|
|
784
1661
|
throw new Error("Windows in-memory adapter changed during attestation");
|
|
785
1662
|
}
|
|
1663
|
+
const boundAfter = assertWindowsAdapterBoundPathIdentity(
|
|
1664
|
+
runtime,
|
|
1665
|
+
assemblyPath,
|
|
1666
|
+
"file",
|
|
1667
|
+
boundBefore.identity,
|
|
1668
|
+
);
|
|
1669
|
+
if (!sameWindowsFileIdentity(afterIdentity, boundAfter.identity)) {
|
|
1670
|
+
throw new Error("Windows in-memory adapter changed during attestation");
|
|
1671
|
+
}
|
|
786
1672
|
return { assemblyDigest, fileIdentity: afterIdentity };
|
|
787
1673
|
}
|
|
788
1674
|
|
|
@@ -792,6 +1678,11 @@ function verifyWindowsAdapterEntry(entry, runtime, source) {
|
|
|
792
1678
|
entry.cleaned ||
|
|
793
1679
|
entry.runtimeFs !== runtime.fs ||
|
|
794
1680
|
entry.tempDirectory !== source.tempDirectory ||
|
|
1681
|
+
entry.idleTtlMs !== source.idleTtlMs ||
|
|
1682
|
+
!sameWindowsAdapterTempRootBinding(
|
|
1683
|
+
entry.tempRootBinding,
|
|
1684
|
+
source.tempRootBinding,
|
|
1685
|
+
) ||
|
|
795
1686
|
entry.sourceDigest !== source.sourceDigest ||
|
|
796
1687
|
entry.sourceContractDigest !== source.sourceContractDigest ||
|
|
797
1688
|
entry.loaderMode !== source.loaderMode
|
|
@@ -799,6 +1690,15 @@ function verifyWindowsAdapterEntry(entry, runtime, source) {
|
|
|
799
1690
|
return false;
|
|
800
1691
|
}
|
|
801
1692
|
try {
|
|
1693
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1694
|
+
if (entry.adapterDirectory) {
|
|
1695
|
+
assertWindowsAdapterBoundPathIdentity(
|
|
1696
|
+
runtime,
|
|
1697
|
+
entry.adapterDirectory,
|
|
1698
|
+
"directory",
|
|
1699
|
+
entry.adapterDirectoryIdentity,
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
802
1702
|
const attestation = inspectWindowsAdapterAssembly(
|
|
803
1703
|
runtime,
|
|
804
1704
|
entry.assemblyPath,
|
|
@@ -822,14 +1722,18 @@ function cleanupWindowsAdapterEntry(entry, attempts = 100) {
|
|
|
822
1722
|
entry.runtime,
|
|
823
1723
|
entry.assemblyPath,
|
|
824
1724
|
attempts,
|
|
1725
|
+
entry,
|
|
1726
|
+
entry.fileIdentity,
|
|
825
1727
|
);
|
|
826
|
-
const directoryDeleted =
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1728
|
+
const directoryDeleted = entry.adapterDirectory
|
|
1729
|
+
? cleanupOrTrackWindowsTemporaryDirectory(
|
|
1730
|
+
entry.runtime,
|
|
1731
|
+
entry.adapterDirectory,
|
|
1732
|
+
attempts,
|
|
1733
|
+
entry,
|
|
1734
|
+
entry.adapterDirectoryIdentity,
|
|
1735
|
+
)
|
|
1736
|
+
: true;
|
|
833
1737
|
if (deleted && directoryDeleted) {
|
|
834
1738
|
entry.cleaned = true;
|
|
835
1739
|
windowsAdapterCacheEntries.delete(entry);
|
|
@@ -872,15 +1776,35 @@ function registerWindowsAdapterExitCleanup() {
|
|
|
872
1776
|
* this hook at a known quiescent point to prove cleanup.
|
|
873
1777
|
*/
|
|
874
1778
|
export function resetWindowsSandboxAdapterCache() {
|
|
875
|
-
|
|
1779
|
+
const appContainerCleaned = retryWindowsAppContainerCleanupBacklog();
|
|
876
1780
|
windowsAdapterCache = null;
|
|
877
1781
|
for (const entry of [...windowsAdapterCacheEntries]) {
|
|
878
1782
|
entry.retired = true;
|
|
879
1783
|
entry.refCount = 0;
|
|
880
|
-
|
|
1784
|
+
cleanupWindowsAdapterEntry(entry);
|
|
881
1785
|
}
|
|
882
|
-
|
|
883
|
-
return
|
|
1786
|
+
retryWindowsTemporaryCleanupBacklog();
|
|
1787
|
+
return (
|
|
1788
|
+
appContainerCleaned &&
|
|
1789
|
+
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
1790
|
+
windowsAdapterCacheEntries.size === 0
|
|
1791
|
+
);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
function normalizeWindowsAdapterIdleTtlMs(runtime) {
|
|
1795
|
+
const explicitTtl = runtime.windowsAdapterIdleTtlMs;
|
|
1796
|
+
const environmentTtl =
|
|
1797
|
+
runtime.env?.[WINDOWS_ADAPTER_IDLE_TTL_ENV] ??
|
|
1798
|
+
process.env[WINDOWS_ADAPTER_IDLE_TTL_ENV];
|
|
1799
|
+
const configuredTtl =
|
|
1800
|
+
explicitTtl === undefined ? environmentTtl : explicitTtl;
|
|
1801
|
+
const numericTtl =
|
|
1802
|
+
configuredTtl === "" || configuredTtl === null
|
|
1803
|
+
? Number.NaN
|
|
1804
|
+
: Number(configuredTtl);
|
|
1805
|
+
return Number.isFinite(numericTtl) && numericTtl >= 0
|
|
1806
|
+
? numericTtl
|
|
1807
|
+
: WINDOWS_ADAPTER_IDLE_TTL_MS;
|
|
884
1808
|
}
|
|
885
1809
|
|
|
886
1810
|
function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
@@ -892,11 +1816,11 @@ function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
|
892
1816
|
) {
|
|
893
1817
|
return;
|
|
894
1818
|
}
|
|
895
|
-
const
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1819
|
+
const idleTtlMs = entry.idleTtlMs;
|
|
1820
|
+
if (idleTtlMs === 0) {
|
|
1821
|
+
retireWindowsAdapterEntry(entry);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
900
1824
|
entry.idleTimer = setTimeout(() => {
|
|
901
1825
|
entry.idleTimer = null;
|
|
902
1826
|
if (
|
|
@@ -912,6 +1836,7 @@ function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
|
912
1836
|
}
|
|
913
1837
|
|
|
914
1838
|
function loadWindowsAdapterSource(runtime, loaderMode) {
|
|
1839
|
+
const tempRootBinding = assertWindowsAdapterTempRoot(runtime);
|
|
915
1840
|
const bundled = runtime.windowsAdapterContent === undefined;
|
|
916
1841
|
const content = bundled
|
|
917
1842
|
? loaderMode === "managed-executable"
|
|
@@ -926,7 +1851,10 @@ function loadWindowsAdapterSource(runtime, loaderMode) {
|
|
|
926
1851
|
: WINDOWS_SANDBOX_HELPER_ASSEMBLY_DIGEST
|
|
927
1852
|
: sha256(content),
|
|
928
1853
|
sourceContractDigest: bundled ? WINDOWS_SANDBOX_HELPER_SOURCE_DIGEST : null,
|
|
929
|
-
tempDirectory:
|
|
1854
|
+
tempDirectory: tempRootBinding.rootPath,
|
|
1855
|
+
tempRootBinding,
|
|
1856
|
+
tempRootAttestation: tempRootBinding.attestation,
|
|
1857
|
+
idleTtlMs: runtime.windowsAdapterIdleTtlMs,
|
|
930
1858
|
loaderMode,
|
|
931
1859
|
};
|
|
932
1860
|
}
|
|
@@ -948,54 +1876,121 @@ function materializeWindowsAdapter(runtime, source) {
|
|
|
948
1876
|
const assemblyPath = runtime.joinPath(
|
|
949
1877
|
adapterDirectory || source.tempDirectory,
|
|
950
1878
|
source.loaderMode === "managed-executable"
|
|
951
|
-
?
|
|
1879
|
+
? WINDOWS_ADAPTER_HELPER_NAME
|
|
952
1880
|
: `${adapterBaseName}.dll`,
|
|
953
1881
|
);
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
runtime.fs.existsSync(assemblyPath)
|
|
957
|
-
) {
|
|
958
|
-
return { reason: "windows_native_adapter_random_path_collision" };
|
|
959
|
-
}
|
|
1882
|
+
let adapterDirectoryIdentity = null;
|
|
1883
|
+
let assemblyFileIdentity = null;
|
|
960
1884
|
try {
|
|
1885
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
961
1886
|
if (adapterDirectory) {
|
|
1887
|
+
if (
|
|
1888
|
+
inspectWindowsAdapterBoundPath(runtime, adapterDirectory, "directory", {
|
|
1889
|
+
allowMissing: true,
|
|
1890
|
+
}).exists
|
|
1891
|
+
) {
|
|
1892
|
+
return { reason: "windows_native_adapter_random_path_collision" };
|
|
1893
|
+
}
|
|
962
1894
|
runtime.fs.mkdirSync(adapterDirectory, {
|
|
963
1895
|
mode: 0o700,
|
|
964
1896
|
recursive: false,
|
|
965
1897
|
});
|
|
1898
|
+
adapterDirectoryIdentity = inspectWindowsAdapterBoundPath(
|
|
1899
|
+
runtime,
|
|
1900
|
+
adapterDirectory,
|
|
1901
|
+
"directory",
|
|
1902
|
+
).identity;
|
|
1903
|
+
}
|
|
1904
|
+
if (
|
|
1905
|
+
inspectWindowsAdapterBoundPath(runtime, assemblyPath, "file", {
|
|
1906
|
+
allowMissing: true,
|
|
1907
|
+
}).exists
|
|
1908
|
+
) {
|
|
1909
|
+
const collision = new Error(
|
|
1910
|
+
"Windows native adapter random path collision",
|
|
1911
|
+
);
|
|
1912
|
+
collision.code = "EEXIST";
|
|
1913
|
+
throw collision;
|
|
966
1914
|
}
|
|
967
1915
|
runtime.fs.writeFileSync(assemblyPath, source.content, {
|
|
968
1916
|
mode: adapterDirectory ? 0o500 : 0o600,
|
|
969
1917
|
flag: "wx",
|
|
970
1918
|
});
|
|
1919
|
+
assemblyFileIdentity = inspectWindowsAdapterAssembly(
|
|
1920
|
+
runtime,
|
|
1921
|
+
assemblyPath,
|
|
1922
|
+
).fileIdentity;
|
|
971
1923
|
} catch (error) {
|
|
1924
|
+
if (error?.code === "EEXIST") {
|
|
1925
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1926
|
+
runtime,
|
|
1927
|
+
adapterDirectory,
|
|
1928
|
+
100,
|
|
1929
|
+
null,
|
|
1930
|
+
adapterDirectoryIdentity,
|
|
1931
|
+
);
|
|
1932
|
+
return {
|
|
1933
|
+
reason: directoryDeleted
|
|
1934
|
+
? "windows_native_adapter_random_path_collision"
|
|
1935
|
+
: "windows_native_adapter_compile_cleanup_unverified",
|
|
1936
|
+
};
|
|
1937
|
+
}
|
|
1938
|
+
if (!assemblyFileIdentity) {
|
|
1939
|
+
try {
|
|
1940
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
1941
|
+
runtime,
|
|
1942
|
+
assemblyPath,
|
|
1943
|
+
"file",
|
|
1944
|
+
{ allowMissing: true },
|
|
1945
|
+
);
|
|
1946
|
+
assemblyFileIdentity = inspected.exists ? inspected.identity : null;
|
|
1947
|
+
} catch {
|
|
1948
|
+
assemblyFileIdentity = null;
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
972
1951
|
const assemblyDeleted = cleanupOrTrackWindowsTemporaryPath(
|
|
973
1952
|
runtime,
|
|
974
1953
|
assemblyPath,
|
|
1954
|
+
100,
|
|
1955
|
+
null,
|
|
1956
|
+
assemblyFileIdentity,
|
|
975
1957
|
);
|
|
976
|
-
const directoryDeleted =
|
|
977
|
-
|
|
978
|
-
|
|
1958
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1959
|
+
runtime,
|
|
1960
|
+
adapterDirectory,
|
|
1961
|
+
100,
|
|
1962
|
+
null,
|
|
1963
|
+
adapterDirectoryIdentity,
|
|
1964
|
+
);
|
|
1965
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
1966
|
+
return { reason: WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON };
|
|
1967
|
+
}
|
|
979
1968
|
if (!assemblyDeleted || !directoryDeleted) {
|
|
980
1969
|
return { reason: "windows_native_adapter_compile_cleanup_unverified" };
|
|
981
1970
|
}
|
|
982
|
-
if (error?.code === "EEXIST") {
|
|
983
|
-
return { reason: "windows_native_adapter_random_path_collision" };
|
|
984
|
-
}
|
|
985
1971
|
return { reason: "windows_native_adapter_materialize_failed" };
|
|
986
1972
|
}
|
|
987
1973
|
return {
|
|
988
1974
|
adapterDirectory,
|
|
1975
|
+
adapterDirectoryIdentity,
|
|
989
1976
|
assemblyPath,
|
|
1977
|
+
assemblyFileIdentity,
|
|
990
1978
|
cleanupAssembly: () => {
|
|
991
1979
|
const assemblyDeleted = cleanupOrTrackWindowsTemporaryPath(
|
|
992
1980
|
runtime,
|
|
993
1981
|
assemblyPath,
|
|
1982
|
+
100,
|
|
1983
|
+
null,
|
|
1984
|
+
assemblyFileIdentity,
|
|
994
1985
|
);
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1986
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1987
|
+
runtime,
|
|
1988
|
+
adapterDirectory,
|
|
1989
|
+
100,
|
|
1990
|
+
null,
|
|
1991
|
+
adapterDirectoryIdentity,
|
|
998
1992
|
);
|
|
1993
|
+
return assemblyDeleted && directoryDeleted;
|
|
999
1994
|
},
|
|
1000
1995
|
};
|
|
1001
1996
|
}
|
|
@@ -1007,9 +2002,7 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1007
2002
|
.randomBytes(24)
|
|
1008
2003
|
.toString("hex")}.json`,
|
|
1009
2004
|
);
|
|
1010
|
-
|
|
1011
|
-
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
1012
|
-
}
|
|
2005
|
+
let fileIdentity = null;
|
|
1013
2006
|
let content;
|
|
1014
2007
|
try {
|
|
1015
2008
|
content = Buffer.from(
|
|
@@ -1029,16 +2022,57 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1029
2022
|
return { reason: "windows_adapter_invocation_too_large" };
|
|
1030
2023
|
}
|
|
1031
2024
|
try {
|
|
2025
|
+
if (
|
|
2026
|
+
inspectWindowsAdapterBoundPath(runtime, payloadPath, "file", {
|
|
2027
|
+
allowMissing: true,
|
|
2028
|
+
}).exists
|
|
2029
|
+
) {
|
|
2030
|
+
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
2031
|
+
}
|
|
1032
2032
|
runtime.fs.writeFileSync(payloadPath, content, {
|
|
1033
2033
|
mode: 0o600,
|
|
1034
2034
|
flag: "wx",
|
|
1035
2035
|
});
|
|
2036
|
+
const inspected = inspectWindowsAdapterAssembly(runtime, payloadPath);
|
|
2037
|
+
if (inspected.assemblyDigest !== sha256(content)) {
|
|
2038
|
+
throw new Error("Windows adapter invocation changed after creation");
|
|
2039
|
+
}
|
|
2040
|
+
fileIdentity = inspected.fileIdentity;
|
|
1036
2041
|
} catch (error) {
|
|
2042
|
+
if (!fileIdentity) {
|
|
2043
|
+
try {
|
|
2044
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
2045
|
+
runtime,
|
|
2046
|
+
payloadPath,
|
|
2047
|
+
"file",
|
|
2048
|
+
{ allowMissing: true },
|
|
2049
|
+
);
|
|
2050
|
+
fileIdentity = inspected.exists ? inspected.identity : null;
|
|
2051
|
+
} catch {
|
|
2052
|
+
fileIdentity = null;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
2056
|
+
cleanupOrTrackWindowsTemporaryPath(
|
|
2057
|
+
runtime,
|
|
2058
|
+
payloadPath,
|
|
2059
|
+
100,
|
|
2060
|
+
null,
|
|
2061
|
+
fileIdentity,
|
|
2062
|
+
);
|
|
2063
|
+
return { reason: WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON };
|
|
2064
|
+
}
|
|
1037
2065
|
if (error?.code === "EEXIST") {
|
|
1038
2066
|
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
1039
2067
|
}
|
|
1040
2068
|
return {
|
|
1041
|
-
reason: cleanupOrTrackWindowsTemporaryPath(
|
|
2069
|
+
reason: cleanupOrTrackWindowsTemporaryPath(
|
|
2070
|
+
runtime,
|
|
2071
|
+
payloadPath,
|
|
2072
|
+
100,
|
|
2073
|
+
null,
|
|
2074
|
+
fileIdentity,
|
|
2075
|
+
)
|
|
1042
2076
|
? "windows_adapter_invocation_materialize_failed"
|
|
1043
2077
|
: "windows_adapter_invocation_cleanup_unverified",
|
|
1044
2078
|
};
|
|
@@ -1046,7 +2080,24 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1046
2080
|
return {
|
|
1047
2081
|
payloadPath,
|
|
1048
2082
|
payloadDigest: sha256(content),
|
|
1049
|
-
|
|
2083
|
+
fileIdentity,
|
|
2084
|
+
verify: () => {
|
|
2085
|
+
const inspected = assertWindowsAdapterBoundPathIdentity(
|
|
2086
|
+
runtime,
|
|
2087
|
+
payloadPath,
|
|
2088
|
+
"file",
|
|
2089
|
+
fileIdentity,
|
|
2090
|
+
);
|
|
2091
|
+
return inspected.exists;
|
|
2092
|
+
},
|
|
2093
|
+
cleanup: () =>
|
|
2094
|
+
cleanupOrTrackWindowsTemporaryPath(
|
|
2095
|
+
runtime,
|
|
2096
|
+
payloadPath,
|
|
2097
|
+
100,
|
|
2098
|
+
null,
|
|
2099
|
+
fileIdentity,
|
|
2100
|
+
),
|
|
1050
2101
|
};
|
|
1051
2102
|
}
|
|
1052
2103
|
|
|
@@ -1102,6 +2153,7 @@ function buildWindowsAdapterInvocation(entry, invocation) {
|
|
|
1102
2153
|
invocation.payloadPath,
|
|
1103
2154
|
invocation.payloadDigest,
|
|
1104
2155
|
],
|
|
2156
|
+
verify: invocation.verify,
|
|
1105
2157
|
cleanup: invocation.cleanup,
|
|
1106
2158
|
};
|
|
1107
2159
|
}
|
|
@@ -1116,6 +2168,7 @@ function buildWindowsAdapterInvocation(entry, invocation) {
|
|
|
1116
2168
|
"-EncodedCommand",
|
|
1117
2169
|
windowsPowerShellBootstrap(entry, invocation),
|
|
1118
2170
|
],
|
|
2171
|
+
verify: invocation.verify,
|
|
1119
2172
|
cleanup: invocation.cleanup,
|
|
1120
2173
|
};
|
|
1121
2174
|
}
|
|
@@ -1202,11 +2255,14 @@ function createFreshWindowsAdapter(runtime, source, hostEnvironment) {
|
|
|
1202
2255
|
runtime,
|
|
1203
2256
|
runtimeFs: runtime.fs,
|
|
1204
2257
|
tempDirectory: source.tempDirectory,
|
|
2258
|
+
tempRootBinding: source.tempRootBinding,
|
|
2259
|
+
idleTtlMs: source.idleTtlMs,
|
|
1205
2260
|
sourceDigest: source.sourceDigest,
|
|
1206
2261
|
sourceContractDigest: source.sourceContractDigest,
|
|
1207
2262
|
loaderMode: source.loaderMode,
|
|
1208
2263
|
powershellPath: powershellHosts[0] || null,
|
|
1209
2264
|
adapterDirectory: materialized.adapterDirectory,
|
|
2265
|
+
adapterDirectoryIdentity: materialized.adapterDirectoryIdentity,
|
|
1210
2266
|
assemblyPath: materialized.assemblyPath,
|
|
1211
2267
|
assemblyDigest: attestation.assemblyDigest,
|
|
1212
2268
|
fileIdentity: attestation.fileIdentity,
|
|
@@ -1362,7 +2418,9 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1362
2418
|
error.adapterReason = payload.reason;
|
|
1363
2419
|
throw error;
|
|
1364
2420
|
}
|
|
1365
|
-
|
|
2421
|
+
const invocation = buildWindowsAdapterInvocation(entry, payload);
|
|
2422
|
+
invocation.verify();
|
|
2423
|
+
return invocation;
|
|
1366
2424
|
};
|
|
1367
2425
|
|
|
1368
2426
|
return {
|
|
@@ -1373,6 +2431,7 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1373
2431
|
let result;
|
|
1374
2432
|
let failure;
|
|
1375
2433
|
try {
|
|
2434
|
+
invocation.verify();
|
|
1376
2435
|
result = runtime.spawnSync(invocation.command, invocation.args, {
|
|
1377
2436
|
...options,
|
|
1378
2437
|
cwd: runtime.joinPath(runtime.windowsDir(), "System32"),
|
|
@@ -1400,20 +2459,40 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1400
2459
|
};
|
|
1401
2460
|
}
|
|
1402
2461
|
|
|
2462
|
+
function readWindowsAdapterBoundFile(runtime, filePath, encoding = null) {
|
|
2463
|
+
const before = inspectWindowsAdapterBoundPath(runtime, filePath, "file");
|
|
2464
|
+
const content = runtime.fs.readFileSync(filePath, encoding || undefined);
|
|
2465
|
+
const after = assertWindowsAdapterBoundPathIdentity(
|
|
2466
|
+
runtime,
|
|
2467
|
+
filePath,
|
|
2468
|
+
"file",
|
|
2469
|
+
before.identity,
|
|
2470
|
+
);
|
|
2471
|
+
if (!sameWindowsFileIdentity(before.identity, after.identity)) {
|
|
2472
|
+
throw windowsAdapterTempRootChangedError();
|
|
2473
|
+
}
|
|
2474
|
+
return { content, fileIdentity: after.identity };
|
|
2475
|
+
}
|
|
2476
|
+
|
|
1403
2477
|
function waitForWindowsTargetIdentity(
|
|
1404
2478
|
proc,
|
|
1405
2479
|
identityPath,
|
|
1406
2480
|
runtime,
|
|
1407
2481
|
expectedAppContainer = null,
|
|
1408
2482
|
timeoutMs = 30_000,
|
|
2483
|
+
onIdentityFileAttested = null,
|
|
1409
2484
|
) {
|
|
1410
2485
|
const deadline = runtime.now() + timeoutMs;
|
|
1411
2486
|
let lastError;
|
|
1412
2487
|
while (runtime.now() < deadline) {
|
|
1413
2488
|
try {
|
|
1414
|
-
const
|
|
1415
|
-
runtime
|
|
2489
|
+
const identityFile = readWindowsAdapterBoundFile(
|
|
2490
|
+
runtime,
|
|
2491
|
+
identityPath,
|
|
2492
|
+
"utf8",
|
|
1416
2493
|
);
|
|
2494
|
+
onIdentityFileAttested?.(identityFile.fileIdentity);
|
|
2495
|
+
const identity = JSON.parse(identityFile.content);
|
|
1417
2496
|
if (identity?.error) {
|
|
1418
2497
|
throw new Error(
|
|
1419
2498
|
`Windows sandbox helper rejected the target: ${identity.error}`,
|
|
@@ -2077,6 +3156,7 @@ export function applyWindowsSandbox(
|
|
|
2077
3156
|
spawnOpts,
|
|
2078
3157
|
sandboxOpts = {},
|
|
2079
3158
|
runtimeOverrides = {},
|
|
3159
|
+
planBindingAuthority = null,
|
|
2080
3160
|
) {
|
|
2081
3161
|
let runtime = resolveRuntime(runtimeOverrides);
|
|
2082
3162
|
const requiredBoundaries = Array.isArray(sandboxOpts.requiredBoundaries)
|
|
@@ -2097,6 +3177,10 @@ export function applyWindowsSandbox(
|
|
|
2097
3177
|
SANDBOX_BOUNDARIES.RESOURCE_LIMITS,
|
|
2098
3178
|
SANDBOX_BOUNDARIES.PRIVILEGE_REDUCTION,
|
|
2099
3179
|
];
|
|
3180
|
+
const appContainerSupportedBoundaries = [
|
|
3181
|
+
...appContainerGuarantees,
|
|
3182
|
+
SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
|
|
3183
|
+
];
|
|
2100
3184
|
const base = {
|
|
2101
3185
|
platform: runtime.platform,
|
|
2102
3186
|
profile: sandboxOpts.profileName || "default",
|
|
@@ -2143,7 +3227,7 @@ export function applyWindowsSandbox(
|
|
|
2143
3227
|
if (
|
|
2144
3228
|
requiresAppContainer &&
|
|
2145
3229
|
requiredBoundaries.some(
|
|
2146
|
-
(boundary) => !
|
|
3230
|
+
(boundary) => !appContainerSupportedBoundaries.includes(boundary),
|
|
2147
3231
|
)
|
|
2148
3232
|
) {
|
|
2149
3233
|
return unavailablePlan("windows_appcontainer_boundary_unsupported");
|
|
@@ -2224,11 +3308,33 @@ export function applyWindowsSandbox(
|
|
|
2224
3308
|
) {
|
|
2225
3309
|
return unavailablePlan("windows_git_nested_process_compatibility");
|
|
2226
3310
|
}
|
|
3311
|
+
const windowsAdapterTempRootBinding = resolveWindowsAdapterTempRoot(
|
|
3312
|
+
runtime,
|
|
3313
|
+
runtimeOverrides,
|
|
3314
|
+
);
|
|
3315
|
+
if (!windowsAdapterTempRootBinding) {
|
|
3316
|
+
return unavailablePlan(WINDOWS_ADAPTER_TEMP_ROOT_UNTRUSTED_REASON);
|
|
3317
|
+
}
|
|
3318
|
+
const windowsAdapterIdleTtlMs = normalizeWindowsAdapterIdleTtlMs(runtime);
|
|
3319
|
+
runtime = {
|
|
3320
|
+
...runtime,
|
|
3321
|
+
windowsAdapterTempRoot: windowsAdapterTempRootBinding.rootPath,
|
|
3322
|
+
windowsAdapterTempRootBinding,
|
|
3323
|
+
windowsAdapterIdleTtlMs,
|
|
3324
|
+
tmpdir: () => windowsAdapterTempRootBinding.rootPath,
|
|
3325
|
+
};
|
|
2227
3326
|
const loaderMode =
|
|
2228
3327
|
profile !== "strict" && !requiresAppContainer && !entrySnapshot.locks
|
|
2229
3328
|
? "managed-executable"
|
|
2230
3329
|
: "powershell-byte-assembly";
|
|
2231
|
-
|
|
3330
|
+
let adapterSource;
|
|
3331
|
+
try {
|
|
3332
|
+
adapterSource = loadWindowsAdapterSource(runtime, loaderMode);
|
|
3333
|
+
} catch (error) {
|
|
3334
|
+
return unavailablePlan(
|
|
3335
|
+
error?.adapterReason || WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON,
|
|
3336
|
+
);
|
|
3337
|
+
}
|
|
2232
3338
|
if (!adapterSource.content) {
|
|
2233
3339
|
return unavailablePlan(adapterSource.reason);
|
|
2234
3340
|
}
|
|
@@ -2273,6 +3379,22 @@ export function applyWindowsSandbox(
|
|
|
2273
3379
|
.toString("hex")}.json`,
|
|
2274
3380
|
)
|
|
2275
3381
|
: null;
|
|
3382
|
+
let identityFileIdentity = null;
|
|
3383
|
+
if (identityPath) {
|
|
3384
|
+
try {
|
|
3385
|
+
if (
|
|
3386
|
+
inspectWindowsAdapterBoundPath(runtime, identityPath, "file", {
|
|
3387
|
+
allowMissing: true,
|
|
3388
|
+
}).exists
|
|
3389
|
+
) {
|
|
3390
|
+
return unavailablePlan("windows_identity_random_path_collision");
|
|
3391
|
+
}
|
|
3392
|
+
} catch (error) {
|
|
3393
|
+
return unavailablePlan(
|
|
3394
|
+
error?.adapterReason || WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON,
|
|
3395
|
+
);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
2276
3398
|
const launchSpec = {
|
|
2277
3399
|
cpuSeconds: Number(limits.cpu || 0),
|
|
2278
3400
|
processMemoryBytes: Number(
|
|
@@ -2313,7 +3435,13 @@ export function applyWindowsSandbox(
|
|
|
2313
3435
|
if (identityPath) launchSpec.identityPath = identityPath;
|
|
2314
3436
|
const cleanupIdentity = () => {
|
|
2315
3437
|
if (!identityPath) return true;
|
|
2316
|
-
return cleanupOrTrackWindowsTemporaryPath(
|
|
3438
|
+
return cleanupOrTrackWindowsTemporaryPath(
|
|
3439
|
+
runtime,
|
|
3440
|
+
identityPath,
|
|
3441
|
+
100,
|
|
3442
|
+
null,
|
|
3443
|
+
identityFileIdentity,
|
|
3444
|
+
);
|
|
2317
3445
|
};
|
|
2318
3446
|
const appContainerProfileName = requiresAppContainer
|
|
2319
3447
|
? `ChainlessChain.CliSandbox.${runtime.randomBytes(12).toString("hex")}`
|
|
@@ -2503,10 +3631,14 @@ export function applyWindowsSandbox(
|
|
|
2503
3631
|
detached: false,
|
|
2504
3632
|
shell: false,
|
|
2505
3633
|
cwd: helperWorkingDirectory,
|
|
2506
|
-
env: hostEnvironment,
|
|
3634
|
+
env: Object.freeze({ ...hostEnvironment }),
|
|
3635
|
+
stdio: Array.isArray(invocation.options.stdio)
|
|
3636
|
+
? Object.freeze([...invocation.options.stdio])
|
|
3637
|
+
: invocation.options.stdio,
|
|
2507
3638
|
};
|
|
2508
3639
|
let appContainerCleanupVerified = false;
|
|
2509
3640
|
let appContainerCleanupRecord = null;
|
|
3641
|
+
let issuedWindowsMcpPlan = null;
|
|
2510
3642
|
const cleanupAppContainer = () => {
|
|
2511
3643
|
if (!appContainer || appContainerCleanupVerified) return true;
|
|
2512
3644
|
if (appContainerCleanupRecord) {
|
|
@@ -2544,6 +3676,10 @@ export function applyWindowsSandbox(
|
|
|
2544
3676
|
return false;
|
|
2545
3677
|
};
|
|
2546
3678
|
const cleanup = () => {
|
|
3679
|
+
if (issuedWindowsMcpPlan) {
|
|
3680
|
+
issuedWindowsMcpCodeSnapshotPlans.delete(issuedWindowsMcpPlan);
|
|
3681
|
+
issuedWindowsMcpPlan = null;
|
|
3682
|
+
}
|
|
2547
3683
|
const failures = [];
|
|
2548
3684
|
const attempt = (label, action) => {
|
|
2549
3685
|
try {
|
|
@@ -2579,6 +3715,10 @@ export function applyWindowsSandbox(
|
|
|
2579
3715
|
identityPath,
|
|
2580
3716
|
runtime,
|
|
2581
3717
|
appContainer,
|
|
3718
|
+
30_000,
|
|
3719
|
+
(fileIdentity) => {
|
|
3720
|
+
identityFileIdentity = fileIdentity;
|
|
3721
|
+
},
|
|
2582
3722
|
);
|
|
2583
3723
|
if (!cleanupIdentity()) {
|
|
2584
3724
|
throw new Error(
|
|
@@ -2604,6 +3744,28 @@ export function applyWindowsSandbox(
|
|
|
2604
3744
|
},
|
|
2605
3745
|
}
|
|
2606
3746
|
: {};
|
|
3747
|
+
const windowsMcpPlanBinding =
|
|
3748
|
+
entrySnapshot.locks &&
|
|
3749
|
+
sandboxOpts.executionContract?.kind ===
|
|
3750
|
+
MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND
|
|
3751
|
+
? createWindowsMcpCodeSnapshotPlanBinding({
|
|
3752
|
+
executionContract: sandboxOpts.executionContract,
|
|
3753
|
+
command,
|
|
3754
|
+
args,
|
|
3755
|
+
spawnOpts,
|
|
3756
|
+
profile,
|
|
3757
|
+
requiredBoundaries,
|
|
3758
|
+
sync: sandboxOpts.sync === true,
|
|
3759
|
+
backend,
|
|
3760
|
+
policyAttested: requiresAppContainer ? true : null,
|
|
3761
|
+
policyDigest: appContainerPolicyDigest,
|
|
3762
|
+
adapterSource,
|
|
3763
|
+
helperInvocation,
|
|
3764
|
+
helperOptions: options,
|
|
3765
|
+
postSpawn: identityContract.postSpawn,
|
|
3766
|
+
postSpawnWindows: identityContract.postSpawnWindows,
|
|
3767
|
+
})
|
|
3768
|
+
: null;
|
|
2607
3769
|
const entrySnapshotRuntimeProbe = entrySnapshot.locks
|
|
2608
3770
|
? {
|
|
2609
3771
|
...(appContainerRuntimeProbe || nodeSnapshotRuntimeProbe),
|
|
@@ -2636,16 +3798,19 @@ export function applyWindowsSandbox(
|
|
|
2636
3798
|
runtimeLaunchMechanism:
|
|
2637
3799
|
"filter-oplock-locked-createprocess-suspended-image-v1",
|
|
2638
3800
|
sharedLibraryClosure: false,
|
|
3801
|
+
planBindingMechanism: "windows-mcp-code-snapshot-plan-binding-v1",
|
|
3802
|
+
planBindingDigest: windowsMcpPlanBinding.planBindingDigest,
|
|
2639
3803
|
}
|
|
2640
3804
|
: {}),
|
|
2641
3805
|
}
|
|
2642
3806
|
: appContainerRuntimeProbe;
|
|
2643
|
-
|
|
3807
|
+
const plan = createSandboxPlan({
|
|
2644
3808
|
...base,
|
|
2645
3809
|
applied: true,
|
|
2646
3810
|
enforcement: backend,
|
|
2647
3811
|
policyAttested: requiresAppContainer ? true : null,
|
|
2648
3812
|
policyDigest: appContainerPolicyDigest,
|
|
3813
|
+
adapterTempRootAttestation: adapterSource.tempRootAttestation,
|
|
2649
3814
|
runtimeProbe: entrySnapshotRuntimeProbe,
|
|
2650
3815
|
guarantees: [
|
|
2651
3816
|
...(requiresAppContainer
|
|
@@ -2667,6 +3832,14 @@ export function applyWindowsSandbox(
|
|
|
2667
3832
|
...identityContract,
|
|
2668
3833
|
cleanup,
|
|
2669
3834
|
});
|
|
3835
|
+
if (
|
|
3836
|
+
windowsMcpPlanBinding &&
|
|
3837
|
+
planBindingAuthority === WINDOWS_MCP_CODE_SNAPSHOT_ISSUER
|
|
3838
|
+
) {
|
|
3839
|
+
issuedWindowsMcpPlan = plan;
|
|
3840
|
+
issuedWindowsMcpCodeSnapshotPlans.set(plan, windowsMcpPlanBinding);
|
|
3841
|
+
}
|
|
3842
|
+
return plan;
|
|
2670
3843
|
}
|
|
2671
3844
|
|
|
2672
3845
|
/**
|
|
@@ -2717,13 +3890,12 @@ function linuxBubblewrapProbe(
|
|
|
2717
3890
|
const supervisorDescriptorBound =
|
|
2718
3891
|
supervisorBinding?.mechanism === LINUX_BWRAP_SUPERVISOR_BINDING_MECHANISM;
|
|
2719
3892
|
const pluginTreeContentSnapshot =
|
|
2720
|
-
!native &&
|
|
2721
3893
|
attempted &&
|
|
2722
3894
|
snapshot &&
|
|
2723
3895
|
runnable &&
|
|
2724
3896
|
reason === null &&
|
|
2725
3897
|
supervisorDescriptorBound &&
|
|
2726
|
-
pluginTreeSnapshot?.scope ===
|
|
3898
|
+
pluginTreeSnapshot?.scope === LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE &&
|
|
2727
3899
|
pluginTreeSnapshot?.mechanism === LINUX_ENTRY_SNAPSHOT_MECHANISM &&
|
|
2728
3900
|
Number.isSafeInteger(pluginTreeSnapshot?.files) &&
|
|
2729
3901
|
pluginTreeSnapshot.files > 0 &&
|
|
@@ -2734,7 +3906,7 @@ function linuxBubblewrapProbe(
|
|
|
2734
3906
|
typeof pluginTreeSnapshot?.digest === "string" &&
|
|
2735
3907
|
/^[a-f0-9]{64}$/.test(pluginTreeSnapshot.digest) &&
|
|
2736
3908
|
pluginTreeSnapshot?.consistency ===
|
|
2737
|
-
|
|
3909
|
+
LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY &&
|
|
2738
3910
|
pluginTreeSnapshot?.contractBound === false &&
|
|
2739
3911
|
pluginTreeSnapshot?.atomic === false;
|
|
2740
3912
|
const initialDynamicLoadClosureDescriptorBound =
|
|
@@ -3349,16 +4521,20 @@ function linuxEntrySnapshotContract(targetRuntime) {
|
|
|
3349
4521
|
throw new Error("entry_snapshot_runtime_unsupported");
|
|
3350
4522
|
}
|
|
3351
4523
|
|
|
3352
|
-
function
|
|
4524
|
+
function linuxPluginTreeFileSnapshotContract(sourceMode, targetRuntime) {
|
|
4525
|
+
const native = targetRuntime?.startsWith("native-");
|
|
4526
|
+
const errorPrefix = native
|
|
4527
|
+
? "native_plugin_tree_snapshot"
|
|
4528
|
+
: "node_plugin_tree_snapshot";
|
|
3353
4529
|
if (!Number.isSafeInteger(sourceMode) || sourceMode < 0) {
|
|
3354
|
-
throw new Error(
|
|
4530
|
+
throw new Error(`${errorPrefix}_source_mode_invalid`);
|
|
3355
4531
|
}
|
|
3356
4532
|
return {
|
|
3357
|
-
scope:
|
|
4533
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
3358
4534
|
targetMode: (sourceMode & 0o111) !== 0 ? "0500" : "0400",
|
|
3359
4535
|
sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
|
|
3360
4536
|
minimumBytes: 0,
|
|
3361
|
-
errorPrefix
|
|
4537
|
+
errorPrefix,
|
|
3362
4538
|
};
|
|
3363
4539
|
}
|
|
3364
4540
|
|
|
@@ -3439,9 +4615,9 @@ function createLinuxRegularFileSnapshot(
|
|
|
3439
4615
|
throw snapshotError("source_changed");
|
|
3440
4616
|
}
|
|
3441
4617
|
if (
|
|
3442
|
-
snapshotContract.scope ===
|
|
4618
|
+
snapshotContract.scope === LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE &&
|
|
3443
4619
|
snapshotContract.targetMode !==
|
|
3444
|
-
|
|
4620
|
+
((sourceFileMode & 0o111) !== 0 ? "0500" : "0400")
|
|
3445
4621
|
) {
|
|
3446
4622
|
throw snapshotError("source_mode_changed");
|
|
3447
4623
|
}
|
|
@@ -3829,7 +5005,6 @@ function applyLinuxMcpCapsuleCodeSnapshot(
|
|
|
3829
5005
|
]);
|
|
3830
5006
|
const descriptors = [runtimeSnapshot.finalMount, entrySnapshot.finalMount];
|
|
3831
5007
|
const runtimeFd = 3;
|
|
3832
|
-
const entryFd = 4;
|
|
3833
5008
|
const snapshotIdentity = Object.freeze({
|
|
3834
5009
|
runtimeSnapshotSha256: runtimeSnapshot.attestation.sha256,
|
|
3835
5010
|
runtimeSnapshotBytes: runtimeSnapshot.attestation.bytes,
|
|
@@ -3846,6 +5021,8 @@ function applyLinuxMcpCapsuleCodeSnapshot(
|
|
|
3846
5021
|
].sort(),
|
|
3847
5022
|
identity: snapshotIdentity,
|
|
3848
5023
|
cwd: invocation.options.cwd,
|
|
5024
|
+
entrySnapshotBootstrapSha256: MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256,
|
|
5025
|
+
runtimeLaunchMechanism: "inherited-executable-fd-v1",
|
|
3849
5026
|
passthroughArgsDigest: sha256(JSON.stringify(invocation.args.slice(1))),
|
|
3850
5027
|
}),
|
|
3851
5028
|
);
|
|
@@ -3874,13 +5051,22 @@ function applyLinuxMcpCapsuleCodeSnapshot(
|
|
|
3874
5051
|
contentSnapshotScope: "mcp-capsule-entry-and-node-runtime",
|
|
3875
5052
|
contentSnapshotMechanism: LINUX_MCP_CAPSULE_SNAPSHOT_MECHANISM,
|
|
3876
5053
|
handleAtomic: true,
|
|
5054
|
+
entrySnapshotAtomic: true,
|
|
5055
|
+
runtimeLaunchAtomic: true,
|
|
5056
|
+
runtimeLaunchMechanism: "inherited-executable-fd-v1",
|
|
5057
|
+
entrySnapshotBootstrapSha256: MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256,
|
|
3877
5058
|
sharedLibraryClosure: false,
|
|
3878
5059
|
...snapshotIdentity,
|
|
3879
5060
|
},
|
|
3880
5061
|
reason: null,
|
|
3881
5062
|
guarantees: [SANDBOX_BOUNDARIES.CODE_SNAPSHOT],
|
|
3882
5063
|
command: `/proc/self/fd/${runtimeFd}`,
|
|
3883
|
-
args: [
|
|
5064
|
+
args: [
|
|
5065
|
+
"-e",
|
|
5066
|
+
MCP_STDIO_FD_ENTRY_BOOTSTRAP,
|
|
5067
|
+
"--",
|
|
5068
|
+
...invocation.args.slice(1),
|
|
5069
|
+
],
|
|
3884
5070
|
options: {
|
|
3885
5071
|
...invocation.options,
|
|
3886
5072
|
shell: false,
|
|
@@ -3892,7 +5078,21 @@ function applyLinuxMcpCapsuleCodeSnapshot(
|
|
|
3892
5078
|
});
|
|
3893
5079
|
}
|
|
3894
5080
|
|
|
3895
|
-
|
|
5081
|
+
/**
|
|
5082
|
+
* Bind the synthetic plugin tree to the anonymous content snapshots mounted
|
|
5083
|
+
* for launch. Directory membership is fixed by pinLinuxPluginTree; contents
|
|
5084
|
+
* are copied and sealed per file, so the evidence deliberately remains
|
|
5085
|
+
* `atomic: false` rather than claiming one filesystem-wide transaction.
|
|
5086
|
+
*/
|
|
5087
|
+
function buildLinuxPluginTreeSnapshotAttestation(
|
|
5088
|
+
mounts,
|
|
5089
|
+
entryDestination,
|
|
5090
|
+
targetRuntime,
|
|
5091
|
+
) {
|
|
5092
|
+
const native = targetRuntime?.startsWith("native-");
|
|
5093
|
+
const errorPrefix = native
|
|
5094
|
+
? "native_plugin_tree_snapshot"
|
|
5095
|
+
: "node_plugin_tree_snapshot";
|
|
3896
5096
|
const members = [];
|
|
3897
5097
|
const destinations = new Set();
|
|
3898
5098
|
for (const mount of mounts || []) {
|
|
@@ -3901,18 +5101,23 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
3901
5101
|
path.posix.normalize(mount.destination) !== mount.destination ||
|
|
3902
5102
|
destinations.has(mount.destination)
|
|
3903
5103
|
) {
|
|
3904
|
-
throw new Error(
|
|
5104
|
+
throw new Error(`${errorPrefix}_destination_invalid`);
|
|
3905
5105
|
}
|
|
3906
5106
|
destinations.add(mount.destination);
|
|
3907
5107
|
const snapshot = mount.contentSnapshot;
|
|
3908
5108
|
const entry = mount.destination === entryDestination;
|
|
3909
5109
|
const expectedScope = entry
|
|
3910
|
-
?
|
|
3911
|
-
|
|
5110
|
+
? native
|
|
5111
|
+
? LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE
|
|
5112
|
+
: LINUX_NODE_ENTRY_SNAPSHOT_SCOPE
|
|
5113
|
+
: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE;
|
|
3912
5114
|
const sourceMode = Number.parseInt(snapshot?.sourceFileMode, 8);
|
|
3913
5115
|
const expectedTargetMode = entry
|
|
3914
|
-
?
|
|
3915
|
-
|
|
5116
|
+
? native
|
|
5117
|
+
? LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE
|
|
5118
|
+
: LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE
|
|
5119
|
+
: linuxPluginTreeFileSnapshotContract(sourceMode, targetRuntime)
|
|
5120
|
+
.targetMode;
|
|
3916
5121
|
if (
|
|
3917
5122
|
snapshot?.scope !== expectedScope ||
|
|
3918
5123
|
snapshot?.mechanism !== LINUX_ENTRY_SNAPSHOT_MECHANISM ||
|
|
@@ -3935,7 +5140,7 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
3935
5140
|
mount.mountMode !== "ro-bind-data" ||
|
|
3936
5141
|
mount.mountPermissions !== expectedTargetMode
|
|
3937
5142
|
) {
|
|
3938
|
-
throw new Error(
|
|
5143
|
+
throw new Error(`${errorPrefix}_member_invalid`);
|
|
3939
5144
|
}
|
|
3940
5145
|
members.push({
|
|
3941
5146
|
destination: mount.destination,
|
|
@@ -3962,58 +5167,66 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
3962
5167
|
members.length > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES ||
|
|
3963
5168
|
!members.some((member) => member.destination === entryDestination)
|
|
3964
5169
|
) {
|
|
3965
|
-
throw new Error(
|
|
5170
|
+
throw new Error(`${errorPrefix}_file_count_invalid`);
|
|
3966
5171
|
}
|
|
3967
5172
|
const bytes = members.reduce((total, member) => total + member.bytes, 0);
|
|
3968
5173
|
if (
|
|
3969
5174
|
!Number.isSafeInteger(bytes) ||
|
|
3970
5175
|
bytes > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES
|
|
3971
5176
|
) {
|
|
3972
|
-
throw new Error(
|
|
5177
|
+
throw new Error(`${errorPrefix}_bytes_exceeded`);
|
|
3973
5178
|
}
|
|
3974
5179
|
const digest = crypto
|
|
3975
5180
|
.createHash("sha256")
|
|
3976
5181
|
.update(
|
|
3977
5182
|
JSON.stringify({
|
|
3978
5183
|
version: 1,
|
|
3979
|
-
scope:
|
|
3980
|
-
|
|
5184
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
5185
|
+
...(native ? { targetRuntime } : {}),
|
|
5186
|
+
consistency: LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY,
|
|
3981
5187
|
members,
|
|
3982
5188
|
}),
|
|
3983
5189
|
)
|
|
3984
5190
|
.digest("hex");
|
|
3985
5191
|
return Object.freeze({
|
|
3986
|
-
scope:
|
|
5192
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
3987
5193
|
mechanism: LINUX_ENTRY_SNAPSHOT_MECHANISM,
|
|
3988
5194
|
files: members.length,
|
|
3989
5195
|
bytes,
|
|
3990
5196
|
digest,
|
|
3991
|
-
consistency:
|
|
5197
|
+
consistency: LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY,
|
|
3992
5198
|
contractBound: false,
|
|
3993
5199
|
atomic: false,
|
|
3994
5200
|
});
|
|
3995
5201
|
}
|
|
3996
5202
|
|
|
3997
|
-
function
|
|
5203
|
+
function attestLinuxPluginTreeSnapshot(
|
|
3998
5204
|
runtime,
|
|
3999
5205
|
mounts,
|
|
4000
5206
|
entryDestination,
|
|
5207
|
+
targetRuntime,
|
|
4001
5208
|
expected,
|
|
4002
5209
|
) {
|
|
5210
|
+
const native = targetRuntime?.startsWith("native-");
|
|
5211
|
+
const errorPrefix = native
|
|
5212
|
+
? "native_plugin_tree_snapshot"
|
|
5213
|
+
: "node_plugin_tree_snapshot";
|
|
4003
5214
|
for (const mount of mounts || []) {
|
|
4004
5215
|
if (!linuxPathWithin("/opt/chainless/plugin", mount?.destination)) continue;
|
|
4005
5216
|
const snapshot = mount.contentSnapshot;
|
|
4006
5217
|
const contract =
|
|
4007
5218
|
mount.destination === entryDestination
|
|
4008
|
-
? linuxEntrySnapshotContract(
|
|
4009
|
-
:
|
|
5219
|
+
? linuxEntrySnapshotContract(targetRuntime)
|
|
5220
|
+
: linuxPluginTreeFileSnapshotContract(
|
|
4010
5221
|
Number.parseInt(snapshot?.sourceFileMode, 8),
|
|
5222
|
+
targetRuntime,
|
|
4011
5223
|
);
|
|
4012
5224
|
attestLinuxRegularFileSnapshot(runtime, mount, snapshot, contract);
|
|
4013
5225
|
}
|
|
4014
|
-
const actual =
|
|
5226
|
+
const actual = buildLinuxPluginTreeSnapshotAttestation(
|
|
4015
5227
|
mounts,
|
|
4016
5228
|
entryDestination,
|
|
5229
|
+
targetRuntime,
|
|
4017
5230
|
);
|
|
4018
5231
|
for (const field of [
|
|
4019
5232
|
"scope",
|
|
@@ -4026,7 +5239,7 @@ function attestLinuxNodePluginTreeSnapshot(
|
|
|
4026
5239
|
"atomic",
|
|
4027
5240
|
]) {
|
|
4028
5241
|
if (actual[field] !== expected?.[field]) {
|
|
4029
|
-
throw new Error(
|
|
5242
|
+
throw new Error(`${errorPrefix}_identity_changed`);
|
|
4030
5243
|
}
|
|
4031
5244
|
}
|
|
4032
5245
|
}
|
|
@@ -5707,7 +6920,10 @@ export function applyLinuxSandbox(
|
|
|
5707
6920
|
boundary === SANDBOX_BOUNDARIES.PROCESS_TREE,
|
|
5708
6921
|
);
|
|
5709
6922
|
|
|
5710
|
-
if (
|
|
6923
|
+
if (
|
|
6924
|
+
requiredBoundaries.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT) &&
|
|
6925
|
+
!requiresStrongLinuxBoundary
|
|
6926
|
+
) {
|
|
5711
6927
|
return applyLinuxMcpCapsuleCodeSnapshot(
|
|
5712
6928
|
command,
|
|
5713
6929
|
args,
|
|
@@ -5921,30 +7137,33 @@ export function applyLinuxSandbox(
|
|
|
5921
7137
|
const rawPluginMounts = pluginMountIndexes.map(
|
|
5922
7138
|
(index) => pinnedMounts[index],
|
|
5923
7139
|
);
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
) {
|
|
5938
|
-
throw new Error("node_plugin_tree_snapshot_member_size_invalid");
|
|
5939
|
-
}
|
|
5940
|
-
return total + bytes;
|
|
5941
|
-
}, 0);
|
|
7140
|
+
const pluginSnapshotErrorPrefix = validation.entryRuntime.startsWith(
|
|
7141
|
+
"native-",
|
|
7142
|
+
)
|
|
7143
|
+
? "native_plugin_tree_snapshot"
|
|
7144
|
+
: "node_plugin_tree_snapshot";
|
|
7145
|
+
if (
|
|
7146
|
+
pluginMountIndexes.length < 1 ||
|
|
7147
|
+
pluginMountIndexes.length > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES
|
|
7148
|
+
) {
|
|
7149
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_file_count_invalid`);
|
|
7150
|
+
}
|
|
7151
|
+
const pluginBytes = pluginMountIndexes.reduce((total, index) => {
|
|
7152
|
+
const bytes = pinnedMounts[index]?.bytes;
|
|
5942
7153
|
if (
|
|
5943
|
-
!Number.isSafeInteger(
|
|
5944
|
-
|
|
7154
|
+
!Number.isSafeInteger(bytes) ||
|
|
7155
|
+
bytes < 0 ||
|
|
7156
|
+
bytes > LINUX_ATTESTED_FILE_MAX_BYTES
|
|
5945
7157
|
) {
|
|
5946
|
-
throw new Error(
|
|
7158
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_member_size_invalid`);
|
|
5947
7159
|
}
|
|
7160
|
+
return total + bytes;
|
|
7161
|
+
}, 0);
|
|
7162
|
+
if (
|
|
7163
|
+
!Number.isSafeInteger(pluginBytes) ||
|
|
7164
|
+
pluginBytes > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES
|
|
7165
|
+
) {
|
|
7166
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_bytes_exceeded`);
|
|
5948
7167
|
}
|
|
5949
7168
|
const rawEntryMount = entryMount;
|
|
5950
7169
|
const snapshot = createLinuxEntrySnapshot(
|
|
@@ -5958,37 +7177,36 @@ export function applyLinuxSandbox(
|
|
|
5958
7177
|
finalMounts[entryIndex] = snapshot.finalMount;
|
|
5959
7178
|
probeMounts[entryIndex] = snapshot.probeMount;
|
|
5960
7179
|
entrySnapshot = snapshot;
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
finalMounts[index] = fileSnapshot.finalMount;
|
|
5978
|
-
probeMounts[index] = fileSnapshot.probeMount;
|
|
5979
|
-
}
|
|
5980
|
-
pluginTreeSnapshot = buildLinuxNodePluginTreeSnapshotAttestation(
|
|
5981
|
-
finalMounts,
|
|
5982
|
-
sandboxEntry,
|
|
7180
|
+
for (const index of pluginMountIndexes) {
|
|
7181
|
+
if (index === entryIndex) continue;
|
|
7182
|
+
const rawMount = pinnedMounts[index];
|
|
7183
|
+
const source = runtime.fs.fstatSync(rawMount.fd);
|
|
7184
|
+
const fileSnapshot = createLinuxRegularFileSnapshot(
|
|
7185
|
+
runtime,
|
|
7186
|
+
rawMount,
|
|
7187
|
+
{
|
|
7188
|
+
fileId: rawMount.fileId,
|
|
7189
|
+
bytes: rawMount.bytes,
|
|
7190
|
+
mtimeMs: rawMount.mtimeMs,
|
|
7191
|
+
},
|
|
7192
|
+
linuxPluginTreeFileSnapshotContract(
|
|
7193
|
+
Number(source.mode),
|
|
7194
|
+
validation.entryRuntime,
|
|
7195
|
+
),
|
|
5983
7196
|
);
|
|
7197
|
+
pluginTreeFileSnapshots.push(fileSnapshot);
|
|
7198
|
+
finalMounts[index] = fileSnapshot.finalMount;
|
|
7199
|
+
probeMounts[index] = fileSnapshot.probeMount;
|
|
5984
7200
|
}
|
|
7201
|
+
pluginTreeSnapshot = buildLinuxPluginTreeSnapshotAttestation(
|
|
7202
|
+
finalMounts,
|
|
7203
|
+
sandboxEntry,
|
|
7204
|
+
validation.entryRuntime,
|
|
7205
|
+
);
|
|
5985
7206
|
pinnedMounts = finalMounts;
|
|
5986
7207
|
probePinnedMounts = probeMounts;
|
|
5987
7208
|
entryMount = entrySnapshot.finalMount;
|
|
5988
|
-
closeLinuxPinnedMounts(
|
|
5989
|
-
runtime,
|
|
5990
|
-
validation.entryRuntime === "node" ? rawPluginMounts : [rawEntryMount],
|
|
5991
|
-
);
|
|
7209
|
+
closeLinuxPinnedMounts(runtime, rawPluginMounts);
|
|
5992
7210
|
} catch (error) {
|
|
5993
7211
|
closeLinuxPinnedMounts(runtime, pinnedMounts);
|
|
5994
7212
|
closeLinuxPinnedMounts(runtime, [
|
|
@@ -6168,7 +7386,7 @@ export function applyLinuxSandbox(
|
|
|
6168
7386
|
.createHash("sha256")
|
|
6169
7387
|
.update(
|
|
6170
7388
|
JSON.stringify({
|
|
6171
|
-
version:
|
|
7389
|
+
version: 4,
|
|
6172
7390
|
backend: LINUX_BWRAP_BACKEND,
|
|
6173
7391
|
supervisor: supervisorPin.attestation,
|
|
6174
7392
|
contract: {
|
|
@@ -6212,9 +7430,7 @@ export function applyLinuxSandbox(
|
|
|
6212
7430
|
...(nativeDynamicClosure
|
|
6213
7431
|
? { initialDynamicLoadClosure: nativeDynamicClosure }
|
|
6214
7432
|
: {}),
|
|
6215
|
-
|
|
6216
|
-
? { pluginTreeContentSnapshot: pluginTreeSnapshot }
|
|
6217
|
-
: {}),
|
|
7433
|
+
pluginTreeContentSnapshot: pluginTreeSnapshot,
|
|
6218
7434
|
seccomp: {
|
|
6219
7435
|
arch: seccompFilter.arch,
|
|
6220
7436
|
policy: seccompFilter.policy,
|
|
@@ -6287,21 +7503,13 @@ export function applyLinuxSandbox(
|
|
|
6287
7503
|
});
|
|
6288
7504
|
}
|
|
6289
7505
|
try {
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
} else {
|
|
6298
|
-
attestLinuxEntrySnapshot(
|
|
6299
|
-
runtime,
|
|
6300
|
-
entryMount,
|
|
6301
|
-
entrySnapshot.attestation,
|
|
6302
|
-
validation.entryRuntime,
|
|
6303
|
-
);
|
|
6304
|
-
}
|
|
7506
|
+
attestLinuxPluginTreeSnapshot(
|
|
7507
|
+
runtime,
|
|
7508
|
+
pinnedMounts,
|
|
7509
|
+
sandboxEntry,
|
|
7510
|
+
validation.entryRuntime,
|
|
7511
|
+
pluginTreeSnapshot,
|
|
7512
|
+
);
|
|
6305
7513
|
} catch (error) {
|
|
6306
7514
|
closeStrongLinuxResources(supervisorPin, finalLaunch);
|
|
6307
7515
|
return createSandboxPlan({
|
|
@@ -6369,17 +7577,36 @@ export function applyLinuxSandbox(
|
|
|
6369
7577
|
const supervisorBinding = supervisorPin.attestation;
|
|
6370
7578
|
closeLinuxPinnedMounts(runtime, [supervisorPin]);
|
|
6371
7579
|
supervisorPin = null;
|
|
7580
|
+
const capsuleContract =
|
|
7581
|
+
validation.contract.kind === MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND;
|
|
7582
|
+
const baseRuntimeProbe = linuxBubblewrapProbe(
|
|
7583
|
+
true,
|
|
7584
|
+
true,
|
|
7585
|
+
null,
|
|
7586
|
+
validation.entryRuntime,
|
|
7587
|
+
entrySnapshot?.attestation,
|
|
7588
|
+
supervisorBinding,
|
|
7589
|
+
pluginTreeSnapshot,
|
|
7590
|
+
nativeDynamicClosure,
|
|
7591
|
+
);
|
|
6372
7592
|
const runtimeProbe = Object.freeze(
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
7593
|
+
capsuleContract
|
|
7594
|
+
? {
|
|
7595
|
+
...baseRuntimeProbe,
|
|
7596
|
+
mcpCapsuleCodeSnapshot: true,
|
|
7597
|
+
entrySnapshotAtomic: true,
|
|
7598
|
+
runtimeLaunchAtomic: true,
|
|
7599
|
+
runtimeLaunchMechanism:
|
|
7600
|
+
"bwrap-descriptor-mount-node-runtime-exec-v1",
|
|
7601
|
+
sharedLibraryClosure: false,
|
|
7602
|
+
runtimeSnapshotSha256: validation.contract.runtimeIdentity.sha256,
|
|
7603
|
+
runtimeSnapshotBytes: validation.contract.runtimeIdentity.bytes,
|
|
7604
|
+
entrySnapshotSha256: entrySnapshot.attestation.sha256,
|
|
7605
|
+
entrySnapshotBytes: entrySnapshot.attestation.bytes,
|
|
7606
|
+
runtimeLaunchPath: "/opt/chainless/runtime/node",
|
|
7607
|
+
entrySnapshotPath: sandboxEntry,
|
|
7608
|
+
}
|
|
7609
|
+
: baseRuntimeProbe,
|
|
6383
7610
|
);
|
|
6384
7611
|
const options = {
|
|
6385
7612
|
...spawnOpts,
|
|
@@ -6417,6 +7644,7 @@ export function applyLinuxSandbox(
|
|
|
6417
7644
|
SANDBOX_BOUNDARIES.FILESYSTEM,
|
|
6418
7645
|
SANDBOX_BOUNDARIES.NETWORK,
|
|
6419
7646
|
SANDBOX_BOUNDARIES.PROCESS_TREE,
|
|
7647
|
+
...(capsuleContract ? [SANDBOX_BOUNDARIES.CODE_SNAPSHOT] : []),
|
|
6420
7648
|
],
|
|
6421
7649
|
command: finalLaunch.command,
|
|
6422
7650
|
args: [...policyArgs, "--", ...targetArgs],
|
|
@@ -6492,21 +7720,27 @@ export function applySandbox(
|
|
|
6492
7720
|
args,
|
|
6493
7721
|
spawnOpts,
|
|
6494
7722
|
profileOrRequest = "default",
|
|
6495
|
-
runtimeOverrides =
|
|
7723
|
+
runtimeOverrides = undefined,
|
|
6496
7724
|
explicitRequest = null,
|
|
6497
7725
|
) {
|
|
7726
|
+
const runtimeInjected = runtimeOverrides !== undefined;
|
|
6498
7727
|
const runtime = resolveRuntime(runtimeOverrides);
|
|
6499
7728
|
const sandboxRequest = normalizeSandboxRequest(
|
|
6500
7729
|
profileOrRequest,
|
|
6501
7730
|
explicitRequest,
|
|
6502
7731
|
);
|
|
6503
7732
|
const profileName = sandboxRequest.profile;
|
|
7733
|
+
// Windows does not consume the POSIX allow-write lists. Avoid invoking an
|
|
7734
|
+
// embedding temp-directory hook while eagerly constructing three unused
|
|
7735
|
+
// profiles; applyWindowsSandbox resolves and pins that hook exactly once.
|
|
7736
|
+
const profileTempDirectory =
|
|
7737
|
+
runtime.platform === "win32" ? null : runtime.tmpdir();
|
|
6504
7738
|
const profiles = {
|
|
6505
7739
|
default: {
|
|
6506
7740
|
allowNetwork: false,
|
|
6507
7741
|
allowExec: true,
|
|
6508
7742
|
allowRead: [runtime.homedir(), "/tmp"],
|
|
6509
|
-
allowWrite: [
|
|
7743
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6510
7744
|
limits: {
|
|
6511
7745
|
// RLIMIT_NPROC is intentionally omitted. Linux applies it per real
|
|
6512
7746
|
// user ID rather than to this child tree, so it can starve unrelated
|
|
@@ -6519,7 +7753,7 @@ export function applySandbox(
|
|
|
6519
7753
|
allowNetwork: false,
|
|
6520
7754
|
allowExec: false,
|
|
6521
7755
|
allowRead: [],
|
|
6522
|
-
allowWrite: [
|
|
7756
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6523
7757
|
limits: {
|
|
6524
7758
|
cpu: 10,
|
|
6525
7759
|
as: 256 * 1024 * 1024, // 256MB address space
|
|
@@ -6530,7 +7764,7 @@ export function applySandbox(
|
|
|
6530
7764
|
allowNetwork: true,
|
|
6531
7765
|
allowExec: true,
|
|
6532
7766
|
allowRead: [runtime.homedir()],
|
|
6533
|
-
allowWrite: [
|
|
7767
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6534
7768
|
limits: {
|
|
6535
7769
|
cpu: 60,
|
|
6536
7770
|
nofile: 512,
|
|
@@ -6558,6 +7792,7 @@ export function applySandbox(
|
|
|
6558
7792
|
spawnOpts,
|
|
6559
7793
|
profile,
|
|
6560
7794
|
runtimeOverrides,
|
|
7795
|
+
runtimeInjected ? null : WINDOWS_MCP_CODE_SNAPSHOT_ISSUER,
|
|
6561
7796
|
);
|
|
6562
7797
|
}
|
|
6563
7798
|
if (runtime.platform === "linux") {
|