chainlesschain 0.163.2 → 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-DnGsmsqJ.js → AIOps-DIY6KxV-.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-BXH0muPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-D39w8vmg.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-CUU1zvLs.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-D8azHO3T.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-D0tAwXTg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-C29mhJll.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-D6Aa1Gro.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-B1uoHbPQ.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-Bs4DLOR5.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-DUYIBSle.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-CNcBPEa4.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-CIY89763.js} +1 -1
- package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-CsPYFmgo.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-BBocEge4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-TQ8Ux0Lf.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-DfBsZTft.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-Cg-I6FVH.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-DTQzfRTp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-DGxEKGpR.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-DLZ1RDSr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-XneICKNI.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-BRU00J1-.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BzCbJuAy.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-D7BRyNBG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-D9DeFIhW.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-6rAtA9Ko.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-8Hqy80pU.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-Cmvkj1rt.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-BRiQUjjw.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-CKN_EzVf.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-BCDHwpwQ.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-DK9ovhaB.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BILPGakp.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DTVCOr9-.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-Hzc1iow4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-GXfgnGYV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-B-3TJWsS.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-DwSJMWtK.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-D2uxf-DA.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-DG6kbKK0.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-f5p49EDU.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-FUgHyzSD.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-DCHGee7e.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer-Dm6LlIoa.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-BawA7qAg.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-D6a5wZU1.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-K8WavwnA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-4JAJYaqx.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-BDWvi8PD.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-DF_K4E9i.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-BYeZlesI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-BIHlgnIc.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Cvf-6B5P.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.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-BGs47s1a.js → Providers-CxC7JuYv.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-VLf3gAfL.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-B5vGjRiF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend--UW4iYs-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-DMSPcfKR.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-kQXlEHmj.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-CxGu3pit.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-Bi5lo8MZ.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-Cq_H5RTh.js} +1 -1
- package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-YFbmkaQS.js} +3 -3
- package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DLOGdaLm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CteIxb3D.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-CccopaIZ.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-AzfEQKX1.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-ekGo5l77.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-BmZmAgYH.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-D7q3Z4uL.js +1 -0
- package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-CX1Tgeka.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Zc59jF4i.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-CXOD7y83.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-B1aBHPBo.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-zALlOq_U.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-CQfJMXaq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-BU0RcM-w.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-IFZ3HsbH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-BzslgW0v.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-BZAcQNS6.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-CBK1DMi0.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-BlMTPzO-.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-B51-8QVn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-504RTByU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-C-GP6IQM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-DOkIj7ID.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-DN7V-_FW.js} +1 -1
- package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index--n-ZciV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-7jIqHbzw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-B2-x9bS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-BFIFJ5Dr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-B_vGU-Js.js} +1 -1
- package/src/assets/web-panel/assets/{index--rq90rL9.js → index-Bkxhaeta.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-By6u5IV1.js} +1 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +1 -0
- package/src/assets/web-panel/assets/{index-caye7AOE.js → index-C0k_Nqg9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-C3TSohzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-C4DV_5uA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-C8UltbXK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-C9_48OzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CKTfZyfU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-CP2A226L.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-CUhQpiYv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-CX78k-qf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-CYopNjcJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-Cao5DZy7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-CfiEM64n.js} +3 -3
- package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-ConjaJro.js} +1 -1
- package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-CqcLo5WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-CySTJKeq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-D1phyca8.js} +1 -1
- package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-D9LyiN-L.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-DA2f1UV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-DAOpQvl_.js} +1 -1
- package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DE-hmI2l.js} +1 -1
- package/src/assets/web-panel/assets/{index-bKm3TUDp.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-7TC3S8SN.js → index-DaA__-q1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-FcV8J_Qz.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-P2mfY27Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-et5JIPHO.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-f4Bo4KXm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-kAqQXfP6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-kt_5rfcZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-so8eIIxF.js} +1 -1
- package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-yYTQZypF.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-CPYEIdpV.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-Cjim9pxs.js} +1 -1
- package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-DuU-cgz3.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-DJHNAkta.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-DSQBgdBj.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-CTmT_j5m.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-Lfm6v-r4.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-CgrcPcDi.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CuE3110i.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-DiK5nbpf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-DPEJdQaZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-CnnodGmp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DWF84Yav.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-a5b5Dw2f.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-DAluzXOq.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.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 +217 -30
- package/src/data/changelog.json +14 -1
- package/src/harness/mcp-client.js +242 -42
- 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 +51 -6
- package/src/lib/cowork-task-runner.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-execution-authority.js +163 -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 +179 -104
- package/src/lib/process-execution-broker/platform-sandbox.js +942 -168
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/skill-mcp.js +65 -4
- package/src/lib/web-fetch.js +4 -4
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/mcp-config.js +235 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/MarkdownRenderer-Bf3DEqP6.js +0 -1
- package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
- package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
- package/src/assets/web-panel/assets/index-hUpvk6Jl.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.
|
|
@@ -179,8 +186,8 @@ export const MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256 = crypto
|
|
|
179
186
|
.digest("hex");
|
|
180
187
|
const LINUX_NODE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-source";
|
|
181
188
|
const LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-executable";
|
|
182
|
-
const
|
|
183
|
-
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";
|
|
184
191
|
const LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE = "0400";
|
|
185
192
|
const LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE = "0500";
|
|
186
193
|
const LINUX_ELF64_HEADER_BYTES = 64;
|
|
@@ -311,6 +318,325 @@ function resolveRuntime(overrides = {}) {
|
|
|
311
318
|
return { ...DEFAULT_RUNTIME, ...overrides };
|
|
312
319
|
}
|
|
313
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
|
+
|
|
314
640
|
function normalizeSandboxRequest(profileOrRequest, explicitRequest) {
|
|
315
641
|
const legacyRequest =
|
|
316
642
|
profileOrRequest &&
|
|
@@ -788,14 +1114,42 @@ function cleanupWindowsTemporaryPath(
|
|
|
788
1114
|
targetPath,
|
|
789
1115
|
attempts = 100,
|
|
790
1116
|
delayMs = 10,
|
|
1117
|
+
expectedIdentity = null,
|
|
791
1118
|
) {
|
|
792
1119
|
if (!targetPath) return true;
|
|
793
1120
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
794
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
|
+
}
|
|
795
1138
|
runtime.fs.unlinkSync(targetPath);
|
|
1139
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
796
1140
|
return true;
|
|
797
1141
|
} catch (error) {
|
|
798
|
-
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
|
+
}
|
|
799
1153
|
}
|
|
800
1154
|
if (attempt + 1 < attempts) runtime.sleepSync(delayMs);
|
|
801
1155
|
}
|
|
@@ -806,13 +1160,32 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
806
1160
|
runtime,
|
|
807
1161
|
targetPath,
|
|
808
1162
|
attempts = 100,
|
|
1163
|
+
entry = null,
|
|
1164
|
+
expectedIdentity = null,
|
|
809
1165
|
) {
|
|
810
|
-
if (
|
|
1166
|
+
if (
|
|
1167
|
+
cleanupWindowsTemporaryPath(
|
|
1168
|
+
runtime,
|
|
1169
|
+
targetPath,
|
|
1170
|
+
attempts,
|
|
1171
|
+
10,
|
|
1172
|
+
expectedIdentity,
|
|
1173
|
+
)
|
|
1174
|
+
) {
|
|
1175
|
+
const residualEntries = new Set();
|
|
811
1176
|
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
812
|
-
if (
|
|
1177
|
+
if (
|
|
1178
|
+
residual.kind === "file" &&
|
|
1179
|
+
residual.runtime === runtime &&
|
|
1180
|
+
residual.targetPath === targetPath
|
|
1181
|
+
) {
|
|
813
1182
|
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1183
|
+
if (residual.entry) residualEntries.add(residual.entry);
|
|
814
1184
|
}
|
|
815
1185
|
}
|
|
1186
|
+
for (const residualEntry of residualEntries) {
|
|
1187
|
+
finalizeWindowsTemporaryCleanupEntry(residualEntry);
|
|
1188
|
+
}
|
|
816
1189
|
if (
|
|
817
1190
|
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
818
1191
|
windowsTemporaryCleanupRetryTimer
|
|
@@ -822,13 +1195,25 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
822
1195
|
}
|
|
823
1196
|
return true;
|
|
824
1197
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
)
|
|
831
|
-
|
|
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
|
+
});
|
|
832
1217
|
}
|
|
833
1218
|
registerWindowsAdapterExitCleanup();
|
|
834
1219
|
scheduleWindowsTemporaryCleanupRetry();
|
|
@@ -838,14 +1223,25 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
838
1223
|
function retryWindowsTemporaryCleanupBacklog(attempts = 100) {
|
|
839
1224
|
let cleaned = true;
|
|
840
1225
|
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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) {
|
|
848
1243
|
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1244
|
+
if (residual.entry) finalizeWindowsTemporaryCleanupEntry(residual.entry);
|
|
849
1245
|
} else {
|
|
850
1246
|
cleaned = false;
|
|
851
1247
|
}
|
|
@@ -860,6 +1256,18 @@ function retryWindowsTemporaryCleanupBacklog(attempts = 100) {
|
|
|
860
1256
|
return cleaned;
|
|
861
1257
|
}
|
|
862
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
|
+
|
|
863
1271
|
function scheduleWindowsTemporaryCleanupRetry() {
|
|
864
1272
|
if (
|
|
865
1273
|
windowsTemporaryCleanupRetryTimer ||
|
|
@@ -882,20 +1290,112 @@ function cleanupWindowsTemporaryDirectory(
|
|
|
882
1290
|
targetPath,
|
|
883
1291
|
attempts = 100,
|
|
884
1292
|
delayMs = 10,
|
|
1293
|
+
expectedIdentity = null,
|
|
885
1294
|
) {
|
|
886
1295
|
if (!targetPath) return true;
|
|
887
1296
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
888
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
|
+
}
|
|
889
1314
|
runtime.fs.rmdirSync(targetPath);
|
|
1315
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
890
1316
|
return true;
|
|
891
1317
|
} catch (error) {
|
|
892
|
-
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
|
+
}
|
|
893
1329
|
}
|
|
894
1330
|
if (attempt + 1 < attempts) runtime.sleepSync(delayMs);
|
|
895
1331
|
}
|
|
896
1332
|
return false;
|
|
897
1333
|
}
|
|
898
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;
|
|
1371
|
+
}
|
|
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
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
registerWindowsAdapterExitCleanup();
|
|
1395
|
+
scheduleWindowsTemporaryCleanupRetry();
|
|
1396
|
+
return false;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
899
1399
|
function sha256(value) {
|
|
900
1400
|
return crypto.createHash("sha256").update(value).digest("hex");
|
|
901
1401
|
}
|
|
@@ -1143,15 +1643,11 @@ function sameWindowsFileIdentity(left, right) {
|
|
|
1143
1643
|
}
|
|
1144
1644
|
|
|
1145
1645
|
function inspectWindowsAdapterAssembly(runtime, assemblyPath) {
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
) {
|
|
1152
|
-
throw new Error("Windows in-memory adapter is not a regular file");
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1646
|
+
const boundBefore = inspectWindowsAdapterBoundPath(
|
|
1647
|
+
runtime,
|
|
1648
|
+
assemblyPath,
|
|
1649
|
+
"file",
|
|
1650
|
+
);
|
|
1155
1651
|
const before = runtime.fs.statSync(assemblyPath, { bigint: true });
|
|
1156
1652
|
if (typeof before.isFile === "function" && !before.isFile()) {
|
|
1157
1653
|
throw new Error("Windows in-memory adapter is not a regular file");
|
|
@@ -1164,6 +1660,15 @@ function inspectWindowsAdapterAssembly(runtime, assemblyPath) {
|
|
|
1164
1660
|
if (!sameWindowsFileIdentity(beforeIdentity, afterIdentity)) {
|
|
1165
1661
|
throw new Error("Windows in-memory adapter changed during attestation");
|
|
1166
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
|
+
}
|
|
1167
1672
|
return { assemblyDigest, fileIdentity: afterIdentity };
|
|
1168
1673
|
}
|
|
1169
1674
|
|
|
@@ -1173,6 +1678,11 @@ function verifyWindowsAdapterEntry(entry, runtime, source) {
|
|
|
1173
1678
|
entry.cleaned ||
|
|
1174
1679
|
entry.runtimeFs !== runtime.fs ||
|
|
1175
1680
|
entry.tempDirectory !== source.tempDirectory ||
|
|
1681
|
+
entry.idleTtlMs !== source.idleTtlMs ||
|
|
1682
|
+
!sameWindowsAdapterTempRootBinding(
|
|
1683
|
+
entry.tempRootBinding,
|
|
1684
|
+
source.tempRootBinding,
|
|
1685
|
+
) ||
|
|
1176
1686
|
entry.sourceDigest !== source.sourceDigest ||
|
|
1177
1687
|
entry.sourceContractDigest !== source.sourceContractDigest ||
|
|
1178
1688
|
entry.loaderMode !== source.loaderMode
|
|
@@ -1180,6 +1690,15 @@ function verifyWindowsAdapterEntry(entry, runtime, source) {
|
|
|
1180
1690
|
return false;
|
|
1181
1691
|
}
|
|
1182
1692
|
try {
|
|
1693
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1694
|
+
if (entry.adapterDirectory) {
|
|
1695
|
+
assertWindowsAdapterBoundPathIdentity(
|
|
1696
|
+
runtime,
|
|
1697
|
+
entry.adapterDirectory,
|
|
1698
|
+
"directory",
|
|
1699
|
+
entry.adapterDirectoryIdentity,
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1183
1702
|
const attestation = inspectWindowsAdapterAssembly(
|
|
1184
1703
|
runtime,
|
|
1185
1704
|
entry.assemblyPath,
|
|
@@ -1203,14 +1722,18 @@ function cleanupWindowsAdapterEntry(entry, attempts = 100) {
|
|
|
1203
1722
|
entry.runtime,
|
|
1204
1723
|
entry.assemblyPath,
|
|
1205
1724
|
attempts,
|
|
1725
|
+
entry,
|
|
1726
|
+
entry.fileIdentity,
|
|
1206
1727
|
);
|
|
1207
|
-
const directoryDeleted =
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1728
|
+
const directoryDeleted = entry.adapterDirectory
|
|
1729
|
+
? cleanupOrTrackWindowsTemporaryDirectory(
|
|
1730
|
+
entry.runtime,
|
|
1731
|
+
entry.adapterDirectory,
|
|
1732
|
+
attempts,
|
|
1733
|
+
entry,
|
|
1734
|
+
entry.adapterDirectoryIdentity,
|
|
1735
|
+
)
|
|
1736
|
+
: true;
|
|
1214
1737
|
if (deleted && directoryDeleted) {
|
|
1215
1738
|
entry.cleaned = true;
|
|
1216
1739
|
windowsAdapterCacheEntries.delete(entry);
|
|
@@ -1253,15 +1776,35 @@ function registerWindowsAdapterExitCleanup() {
|
|
|
1253
1776
|
* this hook at a known quiescent point to prove cleanup.
|
|
1254
1777
|
*/
|
|
1255
1778
|
export function resetWindowsSandboxAdapterCache() {
|
|
1256
|
-
|
|
1779
|
+
const appContainerCleaned = retryWindowsAppContainerCleanupBacklog();
|
|
1257
1780
|
windowsAdapterCache = null;
|
|
1258
1781
|
for (const entry of [...windowsAdapterCacheEntries]) {
|
|
1259
1782
|
entry.retired = true;
|
|
1260
1783
|
entry.refCount = 0;
|
|
1261
|
-
|
|
1784
|
+
cleanupWindowsAdapterEntry(entry);
|
|
1262
1785
|
}
|
|
1263
|
-
|
|
1264
|
-
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;
|
|
1265
1808
|
}
|
|
1266
1809
|
|
|
1267
1810
|
function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
@@ -1273,11 +1816,11 @@ function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
|
1273
1816
|
) {
|
|
1274
1817
|
return;
|
|
1275
1818
|
}
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1819
|
+
const idleTtlMs = entry.idleTtlMs;
|
|
1820
|
+
if (idleTtlMs === 0) {
|
|
1821
|
+
retireWindowsAdapterEntry(entry);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1281
1824
|
entry.idleTimer = setTimeout(() => {
|
|
1282
1825
|
entry.idleTimer = null;
|
|
1283
1826
|
if (
|
|
@@ -1293,6 +1836,7 @@ function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
|
1293
1836
|
}
|
|
1294
1837
|
|
|
1295
1838
|
function loadWindowsAdapterSource(runtime, loaderMode) {
|
|
1839
|
+
const tempRootBinding = assertWindowsAdapterTempRoot(runtime);
|
|
1296
1840
|
const bundled = runtime.windowsAdapterContent === undefined;
|
|
1297
1841
|
const content = bundled
|
|
1298
1842
|
? loaderMode === "managed-executable"
|
|
@@ -1307,7 +1851,10 @@ function loadWindowsAdapterSource(runtime, loaderMode) {
|
|
|
1307
1851
|
: WINDOWS_SANDBOX_HELPER_ASSEMBLY_DIGEST
|
|
1308
1852
|
: sha256(content),
|
|
1309
1853
|
sourceContractDigest: bundled ? WINDOWS_SANDBOX_HELPER_SOURCE_DIGEST : null,
|
|
1310
|
-
tempDirectory:
|
|
1854
|
+
tempDirectory: tempRootBinding.rootPath,
|
|
1855
|
+
tempRootBinding,
|
|
1856
|
+
tempRootAttestation: tempRootBinding.attestation,
|
|
1857
|
+
idleTtlMs: runtime.windowsAdapterIdleTtlMs,
|
|
1311
1858
|
loaderMode,
|
|
1312
1859
|
};
|
|
1313
1860
|
}
|
|
@@ -1329,54 +1876,121 @@ function materializeWindowsAdapter(runtime, source) {
|
|
|
1329
1876
|
const assemblyPath = runtime.joinPath(
|
|
1330
1877
|
adapterDirectory || source.tempDirectory,
|
|
1331
1878
|
source.loaderMode === "managed-executable"
|
|
1332
|
-
?
|
|
1879
|
+
? WINDOWS_ADAPTER_HELPER_NAME
|
|
1333
1880
|
: `${adapterBaseName}.dll`,
|
|
1334
1881
|
);
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
runtime.fs.existsSync(assemblyPath)
|
|
1338
|
-
) {
|
|
1339
|
-
return { reason: "windows_native_adapter_random_path_collision" };
|
|
1340
|
-
}
|
|
1882
|
+
let adapterDirectoryIdentity = null;
|
|
1883
|
+
let assemblyFileIdentity = null;
|
|
1341
1884
|
try {
|
|
1885
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1342
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
|
+
}
|
|
1343
1894
|
runtime.fs.mkdirSync(adapterDirectory, {
|
|
1344
1895
|
mode: 0o700,
|
|
1345
1896
|
recursive: false,
|
|
1346
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;
|
|
1347
1914
|
}
|
|
1348
1915
|
runtime.fs.writeFileSync(assemblyPath, source.content, {
|
|
1349
1916
|
mode: adapterDirectory ? 0o500 : 0o600,
|
|
1350
1917
|
flag: "wx",
|
|
1351
1918
|
});
|
|
1919
|
+
assemblyFileIdentity = inspectWindowsAdapterAssembly(
|
|
1920
|
+
runtime,
|
|
1921
|
+
assemblyPath,
|
|
1922
|
+
).fileIdentity;
|
|
1352
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
|
+
}
|
|
1353
1951
|
const assemblyDeleted = cleanupOrTrackWindowsTemporaryPath(
|
|
1354
1952
|
runtime,
|
|
1355
1953
|
assemblyPath,
|
|
1954
|
+
100,
|
|
1955
|
+
null,
|
|
1956
|
+
assemblyFileIdentity,
|
|
1356
1957
|
);
|
|
1357
|
-
const directoryDeleted =
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|
+
}
|
|
1360
1968
|
if (!assemblyDeleted || !directoryDeleted) {
|
|
1361
1969
|
return { reason: "windows_native_adapter_compile_cleanup_unverified" };
|
|
1362
1970
|
}
|
|
1363
|
-
if (error?.code === "EEXIST") {
|
|
1364
|
-
return { reason: "windows_native_adapter_random_path_collision" };
|
|
1365
|
-
}
|
|
1366
1971
|
return { reason: "windows_native_adapter_materialize_failed" };
|
|
1367
1972
|
}
|
|
1368
1973
|
return {
|
|
1369
1974
|
adapterDirectory,
|
|
1975
|
+
adapterDirectoryIdentity,
|
|
1370
1976
|
assemblyPath,
|
|
1977
|
+
assemblyFileIdentity,
|
|
1371
1978
|
cleanupAssembly: () => {
|
|
1372
1979
|
const assemblyDeleted = cleanupOrTrackWindowsTemporaryPath(
|
|
1373
1980
|
runtime,
|
|
1374
1981
|
assemblyPath,
|
|
1982
|
+
100,
|
|
1983
|
+
null,
|
|
1984
|
+
assemblyFileIdentity,
|
|
1375
1985
|
);
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1986
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1987
|
+
runtime,
|
|
1988
|
+
adapterDirectory,
|
|
1989
|
+
100,
|
|
1990
|
+
null,
|
|
1991
|
+
adapterDirectoryIdentity,
|
|
1379
1992
|
);
|
|
1993
|
+
return assemblyDeleted && directoryDeleted;
|
|
1380
1994
|
},
|
|
1381
1995
|
};
|
|
1382
1996
|
}
|
|
@@ -1388,9 +2002,7 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1388
2002
|
.randomBytes(24)
|
|
1389
2003
|
.toString("hex")}.json`,
|
|
1390
2004
|
);
|
|
1391
|
-
|
|
1392
|
-
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
1393
|
-
}
|
|
2005
|
+
let fileIdentity = null;
|
|
1394
2006
|
let content;
|
|
1395
2007
|
try {
|
|
1396
2008
|
content = Buffer.from(
|
|
@@ -1410,16 +2022,57 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1410
2022
|
return { reason: "windows_adapter_invocation_too_large" };
|
|
1411
2023
|
}
|
|
1412
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
|
+
}
|
|
1413
2032
|
runtime.fs.writeFileSync(payloadPath, content, {
|
|
1414
2033
|
mode: 0o600,
|
|
1415
2034
|
flag: "wx",
|
|
1416
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;
|
|
1417
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
|
+
}
|
|
1418
2065
|
if (error?.code === "EEXIST") {
|
|
1419
2066
|
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
1420
2067
|
}
|
|
1421
2068
|
return {
|
|
1422
|
-
reason: cleanupOrTrackWindowsTemporaryPath(
|
|
2069
|
+
reason: cleanupOrTrackWindowsTemporaryPath(
|
|
2070
|
+
runtime,
|
|
2071
|
+
payloadPath,
|
|
2072
|
+
100,
|
|
2073
|
+
null,
|
|
2074
|
+
fileIdentity,
|
|
2075
|
+
)
|
|
1423
2076
|
? "windows_adapter_invocation_materialize_failed"
|
|
1424
2077
|
: "windows_adapter_invocation_cleanup_unverified",
|
|
1425
2078
|
};
|
|
@@ -1427,7 +2080,24 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1427
2080
|
return {
|
|
1428
2081
|
payloadPath,
|
|
1429
2082
|
payloadDigest: sha256(content),
|
|
1430
|
-
|
|
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
|
+
),
|
|
1431
2101
|
};
|
|
1432
2102
|
}
|
|
1433
2103
|
|
|
@@ -1483,6 +2153,7 @@ function buildWindowsAdapterInvocation(entry, invocation) {
|
|
|
1483
2153
|
invocation.payloadPath,
|
|
1484
2154
|
invocation.payloadDigest,
|
|
1485
2155
|
],
|
|
2156
|
+
verify: invocation.verify,
|
|
1486
2157
|
cleanup: invocation.cleanup,
|
|
1487
2158
|
};
|
|
1488
2159
|
}
|
|
@@ -1497,6 +2168,7 @@ function buildWindowsAdapterInvocation(entry, invocation) {
|
|
|
1497
2168
|
"-EncodedCommand",
|
|
1498
2169
|
windowsPowerShellBootstrap(entry, invocation),
|
|
1499
2170
|
],
|
|
2171
|
+
verify: invocation.verify,
|
|
1500
2172
|
cleanup: invocation.cleanup,
|
|
1501
2173
|
};
|
|
1502
2174
|
}
|
|
@@ -1583,11 +2255,14 @@ function createFreshWindowsAdapter(runtime, source, hostEnvironment) {
|
|
|
1583
2255
|
runtime,
|
|
1584
2256
|
runtimeFs: runtime.fs,
|
|
1585
2257
|
tempDirectory: source.tempDirectory,
|
|
2258
|
+
tempRootBinding: source.tempRootBinding,
|
|
2259
|
+
idleTtlMs: source.idleTtlMs,
|
|
1586
2260
|
sourceDigest: source.sourceDigest,
|
|
1587
2261
|
sourceContractDigest: source.sourceContractDigest,
|
|
1588
2262
|
loaderMode: source.loaderMode,
|
|
1589
2263
|
powershellPath: powershellHosts[0] || null,
|
|
1590
2264
|
adapterDirectory: materialized.adapterDirectory,
|
|
2265
|
+
adapterDirectoryIdentity: materialized.adapterDirectoryIdentity,
|
|
1591
2266
|
assemblyPath: materialized.assemblyPath,
|
|
1592
2267
|
assemblyDigest: attestation.assemblyDigest,
|
|
1593
2268
|
fileIdentity: attestation.fileIdentity,
|
|
@@ -1743,7 +2418,9 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1743
2418
|
error.adapterReason = payload.reason;
|
|
1744
2419
|
throw error;
|
|
1745
2420
|
}
|
|
1746
|
-
|
|
2421
|
+
const invocation = buildWindowsAdapterInvocation(entry, payload);
|
|
2422
|
+
invocation.verify();
|
|
2423
|
+
return invocation;
|
|
1747
2424
|
};
|
|
1748
2425
|
|
|
1749
2426
|
return {
|
|
@@ -1754,6 +2431,7 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1754
2431
|
let result;
|
|
1755
2432
|
let failure;
|
|
1756
2433
|
try {
|
|
2434
|
+
invocation.verify();
|
|
1757
2435
|
result = runtime.spawnSync(invocation.command, invocation.args, {
|
|
1758
2436
|
...options,
|
|
1759
2437
|
cwd: runtime.joinPath(runtime.windowsDir(), "System32"),
|
|
@@ -1781,20 +2459,40 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1781
2459
|
};
|
|
1782
2460
|
}
|
|
1783
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
|
+
|
|
1784
2477
|
function waitForWindowsTargetIdentity(
|
|
1785
2478
|
proc,
|
|
1786
2479
|
identityPath,
|
|
1787
2480
|
runtime,
|
|
1788
2481
|
expectedAppContainer = null,
|
|
1789
2482
|
timeoutMs = 30_000,
|
|
2483
|
+
onIdentityFileAttested = null,
|
|
1790
2484
|
) {
|
|
1791
2485
|
const deadline = runtime.now() + timeoutMs;
|
|
1792
2486
|
let lastError;
|
|
1793
2487
|
while (runtime.now() < deadline) {
|
|
1794
2488
|
try {
|
|
1795
|
-
const
|
|
1796
|
-
runtime
|
|
2489
|
+
const identityFile = readWindowsAdapterBoundFile(
|
|
2490
|
+
runtime,
|
|
2491
|
+
identityPath,
|
|
2492
|
+
"utf8",
|
|
1797
2493
|
);
|
|
2494
|
+
onIdentityFileAttested?.(identityFile.fileIdentity);
|
|
2495
|
+
const identity = JSON.parse(identityFile.content);
|
|
1798
2496
|
if (identity?.error) {
|
|
1799
2497
|
throw new Error(
|
|
1800
2498
|
`Windows sandbox helper rejected the target: ${identity.error}`,
|
|
@@ -2610,11 +3308,33 @@ export function applyWindowsSandbox(
|
|
|
2610
3308
|
) {
|
|
2611
3309
|
return unavailablePlan("windows_git_nested_process_compatibility");
|
|
2612
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
|
+
};
|
|
2613
3326
|
const loaderMode =
|
|
2614
3327
|
profile !== "strict" && !requiresAppContainer && !entrySnapshot.locks
|
|
2615
3328
|
? "managed-executable"
|
|
2616
3329
|
: "powershell-byte-assembly";
|
|
2617
|
-
|
|
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
|
+
}
|
|
2618
3338
|
if (!adapterSource.content) {
|
|
2619
3339
|
return unavailablePlan(adapterSource.reason);
|
|
2620
3340
|
}
|
|
@@ -2659,6 +3379,22 @@ export function applyWindowsSandbox(
|
|
|
2659
3379
|
.toString("hex")}.json`,
|
|
2660
3380
|
)
|
|
2661
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
|
+
}
|
|
2662
3398
|
const launchSpec = {
|
|
2663
3399
|
cpuSeconds: Number(limits.cpu || 0),
|
|
2664
3400
|
processMemoryBytes: Number(
|
|
@@ -2699,7 +3435,13 @@ export function applyWindowsSandbox(
|
|
|
2699
3435
|
if (identityPath) launchSpec.identityPath = identityPath;
|
|
2700
3436
|
const cleanupIdentity = () => {
|
|
2701
3437
|
if (!identityPath) return true;
|
|
2702
|
-
return cleanupOrTrackWindowsTemporaryPath(
|
|
3438
|
+
return cleanupOrTrackWindowsTemporaryPath(
|
|
3439
|
+
runtime,
|
|
3440
|
+
identityPath,
|
|
3441
|
+
100,
|
|
3442
|
+
null,
|
|
3443
|
+
identityFileIdentity,
|
|
3444
|
+
);
|
|
2703
3445
|
};
|
|
2704
3446
|
const appContainerProfileName = requiresAppContainer
|
|
2705
3447
|
? `ChainlessChain.CliSandbox.${runtime.randomBytes(12).toString("hex")}`
|
|
@@ -2973,6 +3715,10 @@ export function applyWindowsSandbox(
|
|
|
2973
3715
|
identityPath,
|
|
2974
3716
|
runtime,
|
|
2975
3717
|
appContainer,
|
|
3718
|
+
30_000,
|
|
3719
|
+
(fileIdentity) => {
|
|
3720
|
+
identityFileIdentity = fileIdentity;
|
|
3721
|
+
},
|
|
2976
3722
|
);
|
|
2977
3723
|
if (!cleanupIdentity()) {
|
|
2978
3724
|
throw new Error(
|
|
@@ -3064,6 +3810,7 @@ export function applyWindowsSandbox(
|
|
|
3064
3810
|
enforcement: backend,
|
|
3065
3811
|
policyAttested: requiresAppContainer ? true : null,
|
|
3066
3812
|
policyDigest: appContainerPolicyDigest,
|
|
3813
|
+
adapterTempRootAttestation: adapterSource.tempRootAttestation,
|
|
3067
3814
|
runtimeProbe: entrySnapshotRuntimeProbe,
|
|
3068
3815
|
guarantees: [
|
|
3069
3816
|
...(requiresAppContainer
|
|
@@ -3143,13 +3890,12 @@ function linuxBubblewrapProbe(
|
|
|
3143
3890
|
const supervisorDescriptorBound =
|
|
3144
3891
|
supervisorBinding?.mechanism === LINUX_BWRAP_SUPERVISOR_BINDING_MECHANISM;
|
|
3145
3892
|
const pluginTreeContentSnapshot =
|
|
3146
|
-
!native &&
|
|
3147
3893
|
attempted &&
|
|
3148
3894
|
snapshot &&
|
|
3149
3895
|
runnable &&
|
|
3150
3896
|
reason === null &&
|
|
3151
3897
|
supervisorDescriptorBound &&
|
|
3152
|
-
pluginTreeSnapshot?.scope ===
|
|
3898
|
+
pluginTreeSnapshot?.scope === LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE &&
|
|
3153
3899
|
pluginTreeSnapshot?.mechanism === LINUX_ENTRY_SNAPSHOT_MECHANISM &&
|
|
3154
3900
|
Number.isSafeInteger(pluginTreeSnapshot?.files) &&
|
|
3155
3901
|
pluginTreeSnapshot.files > 0 &&
|
|
@@ -3160,7 +3906,7 @@ function linuxBubblewrapProbe(
|
|
|
3160
3906
|
typeof pluginTreeSnapshot?.digest === "string" &&
|
|
3161
3907
|
/^[a-f0-9]{64}$/.test(pluginTreeSnapshot.digest) &&
|
|
3162
3908
|
pluginTreeSnapshot?.consistency ===
|
|
3163
|
-
|
|
3909
|
+
LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY &&
|
|
3164
3910
|
pluginTreeSnapshot?.contractBound === false &&
|
|
3165
3911
|
pluginTreeSnapshot?.atomic === false;
|
|
3166
3912
|
const initialDynamicLoadClosureDescriptorBound =
|
|
@@ -3775,16 +4521,20 @@ function linuxEntrySnapshotContract(targetRuntime) {
|
|
|
3775
4521
|
throw new Error("entry_snapshot_runtime_unsupported");
|
|
3776
4522
|
}
|
|
3777
4523
|
|
|
3778
|
-
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";
|
|
3779
4529
|
if (!Number.isSafeInteger(sourceMode) || sourceMode < 0) {
|
|
3780
|
-
throw new Error(
|
|
4530
|
+
throw new Error(`${errorPrefix}_source_mode_invalid`);
|
|
3781
4531
|
}
|
|
3782
4532
|
return {
|
|
3783
|
-
scope:
|
|
4533
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
3784
4534
|
targetMode: (sourceMode & 0o111) !== 0 ? "0500" : "0400",
|
|
3785
4535
|
sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
|
|
3786
4536
|
minimumBytes: 0,
|
|
3787
|
-
errorPrefix
|
|
4537
|
+
errorPrefix,
|
|
3788
4538
|
};
|
|
3789
4539
|
}
|
|
3790
4540
|
|
|
@@ -3865,9 +4615,9 @@ function createLinuxRegularFileSnapshot(
|
|
|
3865
4615
|
throw snapshotError("source_changed");
|
|
3866
4616
|
}
|
|
3867
4617
|
if (
|
|
3868
|
-
snapshotContract.scope ===
|
|
4618
|
+
snapshotContract.scope === LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE &&
|
|
3869
4619
|
snapshotContract.targetMode !==
|
|
3870
|
-
|
|
4620
|
+
((sourceFileMode & 0o111) !== 0 ? "0500" : "0400")
|
|
3871
4621
|
) {
|
|
3872
4622
|
throw snapshotError("source_mode_changed");
|
|
3873
4623
|
}
|
|
@@ -4328,7 +5078,21 @@ function applyLinuxMcpCapsuleCodeSnapshot(
|
|
|
4328
5078
|
});
|
|
4329
5079
|
}
|
|
4330
5080
|
|
|
4331
|
-
|
|
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";
|
|
4332
5096
|
const members = [];
|
|
4333
5097
|
const destinations = new Set();
|
|
4334
5098
|
for (const mount of mounts || []) {
|
|
@@ -4337,18 +5101,23 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
4337
5101
|
path.posix.normalize(mount.destination) !== mount.destination ||
|
|
4338
5102
|
destinations.has(mount.destination)
|
|
4339
5103
|
) {
|
|
4340
|
-
throw new Error(
|
|
5104
|
+
throw new Error(`${errorPrefix}_destination_invalid`);
|
|
4341
5105
|
}
|
|
4342
5106
|
destinations.add(mount.destination);
|
|
4343
5107
|
const snapshot = mount.contentSnapshot;
|
|
4344
5108
|
const entry = mount.destination === entryDestination;
|
|
4345
5109
|
const expectedScope = entry
|
|
4346
|
-
?
|
|
4347
|
-
|
|
5110
|
+
? native
|
|
5111
|
+
? LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE
|
|
5112
|
+
: LINUX_NODE_ENTRY_SNAPSHOT_SCOPE
|
|
5113
|
+
: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE;
|
|
4348
5114
|
const sourceMode = Number.parseInt(snapshot?.sourceFileMode, 8);
|
|
4349
5115
|
const expectedTargetMode = entry
|
|
4350
|
-
?
|
|
4351
|
-
|
|
5116
|
+
? native
|
|
5117
|
+
? LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE
|
|
5118
|
+
: LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE
|
|
5119
|
+
: linuxPluginTreeFileSnapshotContract(sourceMode, targetRuntime)
|
|
5120
|
+
.targetMode;
|
|
4352
5121
|
if (
|
|
4353
5122
|
snapshot?.scope !== expectedScope ||
|
|
4354
5123
|
snapshot?.mechanism !== LINUX_ENTRY_SNAPSHOT_MECHANISM ||
|
|
@@ -4371,7 +5140,7 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
4371
5140
|
mount.mountMode !== "ro-bind-data" ||
|
|
4372
5141
|
mount.mountPermissions !== expectedTargetMode
|
|
4373
5142
|
) {
|
|
4374
|
-
throw new Error(
|
|
5143
|
+
throw new Error(`${errorPrefix}_member_invalid`);
|
|
4375
5144
|
}
|
|
4376
5145
|
members.push({
|
|
4377
5146
|
destination: mount.destination,
|
|
@@ -4398,58 +5167,66 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
4398
5167
|
members.length > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES ||
|
|
4399
5168
|
!members.some((member) => member.destination === entryDestination)
|
|
4400
5169
|
) {
|
|
4401
|
-
throw new Error(
|
|
5170
|
+
throw new Error(`${errorPrefix}_file_count_invalid`);
|
|
4402
5171
|
}
|
|
4403
5172
|
const bytes = members.reduce((total, member) => total + member.bytes, 0);
|
|
4404
5173
|
if (
|
|
4405
5174
|
!Number.isSafeInteger(bytes) ||
|
|
4406
5175
|
bytes > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES
|
|
4407
5176
|
) {
|
|
4408
|
-
throw new Error(
|
|
5177
|
+
throw new Error(`${errorPrefix}_bytes_exceeded`);
|
|
4409
5178
|
}
|
|
4410
5179
|
const digest = crypto
|
|
4411
5180
|
.createHash("sha256")
|
|
4412
5181
|
.update(
|
|
4413
5182
|
JSON.stringify({
|
|
4414
5183
|
version: 1,
|
|
4415
|
-
scope:
|
|
4416
|
-
|
|
5184
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
5185
|
+
...(native ? { targetRuntime } : {}),
|
|
5186
|
+
consistency: LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY,
|
|
4417
5187
|
members,
|
|
4418
5188
|
}),
|
|
4419
5189
|
)
|
|
4420
5190
|
.digest("hex");
|
|
4421
5191
|
return Object.freeze({
|
|
4422
|
-
scope:
|
|
5192
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
4423
5193
|
mechanism: LINUX_ENTRY_SNAPSHOT_MECHANISM,
|
|
4424
5194
|
files: members.length,
|
|
4425
5195
|
bytes,
|
|
4426
5196
|
digest,
|
|
4427
|
-
consistency:
|
|
5197
|
+
consistency: LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY,
|
|
4428
5198
|
contractBound: false,
|
|
4429
5199
|
atomic: false,
|
|
4430
5200
|
});
|
|
4431
5201
|
}
|
|
4432
5202
|
|
|
4433
|
-
function
|
|
5203
|
+
function attestLinuxPluginTreeSnapshot(
|
|
4434
5204
|
runtime,
|
|
4435
5205
|
mounts,
|
|
4436
5206
|
entryDestination,
|
|
5207
|
+
targetRuntime,
|
|
4437
5208
|
expected,
|
|
4438
5209
|
) {
|
|
5210
|
+
const native = targetRuntime?.startsWith("native-");
|
|
5211
|
+
const errorPrefix = native
|
|
5212
|
+
? "native_plugin_tree_snapshot"
|
|
5213
|
+
: "node_plugin_tree_snapshot";
|
|
4439
5214
|
for (const mount of mounts || []) {
|
|
4440
5215
|
if (!linuxPathWithin("/opt/chainless/plugin", mount?.destination)) continue;
|
|
4441
5216
|
const snapshot = mount.contentSnapshot;
|
|
4442
5217
|
const contract =
|
|
4443
5218
|
mount.destination === entryDestination
|
|
4444
|
-
? linuxEntrySnapshotContract(
|
|
4445
|
-
:
|
|
5219
|
+
? linuxEntrySnapshotContract(targetRuntime)
|
|
5220
|
+
: linuxPluginTreeFileSnapshotContract(
|
|
4446
5221
|
Number.parseInt(snapshot?.sourceFileMode, 8),
|
|
5222
|
+
targetRuntime,
|
|
4447
5223
|
);
|
|
4448
5224
|
attestLinuxRegularFileSnapshot(runtime, mount, snapshot, contract);
|
|
4449
5225
|
}
|
|
4450
|
-
const actual =
|
|
5226
|
+
const actual = buildLinuxPluginTreeSnapshotAttestation(
|
|
4451
5227
|
mounts,
|
|
4452
5228
|
entryDestination,
|
|
5229
|
+
targetRuntime,
|
|
4453
5230
|
);
|
|
4454
5231
|
for (const field of [
|
|
4455
5232
|
"scope",
|
|
@@ -4462,7 +5239,7 @@ function attestLinuxNodePluginTreeSnapshot(
|
|
|
4462
5239
|
"atomic",
|
|
4463
5240
|
]) {
|
|
4464
5241
|
if (actual[field] !== expected?.[field]) {
|
|
4465
|
-
throw new Error(
|
|
5242
|
+
throw new Error(`${errorPrefix}_identity_changed`);
|
|
4466
5243
|
}
|
|
4467
5244
|
}
|
|
4468
5245
|
}
|
|
@@ -6360,30 +7137,33 @@ export function applyLinuxSandbox(
|
|
|
6360
7137
|
const rawPluginMounts = pluginMountIndexes.map(
|
|
6361
7138
|
(index) => pinnedMounts[index],
|
|
6362
7139
|
);
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
) {
|
|
6377
|
-
throw new Error("node_plugin_tree_snapshot_member_size_invalid");
|
|
6378
|
-
}
|
|
6379
|
-
return total + bytes;
|
|
6380
|
-
}, 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;
|
|
6381
7153
|
if (
|
|
6382
|
-
!Number.isSafeInteger(
|
|
6383
|
-
|
|
7154
|
+
!Number.isSafeInteger(bytes) ||
|
|
7155
|
+
bytes < 0 ||
|
|
7156
|
+
bytes > LINUX_ATTESTED_FILE_MAX_BYTES
|
|
6384
7157
|
) {
|
|
6385
|
-
throw new Error(
|
|
7158
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_member_size_invalid`);
|
|
6386
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`);
|
|
6387
7167
|
}
|
|
6388
7168
|
const rawEntryMount = entryMount;
|
|
6389
7169
|
const snapshot = createLinuxEntrySnapshot(
|
|
@@ -6397,37 +7177,36 @@ export function applyLinuxSandbox(
|
|
|
6397
7177
|
finalMounts[entryIndex] = snapshot.finalMount;
|
|
6398
7178
|
probeMounts[entryIndex] = snapshot.probeMount;
|
|
6399
7179
|
entrySnapshot = snapshot;
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
|
|
6404
|
-
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
finalMounts[index] = fileSnapshot.finalMount;
|
|
6417
|
-
probeMounts[index] = fileSnapshot.probeMount;
|
|
6418
|
-
}
|
|
6419
|
-
pluginTreeSnapshot = buildLinuxNodePluginTreeSnapshotAttestation(
|
|
6420
|
-
finalMounts,
|
|
6421
|
-
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
|
+
),
|
|
6422
7196
|
);
|
|
7197
|
+
pluginTreeFileSnapshots.push(fileSnapshot);
|
|
7198
|
+
finalMounts[index] = fileSnapshot.finalMount;
|
|
7199
|
+
probeMounts[index] = fileSnapshot.probeMount;
|
|
6423
7200
|
}
|
|
7201
|
+
pluginTreeSnapshot = buildLinuxPluginTreeSnapshotAttestation(
|
|
7202
|
+
finalMounts,
|
|
7203
|
+
sandboxEntry,
|
|
7204
|
+
validation.entryRuntime,
|
|
7205
|
+
);
|
|
6424
7206
|
pinnedMounts = finalMounts;
|
|
6425
7207
|
probePinnedMounts = probeMounts;
|
|
6426
7208
|
entryMount = entrySnapshot.finalMount;
|
|
6427
|
-
closeLinuxPinnedMounts(
|
|
6428
|
-
runtime,
|
|
6429
|
-
validation.entryRuntime === "node" ? rawPluginMounts : [rawEntryMount],
|
|
6430
|
-
);
|
|
7209
|
+
closeLinuxPinnedMounts(runtime, rawPluginMounts);
|
|
6431
7210
|
} catch (error) {
|
|
6432
7211
|
closeLinuxPinnedMounts(runtime, pinnedMounts);
|
|
6433
7212
|
closeLinuxPinnedMounts(runtime, [
|
|
@@ -6607,7 +7386,7 @@ export function applyLinuxSandbox(
|
|
|
6607
7386
|
.createHash("sha256")
|
|
6608
7387
|
.update(
|
|
6609
7388
|
JSON.stringify({
|
|
6610
|
-
version:
|
|
7389
|
+
version: 4,
|
|
6611
7390
|
backend: LINUX_BWRAP_BACKEND,
|
|
6612
7391
|
supervisor: supervisorPin.attestation,
|
|
6613
7392
|
contract: {
|
|
@@ -6651,9 +7430,7 @@ export function applyLinuxSandbox(
|
|
|
6651
7430
|
...(nativeDynamicClosure
|
|
6652
7431
|
? { initialDynamicLoadClosure: nativeDynamicClosure }
|
|
6653
7432
|
: {}),
|
|
6654
|
-
|
|
6655
|
-
? { pluginTreeContentSnapshot: pluginTreeSnapshot }
|
|
6656
|
-
: {}),
|
|
7433
|
+
pluginTreeContentSnapshot: pluginTreeSnapshot,
|
|
6657
7434
|
seccomp: {
|
|
6658
7435
|
arch: seccompFilter.arch,
|
|
6659
7436
|
policy: seccompFilter.policy,
|
|
@@ -6726,21 +7503,13 @@ export function applyLinuxSandbox(
|
|
|
6726
7503
|
});
|
|
6727
7504
|
}
|
|
6728
7505
|
try {
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
} else {
|
|
6737
|
-
attestLinuxEntrySnapshot(
|
|
6738
|
-
runtime,
|
|
6739
|
-
entryMount,
|
|
6740
|
-
entrySnapshot.attestation,
|
|
6741
|
-
validation.entryRuntime,
|
|
6742
|
-
);
|
|
6743
|
-
}
|
|
7506
|
+
attestLinuxPluginTreeSnapshot(
|
|
7507
|
+
runtime,
|
|
7508
|
+
pinnedMounts,
|
|
7509
|
+
sandboxEntry,
|
|
7510
|
+
validation.entryRuntime,
|
|
7511
|
+
pluginTreeSnapshot,
|
|
7512
|
+
);
|
|
6744
7513
|
} catch (error) {
|
|
6745
7514
|
closeStrongLinuxResources(supervisorPin, finalLaunch);
|
|
6746
7515
|
return createSandboxPlan({
|
|
@@ -6961,12 +7730,17 @@ export function applySandbox(
|
|
|
6961
7730
|
explicitRequest,
|
|
6962
7731
|
);
|
|
6963
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();
|
|
6964
7738
|
const profiles = {
|
|
6965
7739
|
default: {
|
|
6966
7740
|
allowNetwork: false,
|
|
6967
7741
|
allowExec: true,
|
|
6968
7742
|
allowRead: [runtime.homedir(), "/tmp"],
|
|
6969
|
-
allowWrite: [
|
|
7743
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6970
7744
|
limits: {
|
|
6971
7745
|
// RLIMIT_NPROC is intentionally omitted. Linux applies it per real
|
|
6972
7746
|
// user ID rather than to this child tree, so it can starve unrelated
|
|
@@ -6979,7 +7753,7 @@ export function applySandbox(
|
|
|
6979
7753
|
allowNetwork: false,
|
|
6980
7754
|
allowExec: false,
|
|
6981
7755
|
allowRead: [],
|
|
6982
|
-
allowWrite: [
|
|
7756
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6983
7757
|
limits: {
|
|
6984
7758
|
cpu: 10,
|
|
6985
7759
|
as: 256 * 1024 * 1024, // 256MB address space
|
|
@@ -6990,7 +7764,7 @@ export function applySandbox(
|
|
|
6990
7764
|
allowNetwork: true,
|
|
6991
7765
|
allowExec: true,
|
|
6992
7766
|
allowRead: [runtime.homedir()],
|
|
6993
|
-
allowWrite: [
|
|
7767
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6994
7768
|
limits: {
|
|
6995
7769
|
cpu: 60,
|
|
6996
7770
|
nofile: 512,
|