chainlesschain 0.163.2 → 0.163.4
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 +3 -2
- package/scripts/command-lifecycle-report.mjs +330 -38
- package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DB5X7qqV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-DxHRtKLk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-DIH5QrxT.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-uu0oxVz7.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-BNSMiW9Y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-DcAzB4_x.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-ClSsvjIc.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-BMciY0js.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-Hn3b57Sh.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-CoSlC53V.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-B4L4Eu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-C77FgRxF.js} +1 -1
- package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-BrLtJLH3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-Be7pXXhA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-DuGPecjo.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-CzS66mjP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-CEhjCoyK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-D7ir8Vja.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-BcXmuLxf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-Cto1dtBy.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-B7KjgwOX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-COMqxijg.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BZrya-nf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-kZU8c-27.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-sOV22v2-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-K-ZxsLsH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-D3OEnawI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-Bll0gRmP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-DBXwjr53.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-CEzPwEfP.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Bf3DEqP6.js → MarkdownRenderer-CLmCbHx6.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BYyRyOPz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DVu8w_rM.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-tenuZ_hO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-Bxgy4ozS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-Dgx1D6sc.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-CRfJ8r8j.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-BBJq4Aiz.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-B3ii9lai.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-FIi-Cubv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-DIq8MPf1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-BFOD9eGd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer--41u7vre.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-B_0AWztW.js} +2 -2
- package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-B6I7TuXv.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-Cdf6qens.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-6nFBjsQp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-nQYV7xpn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-CBVod5jm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-D52jW1Nt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Bdx3BYhf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-b7xr4DUa.js} +2 -2
- package/src/assets/web-panel/assets/Projects-DhfifCKx.js +1 -0
- package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-FQKSmc3e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-CSTrhzcF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend-CxMABOUh.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-Cv2T8yRa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-CE1k4Vvu.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-BNgNcAV_.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-5ZiBZ3ph.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-mDOIF1UV.js} +1 -1
- package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-CtuCTC-e.js} +3 -3
- package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DxFO2Zg7.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CKmKkH9y.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-Bmzl9qkk.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-9RqaWHOC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-CGdzFIYw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-CvNuCeBT.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-B_sLdcnw.js +1 -0
- package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-D9_YijdK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Cuc7BUSP.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-6VYRnJgd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-BZ94Lt5W.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-DEcZgDaX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-CC3z9THe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-Cvtw8cV3.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-CL4e6Bn3.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-ycEemP27.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-DgZ4X_aw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-D0qMhSv7.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-DsAg01Zn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-B0oqAhrQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-CmX0Rabz.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-Be4cn1Ub.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-D5v12kVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-0eWLkQKT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-301ogenv.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-5LGY0JbG.js} +1 -1
- package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index-7VKDn-t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-8WvYGnON.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-B1YGjOWC.js} +1 -1
- package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-B3mUHuVc.js} +1 -1
- package/src/assets/web-panel/assets/{index--rq90rL9.js → index-BA4oulaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-BBREnn1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-BBvyHguy.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-BKmsS2jj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-BRIqLy1m.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-BXGSbelg.js} +3 -3
- package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-BaE0Ui07.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-BoAIAZ5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-BqltWojQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-CGiIyTZw.js} +1 -1
- package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
- package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-CZ2wUEgr.js} +1 -1
- package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-CZpfzcuy.js} +1 -1
- package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-Ckrk3pIV.js} +1 -1
- package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-CqoCZ5vR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-CyWBir7N.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CyyGTMKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-D0BSkxYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-D5YVopyr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-DKUBj36_.js} +1 -1
- package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-DM724NgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-DQRsQen0.js} +1 -1
- package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
- package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-DikJ0Qqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-caye7AOE.js → index-Dn2dSdXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DyUaFWEk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-Hfe_dEGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-_JjRLSD0.js} +1 -1
- package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-frSojpHj.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-jK3ax6Z6.js} +1 -1
- package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-qMuZ-NRR.js} +1 -1
- package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-q_fQ94SE.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-D4VdhJ9_.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-CZJA0MQW.js} +1 -1
- package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-BqEs7Sb1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-BpHEtsu1.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-BW94tY9W.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-BnBjPWP9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-uUkjQ_hu.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-B-klUqXK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CvY1cNAs.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-Ix5sEDT2.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-C8qcXOne.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DGJr345n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-BboN99rI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-Bgt8Vgte.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-BhJtErgt.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agenda.js +6 -5
- package/src/commands/agent.js +237 -75
- package/src/commands/agents.js +6 -0
- package/src/commands/artifacts.js +98 -0
- package/src/commands/background-session.js +11 -1
- package/src/commands/mcp.js +218 -31
- package/src/commands/review.js +221 -22
- package/src/commands/session-mcp-recovery.js +5 -5
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +492 -14
- package/src/harness/jsonl-session-store.js +39 -1
- package/src/harness/mcp-client.js +414 -71
- package/src/harness/prompt-compressor.js +24 -0
- package/src/harness/provider-backed-compaction.js +205 -0
- package/src/harness/worktree-isolator.js +26 -3
- package/src/lib/agent-worktree.js +41 -2
- package/src/lib/app-preview.js +3 -0
- 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/command-lifecycle-report.js +1340 -145
- package/src/lib/cowork-mcp-tools.js +51 -6
- package/src/lib/cowork-task-runner.js +1 -0
- package/src/lib/delivery-coordinator.js +30 -5
- package/src/lib/delivery-production-adapter.js +1777 -0
- package/src/lib/delivery-production-runner.js +240 -0
- package/src/lib/durable-security-store.js +29 -17
- package/src/lib/execution-trace/trace-context.cjs +179 -0
- package/src/lib/execution-trace/trace-context.js +8 -175
- package/src/lib/mcp-call-ledger-store.js +58 -6
- package/src/lib/mcp-headers-helper.js +76 -8
- package/src/lib/mcp-host-recovery-runtime.js +2 -0
- package/src/lib/mcp-ledger-recovery-admission.js +39 -3
- package/src/lib/mcp-recovery-adjudication.js +256 -19
- package/src/lib/mcp-sandbox-policy.js +184 -0
- package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
- package/src/lib/mcp-stdio-executable-identity.js +33 -2
- package/src/lib/mcp-stdio-execution-authority.js +163 -0
- package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
- package/src/lib/mcp-stdio-package-materialization.js +721 -333
- package/src/lib/mcp-stdio-workspace-authority.js +252 -0
- package/src/lib/observability/index.js +2 -0
- package/src/lib/observability/otlp-exporter.js +2 -2
- package/src/lib/packer/pack-update-applier.js +8 -1
- package/src/lib/packer/pkg-config-generator.js +34 -0
- package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
- package/src/lib/process-execution-broker/index.js +213 -109
- package/src/lib/process-execution-broker/platform-sandbox.js +1024 -169
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/scheduler-kernel/contract.js +315 -0
- package/src/lib/scheduler-kernel/store.js +1345 -0
- package/src/lib/session-host-lease.js +403 -30
- package/src/lib/session-persistence-failure.js +4 -1
- package/src/lib/skill-mcp.js +65 -4
- package/src/lib/sub-agent-context.js +8 -0
- package/src/lib/web-fetch.js +4 -4
- package/src/repl/agent-repl.js +285 -38
- package/src/runtime/agent-core.js +1044 -90
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/headless-runner.js +260 -166
- package/src/runtime/headless-stream.js +254 -25
- package/src/runtime/mcp-config.js +235 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
- package/src/assets/web-panel/assets/Providers-BGs47s1a.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.
|
|
@@ -152,6 +159,9 @@ const LINUX_BWRAP_PATH = "/usr/bin/bwrap";
|
|
|
152
159
|
const LINUX_LDD_PATH = "/usr/bin/ldd";
|
|
153
160
|
const LINUX_BWRAP_BACKEND = "linux-bwrap";
|
|
154
161
|
const LINUX_BWRAP_NODE_PROBE_SENTINEL = "chainless-linux-bwrap-plugin-node-v1";
|
|
162
|
+
const LINUX_BWRAP_CHILD_RUNTIME_PROBE_SENTINEL =
|
|
163
|
+
"chainless-linux-bwrap-child-runtime-v1";
|
|
164
|
+
const LINUX_BWRAP_CHILD_RUNTIME_PROBE_FAILURE_STATUS = 86;
|
|
155
165
|
const LINUX_BWRAP_MAX_PLUGIN_ENTRIES = 512;
|
|
156
166
|
const LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES = 256;
|
|
157
167
|
const LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES = 256 * 1024 * 1024;
|
|
@@ -179,8 +189,8 @@ export const MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256 = crypto
|
|
|
179
189
|
.digest("hex");
|
|
180
190
|
const LINUX_NODE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-source";
|
|
181
191
|
const LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE = "plugin-entry-executable";
|
|
182
|
-
const
|
|
183
|
-
const
|
|
192
|
+
const LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE = "all-pinned-plugin-regular-files";
|
|
193
|
+
const LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY = "per-file-pin-to-launch";
|
|
184
194
|
const LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE = "0400";
|
|
185
195
|
const LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE = "0500";
|
|
186
196
|
const LINUX_ELF64_HEADER_BYTES = 64;
|
|
@@ -311,6 +321,325 @@ function resolveRuntime(overrides = {}) {
|
|
|
311
321
|
return { ...DEFAULT_RUNTIME, ...overrides };
|
|
312
322
|
}
|
|
313
323
|
|
|
324
|
+
function windowsNativeRealpath(runtime, candidate) {
|
|
325
|
+
const nativeRealpath = runtime.fs.realpathSync?.native;
|
|
326
|
+
if (typeof nativeRealpath !== "function") {
|
|
327
|
+
throw new Error("Native Windows realpath is unavailable");
|
|
328
|
+
}
|
|
329
|
+
const nativePath = nativeRealpath.call(runtime.fs, candidate);
|
|
330
|
+
const withoutExtendedPrefix = String(nativePath).replace(
|
|
331
|
+
/^\\\\\?\\(?=[A-Za-z]:\\)/,
|
|
332
|
+
"",
|
|
333
|
+
);
|
|
334
|
+
return path.win32.resolve(withoutExtendedPrefix);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function windowsPathHasReparseSemantics(stat) {
|
|
338
|
+
return (
|
|
339
|
+
stat.isSymbolicLink?.() ||
|
|
340
|
+
stat.isReparsePoint?.() ||
|
|
341
|
+
stat.reparsePoint === true
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function windowsLocalPathAncestors(candidate) {
|
|
346
|
+
const resolved = path.win32.resolve(candidate);
|
|
347
|
+
const volumeRoot = path.win32.parse(resolved).root;
|
|
348
|
+
if (!/^[A-Za-z]:\\$/.test(volumeRoot)) return null;
|
|
349
|
+
const relative = path.win32.relative(volumeRoot, resolved);
|
|
350
|
+
if (
|
|
351
|
+
path.win32.isAbsolute(relative) ||
|
|
352
|
+
relative === ".." ||
|
|
353
|
+
relative.startsWith(`..${path.win32.sep}`)
|
|
354
|
+
) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
const ancestors = [volumeRoot];
|
|
358
|
+
let current = volumeRoot;
|
|
359
|
+
for (const segment of relative.split(path.win32.sep).filter(Boolean)) {
|
|
360
|
+
current = path.win32.join(current, segment);
|
|
361
|
+
ancestors.push(current);
|
|
362
|
+
}
|
|
363
|
+
return ancestors;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function windowsDirectoryIdentity(stat) {
|
|
367
|
+
const birthtimeNs =
|
|
368
|
+
stat.birthtimeNs !== undefined
|
|
369
|
+
? String(stat.birthtimeNs)
|
|
370
|
+
: String(Math.trunc(Number(stat.birthtimeMs || 0) * 1_000_000));
|
|
371
|
+
if (
|
|
372
|
+
stat.dev === undefined ||
|
|
373
|
+
stat.ino === undefined ||
|
|
374
|
+
stat.mode === undefined
|
|
375
|
+
) {
|
|
376
|
+
throw new Error("Windows directory identity is unavailable");
|
|
377
|
+
}
|
|
378
|
+
return Object.freeze({
|
|
379
|
+
dev: String(stat.dev),
|
|
380
|
+
ino: String(stat.ino),
|
|
381
|
+
mode: String(stat.mode),
|
|
382
|
+
birthtimeNs,
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function sameWindowsDirectoryIdentity(left, right) {
|
|
387
|
+
return (
|
|
388
|
+
left?.dev === right?.dev &&
|
|
389
|
+
left?.ino === right?.ino &&
|
|
390
|
+
left?.mode === right?.mode &&
|
|
391
|
+
left?.birthtimeNs === right?.birthtimeNs
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function inspectWindowsAdapterTempRoot(runtime, candidate) {
|
|
396
|
+
if (
|
|
397
|
+
typeof candidate !== "string" ||
|
|
398
|
+
candidate.length === 0 ||
|
|
399
|
+
candidate.includes("\0") ||
|
|
400
|
+
!path.win32.isAbsolute(candidate)
|
|
401
|
+
) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
const sourcePath = path.win32.resolve(candidate);
|
|
405
|
+
const sourceAncestors = windowsLocalPathAncestors(sourcePath);
|
|
406
|
+
if (!sourceAncestors) return null;
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
// Keep the caller-visible path in the trust decision even when Windows
|
|
410
|
+
// exposes it through a legitimate 8.3 alias (for example RUNNER~1 on a
|
|
411
|
+
// hosted runner). Every raw ancestor must be a plain directory before the
|
|
412
|
+
// native real path is accepted as the path used for mutations.
|
|
413
|
+
let sourceRootStat = null;
|
|
414
|
+
for (const ancestor of sourceAncestors) {
|
|
415
|
+
const stat = runtime.fs.lstatSync(ancestor, { bigint: true });
|
|
416
|
+
if (windowsPathHasReparseSemantics(stat) || !stat.isDirectory?.()) {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
sourceRootStat = stat;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const rootPath = windowsNativeRealpath(runtime, sourcePath);
|
|
423
|
+
const rootAncestors = windowsLocalPathAncestors(rootPath);
|
|
424
|
+
if (!rootAncestors) return null;
|
|
425
|
+
let canonicalRootStat = null;
|
|
426
|
+
for (const ancestor of rootAncestors) {
|
|
427
|
+
const stat = runtime.fs.lstatSync(ancestor, { bigint: true });
|
|
428
|
+
if (windowsPathHasReparseSemantics(stat) || !stat.isDirectory?.()) {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
canonicalRootStat = stat;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
const sourceIdentity = windowsDirectoryIdentity(sourceRootStat);
|
|
435
|
+
const identity = windowsDirectoryIdentity(canonicalRootStat);
|
|
436
|
+
if (!sameWindowsDirectoryIdentity(sourceIdentity, identity)) return null;
|
|
437
|
+
|
|
438
|
+
const sourceAfter = runtime.fs.lstatSync(sourcePath, { bigint: true });
|
|
439
|
+
const rootAfter = runtime.fs.lstatSync(rootPath, { bigint: true });
|
|
440
|
+
if (
|
|
441
|
+
windowsPathHasReparseSemantics(sourceAfter) ||
|
|
442
|
+
!sourceAfter.isDirectory?.() ||
|
|
443
|
+
windowsPathHasReparseSemantics(rootAfter) ||
|
|
444
|
+
!rootAfter.isDirectory?.() ||
|
|
445
|
+
windowsCanonicalPathKey(windowsNativeRealpath(runtime, sourcePath)) !==
|
|
446
|
+
windowsCanonicalPathKey(rootPath) ||
|
|
447
|
+
windowsCanonicalPathKey(windowsNativeRealpath(runtime, rootPath)) !==
|
|
448
|
+
windowsCanonicalPathKey(rootPath) ||
|
|
449
|
+
!sameWindowsDirectoryIdentity(
|
|
450
|
+
sourceIdentity,
|
|
451
|
+
windowsDirectoryIdentity(sourceAfter),
|
|
452
|
+
) ||
|
|
453
|
+
!sameWindowsDirectoryIdentity(
|
|
454
|
+
identity,
|
|
455
|
+
windowsDirectoryIdentity(rootAfter),
|
|
456
|
+
)
|
|
457
|
+
) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
return Object.freeze({
|
|
461
|
+
sourcePath,
|
|
462
|
+
sourceCanonicalPathKey: windowsCanonicalPathKey(sourcePath),
|
|
463
|
+
rootPath,
|
|
464
|
+
canonicalPathKey: windowsCanonicalPathKey(rootPath),
|
|
465
|
+
volumeRoot: path.win32.parse(rootPath).root.toLowerCase(),
|
|
466
|
+
identity,
|
|
467
|
+
attestation: Object.freeze({
|
|
468
|
+
kind: "windows-adapter-temp-root-path-reattestation-v1",
|
|
469
|
+
localVolume: true,
|
|
470
|
+
nativeRealpathMatched: true,
|
|
471
|
+
sourceAliasCanonicalized:
|
|
472
|
+
windowsCanonicalPathKey(sourcePath) !==
|
|
473
|
+
windowsCanonicalPathKey(rootPath),
|
|
474
|
+
ancestorReparsePointsRejected: true,
|
|
475
|
+
rootIdentityBound: true,
|
|
476
|
+
criticalOperationReattestation: true,
|
|
477
|
+
descriptorRelativeOperations: false,
|
|
478
|
+
handleAtomic: false,
|
|
479
|
+
residualRace:
|
|
480
|
+
"same-principal-path-swap-between-reattestation-and-filesystem-call",
|
|
481
|
+
residualHandling: "fail-closed-on-detected-path-or-identity-change",
|
|
482
|
+
}),
|
|
483
|
+
});
|
|
484
|
+
} catch {
|
|
485
|
+
return null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
function sameWindowsAdapterTempRootBinding(left, right) {
|
|
490
|
+
return (
|
|
491
|
+
left?.sourceCanonicalPathKey === right?.sourceCanonicalPathKey &&
|
|
492
|
+
left?.canonicalPathKey === right?.canonicalPathKey &&
|
|
493
|
+
left?.volumeRoot === right?.volumeRoot &&
|
|
494
|
+
sameWindowsDirectoryIdentity(left?.identity, right?.identity)
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function verifyWindowsAdapterTempRootBinding(runtime, binding) {
|
|
499
|
+
const current = inspectWindowsAdapterTempRoot(
|
|
500
|
+
runtime,
|
|
501
|
+
binding?.sourcePath || binding?.rootPath,
|
|
502
|
+
);
|
|
503
|
+
return Boolean(
|
|
504
|
+
current && sameWindowsAdapterTempRootBinding(binding, current),
|
|
505
|
+
);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function windowsAdapterTempRootChangedError() {
|
|
509
|
+
const error = new Error(
|
|
510
|
+
"Windows sandbox adapter temp root changed after attestation",
|
|
511
|
+
);
|
|
512
|
+
error.code = "ERR_WINDOWS_SANDBOX_ADAPTER_TEMP_ROOT_CHANGED";
|
|
513
|
+
error.adapterReason = WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON;
|
|
514
|
+
return error;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function assertWindowsAdapterTempRoot(runtime) {
|
|
518
|
+
const binding = runtime.windowsAdapterTempRootBinding;
|
|
519
|
+
if (!binding || !verifyWindowsAdapterTempRootBinding(runtime, binding)) {
|
|
520
|
+
throw windowsAdapterTempRootChangedError();
|
|
521
|
+
}
|
|
522
|
+
return binding;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function inspectWindowsAdapterBoundPath(
|
|
526
|
+
runtime,
|
|
527
|
+
targetPath,
|
|
528
|
+
expectedKind,
|
|
529
|
+
{ allowMissing = false } = {},
|
|
530
|
+
) {
|
|
531
|
+
const binding = assertWindowsAdapterTempRoot(runtime);
|
|
532
|
+
if (
|
|
533
|
+
typeof targetPath !== "string" ||
|
|
534
|
+
targetPath.includes("\0") ||
|
|
535
|
+
!path.win32.isAbsolute(targetPath)
|
|
536
|
+
) {
|
|
537
|
+
throw windowsAdapterTempRootChangedError();
|
|
538
|
+
}
|
|
539
|
+
const resolved = path.win32.resolve(targetPath);
|
|
540
|
+
const relative = path.win32.relative(binding.rootPath, resolved);
|
|
541
|
+
if (
|
|
542
|
+
!relative ||
|
|
543
|
+
path.win32.isAbsolute(relative) ||
|
|
544
|
+
relative === ".." ||
|
|
545
|
+
relative.startsWith(`..${path.win32.sep}`)
|
|
546
|
+
) {
|
|
547
|
+
throw windowsAdapterTempRootChangedError();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const segments = relative.split(path.win32.sep).filter(Boolean);
|
|
551
|
+
let current = binding.rootPath;
|
|
552
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
553
|
+
current = path.win32.join(current, segments[index]);
|
|
554
|
+
const isTarget = index === segments.length - 1;
|
|
555
|
+
let stat;
|
|
556
|
+
try {
|
|
557
|
+
stat = runtime.fs.lstatSync(current, { bigint: true });
|
|
558
|
+
} catch (error) {
|
|
559
|
+
if (isTarget && allowMissing && error?.code === "ENOENT") {
|
|
560
|
+
return Object.freeze({ exists: false, resolvedPath: resolved });
|
|
561
|
+
}
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
if (
|
|
565
|
+
windowsPathHasReparseSemantics(stat) ||
|
|
566
|
+
windowsCanonicalPathKey(windowsNativeRealpath(runtime, current)) !==
|
|
567
|
+
windowsCanonicalPathKey(current)
|
|
568
|
+
) {
|
|
569
|
+
throw windowsAdapterTempRootChangedError();
|
|
570
|
+
}
|
|
571
|
+
if (!isTarget && !stat.isDirectory?.()) {
|
|
572
|
+
throw windowsAdapterTempRootChangedError();
|
|
573
|
+
}
|
|
574
|
+
if (isTarget) {
|
|
575
|
+
if (
|
|
576
|
+
(expectedKind === "file" && !stat.isFile?.()) ||
|
|
577
|
+
(expectedKind === "directory" && !stat.isDirectory?.())
|
|
578
|
+
) {
|
|
579
|
+
throw windowsAdapterTempRootChangedError();
|
|
580
|
+
}
|
|
581
|
+
return Object.freeze({
|
|
582
|
+
exists: true,
|
|
583
|
+
resolvedPath: resolved,
|
|
584
|
+
identity:
|
|
585
|
+
expectedKind === "directory"
|
|
586
|
+
? windowsDirectoryIdentity(stat)
|
|
587
|
+
: windowsFileIdentity(stat),
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
throw windowsAdapterTempRootChangedError();
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function assertWindowsAdapterBoundPathIdentity(
|
|
595
|
+
runtime,
|
|
596
|
+
targetPath,
|
|
597
|
+
expectedKind,
|
|
598
|
+
expectedIdentity,
|
|
599
|
+
) {
|
|
600
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
601
|
+
runtime,
|
|
602
|
+
targetPath,
|
|
603
|
+
expectedKind,
|
|
604
|
+
);
|
|
605
|
+
const matches =
|
|
606
|
+
expectedKind === "directory"
|
|
607
|
+
? sameWindowsDirectoryIdentity(inspected.identity, expectedIdentity)
|
|
608
|
+
: sameWindowsFileIdentity(inspected.identity, expectedIdentity);
|
|
609
|
+
if (!matches) throw windowsAdapterTempRootChangedError();
|
|
610
|
+
return inspected;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function resolveWindowsAdapterTempRoot(runtime, runtimeOverrides = {}) {
|
|
614
|
+
const hasExplicitTmpdir = Object.prototype.hasOwnProperty.call(
|
|
615
|
+
runtimeOverrides,
|
|
616
|
+
"tmpdir",
|
|
617
|
+
);
|
|
618
|
+
const hasExplicitAdapterRoot = Object.prototype.hasOwnProperty.call(
|
|
619
|
+
runtimeOverrides,
|
|
620
|
+
"windowsAdapterTempRoot",
|
|
621
|
+
);
|
|
622
|
+
let candidate;
|
|
623
|
+
try {
|
|
624
|
+
if (hasExplicitTmpdir) {
|
|
625
|
+
if (typeof runtime.tmpdir !== "function") return null;
|
|
626
|
+
candidate = runtime.tmpdir();
|
|
627
|
+
} else if (hasExplicitAdapterRoot) {
|
|
628
|
+
candidate = runtimeOverrides.windowsAdapterTempRoot;
|
|
629
|
+
} else {
|
|
630
|
+
const environmentRoot =
|
|
631
|
+
runtime.env?.[WINDOWS_ADAPTER_TEMP_ROOT_ENV] ??
|
|
632
|
+
process.env[WINDOWS_ADAPTER_TEMP_ROOT_ENV];
|
|
633
|
+
candidate =
|
|
634
|
+
environmentRoot === undefined ? runtime.tmpdir() : environmentRoot;
|
|
635
|
+
}
|
|
636
|
+
} catch {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return inspectWindowsAdapterTempRoot(runtime, candidate);
|
|
641
|
+
}
|
|
642
|
+
|
|
314
643
|
function normalizeSandboxRequest(profileOrRequest, explicitRequest) {
|
|
315
644
|
const legacyRequest =
|
|
316
645
|
profileOrRequest &&
|
|
@@ -788,14 +1117,42 @@ function cleanupWindowsTemporaryPath(
|
|
|
788
1117
|
targetPath,
|
|
789
1118
|
attempts = 100,
|
|
790
1119
|
delayMs = 10,
|
|
1120
|
+
expectedIdentity = null,
|
|
791
1121
|
) {
|
|
792
1122
|
if (!targetPath) return true;
|
|
793
1123
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
794
1124
|
try {
|
|
1125
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
1126
|
+
runtime,
|
|
1127
|
+
targetPath,
|
|
1128
|
+
"file",
|
|
1129
|
+
{ allowMissing: true },
|
|
1130
|
+
);
|
|
1131
|
+
if (!inspected.exists) {
|
|
1132
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1133
|
+
return true;
|
|
1134
|
+
}
|
|
1135
|
+
if (
|
|
1136
|
+
!expectedIdentity ||
|
|
1137
|
+
!sameWindowsFileIdentity(inspected.identity, expectedIdentity)
|
|
1138
|
+
) {
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
795
1141
|
runtime.fs.unlinkSync(targetPath);
|
|
1142
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
796
1143
|
return true;
|
|
797
1144
|
} catch (error) {
|
|
798
|
-
if (error?.
|
|
1145
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
1146
|
+
return false;
|
|
1147
|
+
}
|
|
1148
|
+
if (error?.code === "ENOENT") {
|
|
1149
|
+
try {
|
|
1150
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1151
|
+
return true;
|
|
1152
|
+
} catch {
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
799
1156
|
}
|
|
800
1157
|
if (attempt + 1 < attempts) runtime.sleepSync(delayMs);
|
|
801
1158
|
}
|
|
@@ -806,13 +1163,32 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
806
1163
|
runtime,
|
|
807
1164
|
targetPath,
|
|
808
1165
|
attempts = 100,
|
|
1166
|
+
entry = null,
|
|
1167
|
+
expectedIdentity = null,
|
|
809
1168
|
) {
|
|
810
|
-
if (
|
|
1169
|
+
if (
|
|
1170
|
+
cleanupWindowsTemporaryPath(
|
|
1171
|
+
runtime,
|
|
1172
|
+
targetPath,
|
|
1173
|
+
attempts,
|
|
1174
|
+
10,
|
|
1175
|
+
expectedIdentity,
|
|
1176
|
+
)
|
|
1177
|
+
) {
|
|
1178
|
+
const residualEntries = new Set();
|
|
811
1179
|
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
812
|
-
if (
|
|
1180
|
+
if (
|
|
1181
|
+
residual.kind === "file" &&
|
|
1182
|
+
residual.runtime === runtime &&
|
|
1183
|
+
residual.targetPath === targetPath
|
|
1184
|
+
) {
|
|
813
1185
|
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1186
|
+
if (residual.entry) residualEntries.add(residual.entry);
|
|
814
1187
|
}
|
|
815
1188
|
}
|
|
1189
|
+
for (const residualEntry of residualEntries) {
|
|
1190
|
+
finalizeWindowsTemporaryCleanupEntry(residualEntry);
|
|
1191
|
+
}
|
|
816
1192
|
if (
|
|
817
1193
|
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
818
1194
|
windowsTemporaryCleanupRetryTimer
|
|
@@ -822,13 +1198,25 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
822
1198
|
}
|
|
823
1199
|
return true;
|
|
824
1200
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
)
|
|
831
|
-
|
|
1201
|
+
const existing = [...windowsTemporaryCleanupBacklog].find(
|
|
1202
|
+
(residual) =>
|
|
1203
|
+
residual.kind === "file" &&
|
|
1204
|
+
residual.runtime === runtime &&
|
|
1205
|
+
residual.targetPath === targetPath,
|
|
1206
|
+
);
|
|
1207
|
+
if (existing) {
|
|
1208
|
+
if (entry) existing.entry = entry;
|
|
1209
|
+
if (!existing.expectedIdentity && expectedIdentity) {
|
|
1210
|
+
existing.expectedIdentity = expectedIdentity;
|
|
1211
|
+
}
|
|
1212
|
+
} else {
|
|
1213
|
+
windowsTemporaryCleanupBacklog.add({
|
|
1214
|
+
kind: "file",
|
|
1215
|
+
runtime,
|
|
1216
|
+
targetPath,
|
|
1217
|
+
entry,
|
|
1218
|
+
expectedIdentity,
|
|
1219
|
+
});
|
|
832
1220
|
}
|
|
833
1221
|
registerWindowsAdapterExitCleanup();
|
|
834
1222
|
scheduleWindowsTemporaryCleanupRetry();
|
|
@@ -838,14 +1226,25 @@ function cleanupOrTrackWindowsTemporaryPath(
|
|
|
838
1226
|
function retryWindowsTemporaryCleanupBacklog(attempts = 100) {
|
|
839
1227
|
let cleaned = true;
|
|
840
1228
|
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1229
|
+
const residualCleaned =
|
|
1230
|
+
residual.kind === "directory"
|
|
1231
|
+
? cleanupWindowsTemporaryDirectory(
|
|
1232
|
+
residual.runtime,
|
|
1233
|
+
residual.targetPath,
|
|
1234
|
+
attempts,
|
|
1235
|
+
10,
|
|
1236
|
+
residual.expectedIdentity,
|
|
1237
|
+
)
|
|
1238
|
+
: cleanupWindowsTemporaryPath(
|
|
1239
|
+
residual.runtime,
|
|
1240
|
+
residual.targetPath,
|
|
1241
|
+
attempts,
|
|
1242
|
+
10,
|
|
1243
|
+
residual.expectedIdentity,
|
|
1244
|
+
);
|
|
1245
|
+
if (residualCleaned) {
|
|
848
1246
|
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1247
|
+
if (residual.entry) finalizeWindowsTemporaryCleanupEntry(residual.entry);
|
|
849
1248
|
} else {
|
|
850
1249
|
cleaned = false;
|
|
851
1250
|
}
|
|
@@ -860,6 +1259,18 @@ function retryWindowsTemporaryCleanupBacklog(attempts = 100) {
|
|
|
860
1259
|
return cleaned;
|
|
861
1260
|
}
|
|
862
1261
|
|
|
1262
|
+
function finalizeWindowsTemporaryCleanupEntry(entry) {
|
|
1263
|
+
if (
|
|
1264
|
+
[...windowsTemporaryCleanupBacklog].some(
|
|
1265
|
+
(residual) => residual.entry === entry,
|
|
1266
|
+
)
|
|
1267
|
+
) {
|
|
1268
|
+
return;
|
|
1269
|
+
}
|
|
1270
|
+
entry.cleaned = true;
|
|
1271
|
+
windowsAdapterCacheEntries.delete(entry);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
863
1274
|
function scheduleWindowsTemporaryCleanupRetry() {
|
|
864
1275
|
if (
|
|
865
1276
|
windowsTemporaryCleanupRetryTimer ||
|
|
@@ -882,20 +1293,112 @@ function cleanupWindowsTemporaryDirectory(
|
|
|
882
1293
|
targetPath,
|
|
883
1294
|
attempts = 100,
|
|
884
1295
|
delayMs = 10,
|
|
1296
|
+
expectedIdentity = null,
|
|
885
1297
|
) {
|
|
886
1298
|
if (!targetPath) return true;
|
|
887
1299
|
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
888
1300
|
try {
|
|
1301
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
1302
|
+
runtime,
|
|
1303
|
+
targetPath,
|
|
1304
|
+
"directory",
|
|
1305
|
+
{ allowMissing: true },
|
|
1306
|
+
);
|
|
1307
|
+
if (!inspected.exists) {
|
|
1308
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1309
|
+
return true;
|
|
1310
|
+
}
|
|
1311
|
+
if (
|
|
1312
|
+
!expectedIdentity ||
|
|
1313
|
+
!sameWindowsDirectoryIdentity(inspected.identity, expectedIdentity)
|
|
1314
|
+
) {
|
|
1315
|
+
return false;
|
|
1316
|
+
}
|
|
889
1317
|
runtime.fs.rmdirSync(targetPath);
|
|
1318
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
890
1319
|
return true;
|
|
891
1320
|
} catch (error) {
|
|
892
|
-
if (error?.
|
|
1321
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
1322
|
+
return false;
|
|
1323
|
+
}
|
|
1324
|
+
if (error?.code === "ENOENT") {
|
|
1325
|
+
try {
|
|
1326
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1327
|
+
return true;
|
|
1328
|
+
} catch {
|
|
1329
|
+
return false;
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
893
1332
|
}
|
|
894
1333
|
if (attempt + 1 < attempts) runtime.sleepSync(delayMs);
|
|
895
1334
|
}
|
|
896
1335
|
return false;
|
|
897
1336
|
}
|
|
898
1337
|
|
|
1338
|
+
function cleanupOrTrackWindowsTemporaryDirectory(
|
|
1339
|
+
runtime,
|
|
1340
|
+
targetPath,
|
|
1341
|
+
attempts = 100,
|
|
1342
|
+
entry = null,
|
|
1343
|
+
expectedIdentity = null,
|
|
1344
|
+
) {
|
|
1345
|
+
if (
|
|
1346
|
+
cleanupWindowsTemporaryDirectory(
|
|
1347
|
+
runtime,
|
|
1348
|
+
targetPath,
|
|
1349
|
+
attempts,
|
|
1350
|
+
10,
|
|
1351
|
+
expectedIdentity,
|
|
1352
|
+
)
|
|
1353
|
+
) {
|
|
1354
|
+
const residualEntries = new Set();
|
|
1355
|
+
for (const residual of [...windowsTemporaryCleanupBacklog]) {
|
|
1356
|
+
if (
|
|
1357
|
+
residual.kind === "directory" &&
|
|
1358
|
+
residual.runtime === runtime &&
|
|
1359
|
+
residual.targetPath === targetPath
|
|
1360
|
+
) {
|
|
1361
|
+
windowsTemporaryCleanupBacklog.delete(residual);
|
|
1362
|
+
if (residual.entry) residualEntries.add(residual.entry);
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
for (const residualEntry of residualEntries) {
|
|
1366
|
+
finalizeWindowsTemporaryCleanupEntry(residualEntry);
|
|
1367
|
+
}
|
|
1368
|
+
if (
|
|
1369
|
+
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
1370
|
+
windowsTemporaryCleanupRetryTimer
|
|
1371
|
+
) {
|
|
1372
|
+
clearTimeout(windowsTemporaryCleanupRetryTimer);
|
|
1373
|
+
windowsTemporaryCleanupRetryTimer = null;
|
|
1374
|
+
}
|
|
1375
|
+
return true;
|
|
1376
|
+
}
|
|
1377
|
+
const existing = [...windowsTemporaryCleanupBacklog].find(
|
|
1378
|
+
(residual) =>
|
|
1379
|
+
residual.kind === "directory" &&
|
|
1380
|
+
residual.runtime === runtime &&
|
|
1381
|
+
residual.targetPath === targetPath,
|
|
1382
|
+
);
|
|
1383
|
+
if (existing) {
|
|
1384
|
+
if (entry) existing.entry = entry;
|
|
1385
|
+
if (!existing.expectedIdentity && expectedIdentity) {
|
|
1386
|
+
existing.expectedIdentity = expectedIdentity;
|
|
1387
|
+
}
|
|
1388
|
+
} else {
|
|
1389
|
+
windowsTemporaryCleanupBacklog.add({
|
|
1390
|
+
kind: "directory",
|
|
1391
|
+
runtime,
|
|
1392
|
+
targetPath,
|
|
1393
|
+
entry,
|
|
1394
|
+
expectedIdentity,
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
registerWindowsAdapterExitCleanup();
|
|
1398
|
+
scheduleWindowsTemporaryCleanupRetry();
|
|
1399
|
+
return false;
|
|
1400
|
+
}
|
|
1401
|
+
|
|
899
1402
|
function sha256(value) {
|
|
900
1403
|
return crypto.createHash("sha256").update(value).digest("hex");
|
|
901
1404
|
}
|
|
@@ -1143,15 +1646,11 @@ function sameWindowsFileIdentity(left, right) {
|
|
|
1143
1646
|
}
|
|
1144
1647
|
|
|
1145
1648
|
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
|
-
}
|
|
1649
|
+
const boundBefore = inspectWindowsAdapterBoundPath(
|
|
1650
|
+
runtime,
|
|
1651
|
+
assemblyPath,
|
|
1652
|
+
"file",
|
|
1653
|
+
);
|
|
1155
1654
|
const before = runtime.fs.statSync(assemblyPath, { bigint: true });
|
|
1156
1655
|
if (typeof before.isFile === "function" && !before.isFile()) {
|
|
1157
1656
|
throw new Error("Windows in-memory adapter is not a regular file");
|
|
@@ -1164,6 +1663,15 @@ function inspectWindowsAdapterAssembly(runtime, assemblyPath) {
|
|
|
1164
1663
|
if (!sameWindowsFileIdentity(beforeIdentity, afterIdentity)) {
|
|
1165
1664
|
throw new Error("Windows in-memory adapter changed during attestation");
|
|
1166
1665
|
}
|
|
1666
|
+
const boundAfter = assertWindowsAdapterBoundPathIdentity(
|
|
1667
|
+
runtime,
|
|
1668
|
+
assemblyPath,
|
|
1669
|
+
"file",
|
|
1670
|
+
boundBefore.identity,
|
|
1671
|
+
);
|
|
1672
|
+
if (!sameWindowsFileIdentity(afterIdentity, boundAfter.identity)) {
|
|
1673
|
+
throw new Error("Windows in-memory adapter changed during attestation");
|
|
1674
|
+
}
|
|
1167
1675
|
return { assemblyDigest, fileIdentity: afterIdentity };
|
|
1168
1676
|
}
|
|
1169
1677
|
|
|
@@ -1173,6 +1681,11 @@ function verifyWindowsAdapterEntry(entry, runtime, source) {
|
|
|
1173
1681
|
entry.cleaned ||
|
|
1174
1682
|
entry.runtimeFs !== runtime.fs ||
|
|
1175
1683
|
entry.tempDirectory !== source.tempDirectory ||
|
|
1684
|
+
entry.idleTtlMs !== source.idleTtlMs ||
|
|
1685
|
+
!sameWindowsAdapterTempRootBinding(
|
|
1686
|
+
entry.tempRootBinding,
|
|
1687
|
+
source.tempRootBinding,
|
|
1688
|
+
) ||
|
|
1176
1689
|
entry.sourceDigest !== source.sourceDigest ||
|
|
1177
1690
|
entry.sourceContractDigest !== source.sourceContractDigest ||
|
|
1178
1691
|
entry.loaderMode !== source.loaderMode
|
|
@@ -1180,6 +1693,15 @@ function verifyWindowsAdapterEntry(entry, runtime, source) {
|
|
|
1180
1693
|
return false;
|
|
1181
1694
|
}
|
|
1182
1695
|
try {
|
|
1696
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1697
|
+
if (entry.adapterDirectory) {
|
|
1698
|
+
assertWindowsAdapterBoundPathIdentity(
|
|
1699
|
+
runtime,
|
|
1700
|
+
entry.adapterDirectory,
|
|
1701
|
+
"directory",
|
|
1702
|
+
entry.adapterDirectoryIdentity,
|
|
1703
|
+
);
|
|
1704
|
+
}
|
|
1183
1705
|
const attestation = inspectWindowsAdapterAssembly(
|
|
1184
1706
|
runtime,
|
|
1185
1707
|
entry.assemblyPath,
|
|
@@ -1203,14 +1725,18 @@ function cleanupWindowsAdapterEntry(entry, attempts = 100) {
|
|
|
1203
1725
|
entry.runtime,
|
|
1204
1726
|
entry.assemblyPath,
|
|
1205
1727
|
attempts,
|
|
1728
|
+
entry,
|
|
1729
|
+
entry.fileIdentity,
|
|
1206
1730
|
);
|
|
1207
|
-
const directoryDeleted =
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1731
|
+
const directoryDeleted = entry.adapterDirectory
|
|
1732
|
+
? cleanupOrTrackWindowsTemporaryDirectory(
|
|
1733
|
+
entry.runtime,
|
|
1734
|
+
entry.adapterDirectory,
|
|
1735
|
+
attempts,
|
|
1736
|
+
entry,
|
|
1737
|
+
entry.adapterDirectoryIdentity,
|
|
1738
|
+
)
|
|
1739
|
+
: true;
|
|
1214
1740
|
if (deleted && directoryDeleted) {
|
|
1215
1741
|
entry.cleaned = true;
|
|
1216
1742
|
windowsAdapterCacheEntries.delete(entry);
|
|
@@ -1253,15 +1779,35 @@ function registerWindowsAdapterExitCleanup() {
|
|
|
1253
1779
|
* this hook at a known quiescent point to prove cleanup.
|
|
1254
1780
|
*/
|
|
1255
1781
|
export function resetWindowsSandboxAdapterCache() {
|
|
1256
|
-
|
|
1782
|
+
const appContainerCleaned = retryWindowsAppContainerCleanupBacklog();
|
|
1257
1783
|
windowsAdapterCache = null;
|
|
1258
1784
|
for (const entry of [...windowsAdapterCacheEntries]) {
|
|
1259
1785
|
entry.retired = true;
|
|
1260
1786
|
entry.refCount = 0;
|
|
1261
|
-
|
|
1787
|
+
cleanupWindowsAdapterEntry(entry);
|
|
1262
1788
|
}
|
|
1263
|
-
|
|
1264
|
-
return
|
|
1789
|
+
retryWindowsTemporaryCleanupBacklog();
|
|
1790
|
+
return (
|
|
1791
|
+
appContainerCleaned &&
|
|
1792
|
+
windowsTemporaryCleanupBacklog.size === 0 &&
|
|
1793
|
+
windowsAdapterCacheEntries.size === 0
|
|
1794
|
+
);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
function normalizeWindowsAdapterIdleTtlMs(runtime) {
|
|
1798
|
+
const explicitTtl = runtime.windowsAdapterIdleTtlMs;
|
|
1799
|
+
const environmentTtl =
|
|
1800
|
+
runtime.env?.[WINDOWS_ADAPTER_IDLE_TTL_ENV] ??
|
|
1801
|
+
process.env[WINDOWS_ADAPTER_IDLE_TTL_ENV];
|
|
1802
|
+
const configuredTtl =
|
|
1803
|
+
explicitTtl === undefined ? environmentTtl : explicitTtl;
|
|
1804
|
+
const numericTtl =
|
|
1805
|
+
configuredTtl === "" || configuredTtl === null
|
|
1806
|
+
? Number.NaN
|
|
1807
|
+
: Number(configuredTtl);
|
|
1808
|
+
return Number.isFinite(numericTtl) && numericTtl >= 0
|
|
1809
|
+
? numericTtl
|
|
1810
|
+
: WINDOWS_ADAPTER_IDLE_TTL_MS;
|
|
1265
1811
|
}
|
|
1266
1812
|
|
|
1267
1813
|
function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
@@ -1273,11 +1819,11 @@ function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
|
1273
1819
|
) {
|
|
1274
1820
|
return;
|
|
1275
1821
|
}
|
|
1276
|
-
const
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1822
|
+
const idleTtlMs = entry.idleTtlMs;
|
|
1823
|
+
if (idleTtlMs === 0) {
|
|
1824
|
+
retireWindowsAdapterEntry(entry);
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1281
1827
|
entry.idleTimer = setTimeout(() => {
|
|
1282
1828
|
entry.idleTimer = null;
|
|
1283
1829
|
if (
|
|
@@ -1293,6 +1839,7 @@ function scheduleWindowsAdapterIdleCleanup(entry) {
|
|
|
1293
1839
|
}
|
|
1294
1840
|
|
|
1295
1841
|
function loadWindowsAdapterSource(runtime, loaderMode) {
|
|
1842
|
+
const tempRootBinding = assertWindowsAdapterTempRoot(runtime);
|
|
1296
1843
|
const bundled = runtime.windowsAdapterContent === undefined;
|
|
1297
1844
|
const content = bundled
|
|
1298
1845
|
? loaderMode === "managed-executable"
|
|
@@ -1307,7 +1854,10 @@ function loadWindowsAdapterSource(runtime, loaderMode) {
|
|
|
1307
1854
|
: WINDOWS_SANDBOX_HELPER_ASSEMBLY_DIGEST
|
|
1308
1855
|
: sha256(content),
|
|
1309
1856
|
sourceContractDigest: bundled ? WINDOWS_SANDBOX_HELPER_SOURCE_DIGEST : null,
|
|
1310
|
-
tempDirectory:
|
|
1857
|
+
tempDirectory: tempRootBinding.rootPath,
|
|
1858
|
+
tempRootBinding,
|
|
1859
|
+
tempRootAttestation: tempRootBinding.attestation,
|
|
1860
|
+
idleTtlMs: runtime.windowsAdapterIdleTtlMs,
|
|
1311
1861
|
loaderMode,
|
|
1312
1862
|
};
|
|
1313
1863
|
}
|
|
@@ -1329,54 +1879,121 @@ function materializeWindowsAdapter(runtime, source) {
|
|
|
1329
1879
|
const assemblyPath = runtime.joinPath(
|
|
1330
1880
|
adapterDirectory || source.tempDirectory,
|
|
1331
1881
|
source.loaderMode === "managed-executable"
|
|
1332
|
-
?
|
|
1882
|
+
? WINDOWS_ADAPTER_HELPER_NAME
|
|
1333
1883
|
: `${adapterBaseName}.dll`,
|
|
1334
1884
|
);
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
runtime.fs.existsSync(assemblyPath)
|
|
1338
|
-
) {
|
|
1339
|
-
return { reason: "windows_native_adapter_random_path_collision" };
|
|
1340
|
-
}
|
|
1885
|
+
let adapterDirectoryIdentity = null;
|
|
1886
|
+
let assemblyFileIdentity = null;
|
|
1341
1887
|
try {
|
|
1888
|
+
assertWindowsAdapterTempRoot(runtime);
|
|
1342
1889
|
if (adapterDirectory) {
|
|
1890
|
+
if (
|
|
1891
|
+
inspectWindowsAdapterBoundPath(runtime, adapterDirectory, "directory", {
|
|
1892
|
+
allowMissing: true,
|
|
1893
|
+
}).exists
|
|
1894
|
+
) {
|
|
1895
|
+
return { reason: "windows_native_adapter_random_path_collision" };
|
|
1896
|
+
}
|
|
1343
1897
|
runtime.fs.mkdirSync(adapterDirectory, {
|
|
1344
1898
|
mode: 0o700,
|
|
1345
1899
|
recursive: false,
|
|
1346
1900
|
});
|
|
1901
|
+
adapterDirectoryIdentity = inspectWindowsAdapterBoundPath(
|
|
1902
|
+
runtime,
|
|
1903
|
+
adapterDirectory,
|
|
1904
|
+
"directory",
|
|
1905
|
+
).identity;
|
|
1906
|
+
}
|
|
1907
|
+
if (
|
|
1908
|
+
inspectWindowsAdapterBoundPath(runtime, assemblyPath, "file", {
|
|
1909
|
+
allowMissing: true,
|
|
1910
|
+
}).exists
|
|
1911
|
+
) {
|
|
1912
|
+
const collision = new Error(
|
|
1913
|
+
"Windows native adapter random path collision",
|
|
1914
|
+
);
|
|
1915
|
+
collision.code = "EEXIST";
|
|
1916
|
+
throw collision;
|
|
1347
1917
|
}
|
|
1348
1918
|
runtime.fs.writeFileSync(assemblyPath, source.content, {
|
|
1349
1919
|
mode: adapterDirectory ? 0o500 : 0o600,
|
|
1350
1920
|
flag: "wx",
|
|
1351
1921
|
});
|
|
1922
|
+
assemblyFileIdentity = inspectWindowsAdapterAssembly(
|
|
1923
|
+
runtime,
|
|
1924
|
+
assemblyPath,
|
|
1925
|
+
).fileIdentity;
|
|
1352
1926
|
} catch (error) {
|
|
1927
|
+
if (error?.code === "EEXIST") {
|
|
1928
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1929
|
+
runtime,
|
|
1930
|
+
adapterDirectory,
|
|
1931
|
+
100,
|
|
1932
|
+
null,
|
|
1933
|
+
adapterDirectoryIdentity,
|
|
1934
|
+
);
|
|
1935
|
+
return {
|
|
1936
|
+
reason: directoryDeleted
|
|
1937
|
+
? "windows_native_adapter_random_path_collision"
|
|
1938
|
+
: "windows_native_adapter_compile_cleanup_unverified",
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
if (!assemblyFileIdentity) {
|
|
1942
|
+
try {
|
|
1943
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
1944
|
+
runtime,
|
|
1945
|
+
assemblyPath,
|
|
1946
|
+
"file",
|
|
1947
|
+
{ allowMissing: true },
|
|
1948
|
+
);
|
|
1949
|
+
assemblyFileIdentity = inspected.exists ? inspected.identity : null;
|
|
1950
|
+
} catch {
|
|
1951
|
+
assemblyFileIdentity = null;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1353
1954
|
const assemblyDeleted = cleanupOrTrackWindowsTemporaryPath(
|
|
1354
1955
|
runtime,
|
|
1355
1956
|
assemblyPath,
|
|
1957
|
+
100,
|
|
1958
|
+
null,
|
|
1959
|
+
assemblyFileIdentity,
|
|
1960
|
+
);
|
|
1961
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1962
|
+
runtime,
|
|
1963
|
+
adapterDirectory,
|
|
1964
|
+
100,
|
|
1965
|
+
null,
|
|
1966
|
+
adapterDirectoryIdentity,
|
|
1356
1967
|
);
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1968
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
1969
|
+
return { reason: WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON };
|
|
1970
|
+
}
|
|
1360
1971
|
if (!assemblyDeleted || !directoryDeleted) {
|
|
1361
1972
|
return { reason: "windows_native_adapter_compile_cleanup_unverified" };
|
|
1362
1973
|
}
|
|
1363
|
-
if (error?.code === "EEXIST") {
|
|
1364
|
-
return { reason: "windows_native_adapter_random_path_collision" };
|
|
1365
|
-
}
|
|
1366
1974
|
return { reason: "windows_native_adapter_materialize_failed" };
|
|
1367
1975
|
}
|
|
1368
1976
|
return {
|
|
1369
1977
|
adapterDirectory,
|
|
1978
|
+
adapterDirectoryIdentity,
|
|
1370
1979
|
assemblyPath,
|
|
1980
|
+
assemblyFileIdentity,
|
|
1371
1981
|
cleanupAssembly: () => {
|
|
1372
1982
|
const assemblyDeleted = cleanupOrTrackWindowsTemporaryPath(
|
|
1373
1983
|
runtime,
|
|
1374
1984
|
assemblyPath,
|
|
1985
|
+
100,
|
|
1986
|
+
null,
|
|
1987
|
+
assemblyFileIdentity,
|
|
1375
1988
|
);
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1989
|
+
const directoryDeleted = cleanupOrTrackWindowsTemporaryDirectory(
|
|
1990
|
+
runtime,
|
|
1991
|
+
adapterDirectory,
|
|
1992
|
+
100,
|
|
1993
|
+
null,
|
|
1994
|
+
adapterDirectoryIdentity,
|
|
1379
1995
|
);
|
|
1996
|
+
return assemblyDeleted && directoryDeleted;
|
|
1380
1997
|
},
|
|
1381
1998
|
};
|
|
1382
1999
|
}
|
|
@@ -1388,9 +2005,7 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1388
2005
|
.randomBytes(24)
|
|
1389
2006
|
.toString("hex")}.json`,
|
|
1390
2007
|
);
|
|
1391
|
-
|
|
1392
|
-
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
1393
|
-
}
|
|
2008
|
+
let fileIdentity = null;
|
|
1394
2009
|
let content;
|
|
1395
2010
|
try {
|
|
1396
2011
|
content = Buffer.from(
|
|
@@ -1410,16 +2025,57 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1410
2025
|
return { reason: "windows_adapter_invocation_too_large" };
|
|
1411
2026
|
}
|
|
1412
2027
|
try {
|
|
2028
|
+
if (
|
|
2029
|
+
inspectWindowsAdapterBoundPath(runtime, payloadPath, "file", {
|
|
2030
|
+
allowMissing: true,
|
|
2031
|
+
}).exists
|
|
2032
|
+
) {
|
|
2033
|
+
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
2034
|
+
}
|
|
1413
2035
|
runtime.fs.writeFileSync(payloadPath, content, {
|
|
1414
2036
|
mode: 0o600,
|
|
1415
2037
|
flag: "wx",
|
|
1416
2038
|
});
|
|
2039
|
+
const inspected = inspectWindowsAdapterAssembly(runtime, payloadPath);
|
|
2040
|
+
if (inspected.assemblyDigest !== sha256(content)) {
|
|
2041
|
+
throw new Error("Windows adapter invocation changed after creation");
|
|
2042
|
+
}
|
|
2043
|
+
fileIdentity = inspected.fileIdentity;
|
|
1417
2044
|
} catch (error) {
|
|
2045
|
+
if (!fileIdentity) {
|
|
2046
|
+
try {
|
|
2047
|
+
const inspected = inspectWindowsAdapterBoundPath(
|
|
2048
|
+
runtime,
|
|
2049
|
+
payloadPath,
|
|
2050
|
+
"file",
|
|
2051
|
+
{ allowMissing: true },
|
|
2052
|
+
);
|
|
2053
|
+
fileIdentity = inspected.exists ? inspected.identity : null;
|
|
2054
|
+
} catch {
|
|
2055
|
+
fileIdentity = null;
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
if (error?.adapterReason === WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON) {
|
|
2059
|
+
cleanupOrTrackWindowsTemporaryPath(
|
|
2060
|
+
runtime,
|
|
2061
|
+
payloadPath,
|
|
2062
|
+
100,
|
|
2063
|
+
null,
|
|
2064
|
+
fileIdentity,
|
|
2065
|
+
);
|
|
2066
|
+
return { reason: WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON };
|
|
2067
|
+
}
|
|
1418
2068
|
if (error?.code === "EEXIST") {
|
|
1419
2069
|
return { reason: "windows_adapter_invocation_random_path_collision" };
|
|
1420
2070
|
}
|
|
1421
2071
|
return {
|
|
1422
|
-
reason: cleanupOrTrackWindowsTemporaryPath(
|
|
2072
|
+
reason: cleanupOrTrackWindowsTemporaryPath(
|
|
2073
|
+
runtime,
|
|
2074
|
+
payloadPath,
|
|
2075
|
+
100,
|
|
2076
|
+
null,
|
|
2077
|
+
fileIdentity,
|
|
2078
|
+
)
|
|
1423
2079
|
? "windows_adapter_invocation_materialize_failed"
|
|
1424
2080
|
: "windows_adapter_invocation_cleanup_unverified",
|
|
1425
2081
|
};
|
|
@@ -1427,7 +2083,24 @@ function materializeWindowsAdapterInvocation(runtime, helperArgs) {
|
|
|
1427
2083
|
return {
|
|
1428
2084
|
payloadPath,
|
|
1429
2085
|
payloadDigest: sha256(content),
|
|
1430
|
-
|
|
2086
|
+
fileIdentity,
|
|
2087
|
+
verify: () => {
|
|
2088
|
+
const inspected = assertWindowsAdapterBoundPathIdentity(
|
|
2089
|
+
runtime,
|
|
2090
|
+
payloadPath,
|
|
2091
|
+
"file",
|
|
2092
|
+
fileIdentity,
|
|
2093
|
+
);
|
|
2094
|
+
return inspected.exists;
|
|
2095
|
+
},
|
|
2096
|
+
cleanup: () =>
|
|
2097
|
+
cleanupOrTrackWindowsTemporaryPath(
|
|
2098
|
+
runtime,
|
|
2099
|
+
payloadPath,
|
|
2100
|
+
100,
|
|
2101
|
+
null,
|
|
2102
|
+
fileIdentity,
|
|
2103
|
+
),
|
|
1431
2104
|
};
|
|
1432
2105
|
}
|
|
1433
2106
|
|
|
@@ -1483,6 +2156,7 @@ function buildWindowsAdapterInvocation(entry, invocation) {
|
|
|
1483
2156
|
invocation.payloadPath,
|
|
1484
2157
|
invocation.payloadDigest,
|
|
1485
2158
|
],
|
|
2159
|
+
verify: invocation.verify,
|
|
1486
2160
|
cleanup: invocation.cleanup,
|
|
1487
2161
|
};
|
|
1488
2162
|
}
|
|
@@ -1497,6 +2171,7 @@ function buildWindowsAdapterInvocation(entry, invocation) {
|
|
|
1497
2171
|
"-EncodedCommand",
|
|
1498
2172
|
windowsPowerShellBootstrap(entry, invocation),
|
|
1499
2173
|
],
|
|
2174
|
+
verify: invocation.verify,
|
|
1500
2175
|
cleanup: invocation.cleanup,
|
|
1501
2176
|
};
|
|
1502
2177
|
}
|
|
@@ -1583,11 +2258,14 @@ function createFreshWindowsAdapter(runtime, source, hostEnvironment) {
|
|
|
1583
2258
|
runtime,
|
|
1584
2259
|
runtimeFs: runtime.fs,
|
|
1585
2260
|
tempDirectory: source.tempDirectory,
|
|
2261
|
+
tempRootBinding: source.tempRootBinding,
|
|
2262
|
+
idleTtlMs: source.idleTtlMs,
|
|
1586
2263
|
sourceDigest: source.sourceDigest,
|
|
1587
2264
|
sourceContractDigest: source.sourceContractDigest,
|
|
1588
2265
|
loaderMode: source.loaderMode,
|
|
1589
2266
|
powershellPath: powershellHosts[0] || null,
|
|
1590
2267
|
adapterDirectory: materialized.adapterDirectory,
|
|
2268
|
+
adapterDirectoryIdentity: materialized.adapterDirectoryIdentity,
|
|
1591
2269
|
assemblyPath: materialized.assemblyPath,
|
|
1592
2270
|
assemblyDigest: attestation.assemblyDigest,
|
|
1593
2271
|
fileIdentity: attestation.fileIdentity,
|
|
@@ -1743,7 +2421,9 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1743
2421
|
error.adapterReason = payload.reason;
|
|
1744
2422
|
throw error;
|
|
1745
2423
|
}
|
|
1746
|
-
|
|
2424
|
+
const invocation = buildWindowsAdapterInvocation(entry, payload);
|
|
2425
|
+
invocation.verify();
|
|
2426
|
+
return invocation;
|
|
1747
2427
|
};
|
|
1748
2428
|
|
|
1749
2429
|
return {
|
|
@@ -1754,6 +2434,7 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1754
2434
|
let result;
|
|
1755
2435
|
let failure;
|
|
1756
2436
|
try {
|
|
2437
|
+
invocation.verify();
|
|
1757
2438
|
result = runtime.spawnSync(invocation.command, invocation.args, {
|
|
1758
2439
|
...options,
|
|
1759
2440
|
cwd: runtime.joinPath(runtime.windowsDir(), "System32"),
|
|
@@ -1781,20 +2462,40 @@ function createWindowsAdapterController(runtime, source, hostEnvironment) {
|
|
|
1781
2462
|
};
|
|
1782
2463
|
}
|
|
1783
2464
|
|
|
2465
|
+
function readWindowsAdapterBoundFile(runtime, filePath, encoding = null) {
|
|
2466
|
+
const before = inspectWindowsAdapterBoundPath(runtime, filePath, "file");
|
|
2467
|
+
const content = runtime.fs.readFileSync(filePath, encoding || undefined);
|
|
2468
|
+
const after = assertWindowsAdapterBoundPathIdentity(
|
|
2469
|
+
runtime,
|
|
2470
|
+
filePath,
|
|
2471
|
+
"file",
|
|
2472
|
+
before.identity,
|
|
2473
|
+
);
|
|
2474
|
+
if (!sameWindowsFileIdentity(before.identity, after.identity)) {
|
|
2475
|
+
throw windowsAdapterTempRootChangedError();
|
|
2476
|
+
}
|
|
2477
|
+
return { content, fileIdentity: after.identity };
|
|
2478
|
+
}
|
|
2479
|
+
|
|
1784
2480
|
function waitForWindowsTargetIdentity(
|
|
1785
2481
|
proc,
|
|
1786
2482
|
identityPath,
|
|
1787
2483
|
runtime,
|
|
1788
2484
|
expectedAppContainer = null,
|
|
1789
2485
|
timeoutMs = 30_000,
|
|
2486
|
+
onIdentityFileAttested = null,
|
|
1790
2487
|
) {
|
|
1791
2488
|
const deadline = runtime.now() + timeoutMs;
|
|
1792
2489
|
let lastError;
|
|
1793
2490
|
while (runtime.now() < deadline) {
|
|
1794
2491
|
try {
|
|
1795
|
-
const
|
|
1796
|
-
runtime
|
|
2492
|
+
const identityFile = readWindowsAdapterBoundFile(
|
|
2493
|
+
runtime,
|
|
2494
|
+
identityPath,
|
|
2495
|
+
"utf8",
|
|
1797
2496
|
);
|
|
2497
|
+
onIdentityFileAttested?.(identityFile.fileIdentity);
|
|
2498
|
+
const identity = JSON.parse(identityFile.content);
|
|
1798
2499
|
if (identity?.error) {
|
|
1799
2500
|
throw new Error(
|
|
1800
2501
|
`Windows sandbox helper rejected the target: ${identity.error}`,
|
|
@@ -1846,6 +2547,7 @@ function waitForWindowsTargetIdentity(
|
|
|
1846
2547
|
proc.sandboxAppContainerProfile =
|
|
1847
2548
|
expectedAppContainer.appContainerProfileName;
|
|
1848
2549
|
proc.sandboxAppContainerSid = expectedAppContainer.appContainerSid;
|
|
2550
|
+
proc.sandboxAppContainerCapabilityCount = identity.capabilityCount;
|
|
1849
2551
|
}
|
|
1850
2552
|
// ChildProcess.pid is a writable data property. The native ChildProcess
|
|
1851
2553
|
// handle still points at the helper, so kill()/ref()/unref() retain Job
|
|
@@ -2049,6 +2751,7 @@ function appContainerReadinessResult(
|
|
|
2049
2751
|
...runtimeProbe,
|
|
2050
2752
|
runnable: true,
|
|
2051
2753
|
reason: null,
|
|
2754
|
+
capabilityCount: readiness.capabilityCount,
|
|
2052
2755
|
...(targetRuntime
|
|
2053
2756
|
? {
|
|
2054
2757
|
probeRuntime: targetRuntime,
|
|
@@ -2610,11 +3313,33 @@ export function applyWindowsSandbox(
|
|
|
2610
3313
|
) {
|
|
2611
3314
|
return unavailablePlan("windows_git_nested_process_compatibility");
|
|
2612
3315
|
}
|
|
3316
|
+
const windowsAdapterTempRootBinding = resolveWindowsAdapterTempRoot(
|
|
3317
|
+
runtime,
|
|
3318
|
+
runtimeOverrides,
|
|
3319
|
+
);
|
|
3320
|
+
if (!windowsAdapterTempRootBinding) {
|
|
3321
|
+
return unavailablePlan(WINDOWS_ADAPTER_TEMP_ROOT_UNTRUSTED_REASON);
|
|
3322
|
+
}
|
|
3323
|
+
const windowsAdapterIdleTtlMs = normalizeWindowsAdapterIdleTtlMs(runtime);
|
|
3324
|
+
runtime = {
|
|
3325
|
+
...runtime,
|
|
3326
|
+
windowsAdapterTempRoot: windowsAdapterTempRootBinding.rootPath,
|
|
3327
|
+
windowsAdapterTempRootBinding,
|
|
3328
|
+
windowsAdapterIdleTtlMs,
|
|
3329
|
+
tmpdir: () => windowsAdapterTempRootBinding.rootPath,
|
|
3330
|
+
};
|
|
2613
3331
|
const loaderMode =
|
|
2614
3332
|
profile !== "strict" && !requiresAppContainer && !entrySnapshot.locks
|
|
2615
3333
|
? "managed-executable"
|
|
2616
3334
|
: "powershell-byte-assembly";
|
|
2617
|
-
|
|
3335
|
+
let adapterSource;
|
|
3336
|
+
try {
|
|
3337
|
+
adapterSource = loadWindowsAdapterSource(runtime, loaderMode);
|
|
3338
|
+
} catch (error) {
|
|
3339
|
+
return unavailablePlan(
|
|
3340
|
+
error?.adapterReason || WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON,
|
|
3341
|
+
);
|
|
3342
|
+
}
|
|
2618
3343
|
if (!adapterSource.content) {
|
|
2619
3344
|
return unavailablePlan(adapterSource.reason);
|
|
2620
3345
|
}
|
|
@@ -2659,6 +3384,22 @@ export function applyWindowsSandbox(
|
|
|
2659
3384
|
.toString("hex")}.json`,
|
|
2660
3385
|
)
|
|
2661
3386
|
: null;
|
|
3387
|
+
let identityFileIdentity = null;
|
|
3388
|
+
if (identityPath) {
|
|
3389
|
+
try {
|
|
3390
|
+
if (
|
|
3391
|
+
inspectWindowsAdapterBoundPath(runtime, identityPath, "file", {
|
|
3392
|
+
allowMissing: true,
|
|
3393
|
+
}).exists
|
|
3394
|
+
) {
|
|
3395
|
+
return unavailablePlan("windows_identity_random_path_collision");
|
|
3396
|
+
}
|
|
3397
|
+
} catch (error) {
|
|
3398
|
+
return unavailablePlan(
|
|
3399
|
+
error?.adapterReason || WINDOWS_ADAPTER_TEMP_ROOT_CHANGED_REASON,
|
|
3400
|
+
);
|
|
3401
|
+
}
|
|
3402
|
+
}
|
|
2662
3403
|
const launchSpec = {
|
|
2663
3404
|
cpuSeconds: Number(limits.cpu || 0),
|
|
2664
3405
|
processMemoryBytes: Number(
|
|
@@ -2699,7 +3440,13 @@ export function applyWindowsSandbox(
|
|
|
2699
3440
|
if (identityPath) launchSpec.identityPath = identityPath;
|
|
2700
3441
|
const cleanupIdentity = () => {
|
|
2701
3442
|
if (!identityPath) return true;
|
|
2702
|
-
return cleanupOrTrackWindowsTemporaryPath(
|
|
3443
|
+
return cleanupOrTrackWindowsTemporaryPath(
|
|
3444
|
+
runtime,
|
|
3445
|
+
identityPath,
|
|
3446
|
+
100,
|
|
3447
|
+
null,
|
|
3448
|
+
identityFileIdentity,
|
|
3449
|
+
);
|
|
2703
3450
|
};
|
|
2704
3451
|
const appContainerProfileName = requiresAppContainer
|
|
2705
3452
|
? `ChainlessChain.CliSandbox.${runtime.randomBytes(12).toString("hex")}`
|
|
@@ -2973,6 +3720,10 @@ export function applyWindowsSandbox(
|
|
|
2973
3720
|
identityPath,
|
|
2974
3721
|
runtime,
|
|
2975
3722
|
appContainer,
|
|
3723
|
+
30_000,
|
|
3724
|
+
(fileIdentity) => {
|
|
3725
|
+
identityFileIdentity = fileIdentity;
|
|
3726
|
+
},
|
|
2976
3727
|
);
|
|
2977
3728
|
if (!cleanupIdentity()) {
|
|
2978
3729
|
throw new Error(
|
|
@@ -3064,6 +3815,7 @@ export function applyWindowsSandbox(
|
|
|
3064
3815
|
enforcement: backend,
|
|
3065
3816
|
policyAttested: requiresAppContainer ? true : null,
|
|
3066
3817
|
policyDigest: appContainerPolicyDigest,
|
|
3818
|
+
adapterTempRootAttestation: adapterSource.tempRootAttestation,
|
|
3067
3819
|
runtimeProbe: entrySnapshotRuntimeProbe,
|
|
3068
3820
|
guarantees: [
|
|
3069
3821
|
...(requiresAppContainer
|
|
@@ -3130,6 +3882,7 @@ function linuxBubblewrapProbe(
|
|
|
3130
3882
|
supervisorBinding = null,
|
|
3131
3883
|
pluginTreeSnapshot = null,
|
|
3132
3884
|
nativeDynamicClosure = null,
|
|
3885
|
+
runtimeDetachedChildSpawnVerified = false,
|
|
3133
3886
|
) {
|
|
3134
3887
|
const native =
|
|
3135
3888
|
typeof targetRuntime === "string" && targetRuntime.startsWith("native-");
|
|
@@ -3143,13 +3896,12 @@ function linuxBubblewrapProbe(
|
|
|
3143
3896
|
const supervisorDescriptorBound =
|
|
3144
3897
|
supervisorBinding?.mechanism === LINUX_BWRAP_SUPERVISOR_BINDING_MECHANISM;
|
|
3145
3898
|
const pluginTreeContentSnapshot =
|
|
3146
|
-
!native &&
|
|
3147
3899
|
attempted &&
|
|
3148
3900
|
snapshot &&
|
|
3149
3901
|
runnable &&
|
|
3150
3902
|
reason === null &&
|
|
3151
3903
|
supervisorDescriptorBound &&
|
|
3152
|
-
pluginTreeSnapshot?.scope ===
|
|
3904
|
+
pluginTreeSnapshot?.scope === LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE &&
|
|
3153
3905
|
pluginTreeSnapshot?.mechanism === LINUX_ENTRY_SNAPSHOT_MECHANISM &&
|
|
3154
3906
|
Number.isSafeInteger(pluginTreeSnapshot?.files) &&
|
|
3155
3907
|
pluginTreeSnapshot.files > 0 &&
|
|
@@ -3160,7 +3912,7 @@ function linuxBubblewrapProbe(
|
|
|
3160
3912
|
typeof pluginTreeSnapshot?.digest === "string" &&
|
|
3161
3913
|
/^[a-f0-9]{64}$/.test(pluginTreeSnapshot.digest) &&
|
|
3162
3914
|
pluginTreeSnapshot?.consistency ===
|
|
3163
|
-
|
|
3915
|
+
LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY &&
|
|
3164
3916
|
pluginTreeSnapshot?.contractBound === false &&
|
|
3165
3917
|
pluginTreeSnapshot?.atomic === false;
|
|
3166
3918
|
const initialDynamicLoadClosureDescriptorBound =
|
|
@@ -3196,6 +3948,7 @@ function linuxBubblewrapProbe(
|
|
|
3196
3948
|
reason,
|
|
3197
3949
|
probeRuntime: "node",
|
|
3198
3950
|
targetRuntime,
|
|
3951
|
+
runtimeDetachedChildSpawnVerified,
|
|
3199
3952
|
contentSnapshot: snapshot,
|
|
3200
3953
|
...(snapshot
|
|
3201
3954
|
? {
|
|
@@ -3775,16 +4528,20 @@ function linuxEntrySnapshotContract(targetRuntime) {
|
|
|
3775
4528
|
throw new Error("entry_snapshot_runtime_unsupported");
|
|
3776
4529
|
}
|
|
3777
4530
|
|
|
3778
|
-
function
|
|
4531
|
+
function linuxPluginTreeFileSnapshotContract(sourceMode, targetRuntime) {
|
|
4532
|
+
const native = targetRuntime?.startsWith("native-");
|
|
4533
|
+
const errorPrefix = native
|
|
4534
|
+
? "native_plugin_tree_snapshot"
|
|
4535
|
+
: "node_plugin_tree_snapshot";
|
|
3779
4536
|
if (!Number.isSafeInteger(sourceMode) || sourceMode < 0) {
|
|
3780
|
-
throw new Error(
|
|
4537
|
+
throw new Error(`${errorPrefix}_source_mode_invalid`);
|
|
3781
4538
|
}
|
|
3782
4539
|
return {
|
|
3783
|
-
scope:
|
|
4540
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
3784
4541
|
targetMode: (sourceMode & 0o111) !== 0 ? "0500" : "0400",
|
|
3785
4542
|
sourceMode: LINUX_ENTRY_SNAPSHOT_SOURCE_MODE,
|
|
3786
4543
|
minimumBytes: 0,
|
|
3787
|
-
errorPrefix
|
|
4544
|
+
errorPrefix,
|
|
3788
4545
|
};
|
|
3789
4546
|
}
|
|
3790
4547
|
|
|
@@ -3865,9 +4622,9 @@ function createLinuxRegularFileSnapshot(
|
|
|
3865
4622
|
throw snapshotError("source_changed");
|
|
3866
4623
|
}
|
|
3867
4624
|
if (
|
|
3868
|
-
snapshotContract.scope ===
|
|
4625
|
+
snapshotContract.scope === LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE &&
|
|
3869
4626
|
snapshotContract.targetMode !==
|
|
3870
|
-
|
|
4627
|
+
((sourceFileMode & 0o111) !== 0 ? "0500" : "0400")
|
|
3871
4628
|
) {
|
|
3872
4629
|
throw snapshotError("source_mode_changed");
|
|
3873
4630
|
}
|
|
@@ -4328,7 +5085,21 @@ function applyLinuxMcpCapsuleCodeSnapshot(
|
|
|
4328
5085
|
});
|
|
4329
5086
|
}
|
|
4330
5087
|
|
|
4331
|
-
|
|
5088
|
+
/**
|
|
5089
|
+
* Bind the synthetic plugin tree to the anonymous content snapshots mounted
|
|
5090
|
+
* for launch. Directory membership is fixed by pinLinuxPluginTree; contents
|
|
5091
|
+
* are copied and sealed per file, so the evidence deliberately remains
|
|
5092
|
+
* `atomic: false` rather than claiming one filesystem-wide transaction.
|
|
5093
|
+
*/
|
|
5094
|
+
function buildLinuxPluginTreeSnapshotAttestation(
|
|
5095
|
+
mounts,
|
|
5096
|
+
entryDestination,
|
|
5097
|
+
targetRuntime,
|
|
5098
|
+
) {
|
|
5099
|
+
const native = targetRuntime?.startsWith("native-");
|
|
5100
|
+
const errorPrefix = native
|
|
5101
|
+
? "native_plugin_tree_snapshot"
|
|
5102
|
+
: "node_plugin_tree_snapshot";
|
|
4332
5103
|
const members = [];
|
|
4333
5104
|
const destinations = new Set();
|
|
4334
5105
|
for (const mount of mounts || []) {
|
|
@@ -4337,18 +5108,23 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
4337
5108
|
path.posix.normalize(mount.destination) !== mount.destination ||
|
|
4338
5109
|
destinations.has(mount.destination)
|
|
4339
5110
|
) {
|
|
4340
|
-
throw new Error(
|
|
5111
|
+
throw new Error(`${errorPrefix}_destination_invalid`);
|
|
4341
5112
|
}
|
|
4342
5113
|
destinations.add(mount.destination);
|
|
4343
5114
|
const snapshot = mount.contentSnapshot;
|
|
4344
5115
|
const entry = mount.destination === entryDestination;
|
|
4345
5116
|
const expectedScope = entry
|
|
4346
|
-
?
|
|
4347
|
-
|
|
5117
|
+
? native
|
|
5118
|
+
? LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE
|
|
5119
|
+
: LINUX_NODE_ENTRY_SNAPSHOT_SCOPE
|
|
5120
|
+
: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE;
|
|
4348
5121
|
const sourceMode = Number.parseInt(snapshot?.sourceFileMode, 8);
|
|
4349
5122
|
const expectedTargetMode = entry
|
|
4350
|
-
?
|
|
4351
|
-
|
|
5123
|
+
? native
|
|
5124
|
+
? LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE
|
|
5125
|
+
: LINUX_NODE_ENTRY_SNAPSHOT_TARGET_MODE
|
|
5126
|
+
: linuxPluginTreeFileSnapshotContract(sourceMode, targetRuntime)
|
|
5127
|
+
.targetMode;
|
|
4352
5128
|
if (
|
|
4353
5129
|
snapshot?.scope !== expectedScope ||
|
|
4354
5130
|
snapshot?.mechanism !== LINUX_ENTRY_SNAPSHOT_MECHANISM ||
|
|
@@ -4371,7 +5147,7 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
4371
5147
|
mount.mountMode !== "ro-bind-data" ||
|
|
4372
5148
|
mount.mountPermissions !== expectedTargetMode
|
|
4373
5149
|
) {
|
|
4374
|
-
throw new Error(
|
|
5150
|
+
throw new Error(`${errorPrefix}_member_invalid`);
|
|
4375
5151
|
}
|
|
4376
5152
|
members.push({
|
|
4377
5153
|
destination: mount.destination,
|
|
@@ -4398,58 +5174,66 @@ function buildLinuxNodePluginTreeSnapshotAttestation(mounts, entryDestination) {
|
|
|
4398
5174
|
members.length > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES ||
|
|
4399
5175
|
!members.some((member) => member.destination === entryDestination)
|
|
4400
5176
|
) {
|
|
4401
|
-
throw new Error(
|
|
5177
|
+
throw new Error(`${errorPrefix}_file_count_invalid`);
|
|
4402
5178
|
}
|
|
4403
5179
|
const bytes = members.reduce((total, member) => total + member.bytes, 0);
|
|
4404
5180
|
if (
|
|
4405
5181
|
!Number.isSafeInteger(bytes) ||
|
|
4406
5182
|
bytes > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES
|
|
4407
5183
|
) {
|
|
4408
|
-
throw new Error(
|
|
5184
|
+
throw new Error(`${errorPrefix}_bytes_exceeded`);
|
|
4409
5185
|
}
|
|
4410
5186
|
const digest = crypto
|
|
4411
5187
|
.createHash("sha256")
|
|
4412
5188
|
.update(
|
|
4413
5189
|
JSON.stringify({
|
|
4414
5190
|
version: 1,
|
|
4415
|
-
scope:
|
|
4416
|
-
|
|
5191
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
5192
|
+
...(native ? { targetRuntime } : {}),
|
|
5193
|
+
consistency: LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY,
|
|
4417
5194
|
members,
|
|
4418
5195
|
}),
|
|
4419
5196
|
)
|
|
4420
5197
|
.digest("hex");
|
|
4421
5198
|
return Object.freeze({
|
|
4422
|
-
scope:
|
|
5199
|
+
scope: LINUX_PLUGIN_TREE_SNAPSHOT_SCOPE,
|
|
4423
5200
|
mechanism: LINUX_ENTRY_SNAPSHOT_MECHANISM,
|
|
4424
5201
|
files: members.length,
|
|
4425
5202
|
bytes,
|
|
4426
5203
|
digest,
|
|
4427
|
-
consistency:
|
|
5204
|
+
consistency: LINUX_PLUGIN_TREE_SNAPSHOT_CONSISTENCY,
|
|
4428
5205
|
contractBound: false,
|
|
4429
5206
|
atomic: false,
|
|
4430
5207
|
});
|
|
4431
5208
|
}
|
|
4432
5209
|
|
|
4433
|
-
function
|
|
5210
|
+
function attestLinuxPluginTreeSnapshot(
|
|
4434
5211
|
runtime,
|
|
4435
5212
|
mounts,
|
|
4436
5213
|
entryDestination,
|
|
5214
|
+
targetRuntime,
|
|
4437
5215
|
expected,
|
|
4438
5216
|
) {
|
|
5217
|
+
const native = targetRuntime?.startsWith("native-");
|
|
5218
|
+
const errorPrefix = native
|
|
5219
|
+
? "native_plugin_tree_snapshot"
|
|
5220
|
+
: "node_plugin_tree_snapshot";
|
|
4439
5221
|
for (const mount of mounts || []) {
|
|
4440
5222
|
if (!linuxPathWithin("/opt/chainless/plugin", mount?.destination)) continue;
|
|
4441
5223
|
const snapshot = mount.contentSnapshot;
|
|
4442
5224
|
const contract =
|
|
4443
5225
|
mount.destination === entryDestination
|
|
4444
|
-
? linuxEntrySnapshotContract(
|
|
4445
|
-
:
|
|
5226
|
+
? linuxEntrySnapshotContract(targetRuntime)
|
|
5227
|
+
: linuxPluginTreeFileSnapshotContract(
|
|
4446
5228
|
Number.parseInt(snapshot?.sourceFileMode, 8),
|
|
5229
|
+
targetRuntime,
|
|
4447
5230
|
);
|
|
4448
5231
|
attestLinuxRegularFileSnapshot(runtime, mount, snapshot, contract);
|
|
4449
5232
|
}
|
|
4450
|
-
const actual =
|
|
5233
|
+
const actual = buildLinuxPluginTreeSnapshotAttestation(
|
|
4451
5234
|
mounts,
|
|
4452
5235
|
entryDestination,
|
|
5236
|
+
targetRuntime,
|
|
4453
5237
|
);
|
|
4454
5238
|
for (const field of [
|
|
4455
5239
|
"scope",
|
|
@@ -4462,7 +5246,7 @@ function attestLinuxNodePluginTreeSnapshot(
|
|
|
4462
5246
|
"atomic",
|
|
4463
5247
|
]) {
|
|
4464
5248
|
if (actual[field] !== expected?.[field]) {
|
|
4465
|
-
throw new Error(
|
|
5249
|
+
throw new Error(`${errorPrefix}_identity_changed`);
|
|
4466
5250
|
}
|
|
4467
5251
|
}
|
|
4468
5252
|
}
|
|
@@ -6014,8 +6798,22 @@ function probeLinuxBubblewrapPolicy(
|
|
|
6014
6798
|
ino: supervisorBinding.fileId.ino,
|
|
6015
6799
|
bytes: supervisorBinding.bytes,
|
|
6016
6800
|
});
|
|
6801
|
+
const childProbeSource = [
|
|
6802
|
+
'const fs = require("node:fs");',
|
|
6803
|
+
"const sentinel = process.argv[1];",
|
|
6804
|
+
'const raw = fs.readFileSync("/proc/self/stat", "utf8");',
|
|
6805
|
+
'const close = raw.lastIndexOf(")");',
|
|
6806
|
+
'const pid = Number(raw.slice(0, raw.indexOf(" ")));',
|
|
6807
|
+
"const fields = raw.slice(close + 2).trim().split(/\\s+/);",
|
|
6808
|
+
"const processGroupPid = Number(fields[2]);",
|
|
6809
|
+
"const sessionPid = Number(fields[3]);",
|
|
6810
|
+
"fs.writeSync(1, JSON.stringify({ sentinel, pid, processGroupPid, sessionPid }));",
|
|
6811
|
+
"if (processGroupPid !== pid || sessionPid !== pid) process.exit(2);",
|
|
6812
|
+
].join("\n");
|
|
6017
6813
|
const probeSource = [
|
|
6018
6814
|
'const fs = require("node:fs");',
|
|
6815
|
+
'const crypto = require("node:crypto");',
|
|
6816
|
+
'const { spawnSync } = require("node:child_process");',
|
|
6019
6817
|
`const expected = ${supervisorIdentity};`,
|
|
6020
6818
|
'for (const name of fs.readdirSync("/proc/self/fd")) {',
|
|
6021
6819
|
" let current;",
|
|
@@ -6032,10 +6830,62 @@ function probeLinuxBubblewrapPolicy(
|
|
|
6032
6830
|
"} catch (error) {",
|
|
6033
6831
|
' if (error?.code !== "ENOENT") throw error;',
|
|
6034
6832
|
"}",
|
|
6833
|
+
"// /tmp is a fresh private bwrap tmpfs; read the same O_EXCL inode by fd.",
|
|
6834
|
+
'const childReportPath = "/tmp/.chainless-runtime-child-probe.json";',
|
|
6835
|
+
`const expectedChildSentinel = ${JSON.stringify(
|
|
6836
|
+
`${LINUX_BWRAP_CHILD_RUNTIME_PROBE_SENTINEL}:`,
|
|
6837
|
+
)} + crypto.randomBytes(16).toString("hex");`,
|
|
6838
|
+
"let reportFd;",
|
|
6839
|
+
"let runtimeDetachedChildSpawnVerified = false;",
|
|
6840
|
+
"try {",
|
|
6841
|
+
' reportFd = fs.openSync(childReportPath, "wx+", 0o600);',
|
|
6842
|
+
` const child = spawnSync("/opt/chainless/runtime/node", ["-e", ${JSON.stringify(
|
|
6843
|
+
childProbeSource,
|
|
6844
|
+
)}, expectedChildSentinel], {`,
|
|
6845
|
+
' cwd: "/",',
|
|
6846
|
+
" detached: true,",
|
|
6847
|
+
" env: process.env,",
|
|
6848
|
+
" shell: false,",
|
|
6849
|
+
' stdio: ["ignore", reportFd, "ignore"],',
|
|
6850
|
+
" timeout: 5_000,",
|
|
6851
|
+
" });",
|
|
6852
|
+
" const reportStat = fs.fstatSync(reportFd);",
|
|
6853
|
+
" const reportBytes = Number(reportStat.size);",
|
|
6854
|
+
" if (!child.error && child.status === 0 && child.signal === null &&",
|
|
6855
|
+
" Number.isSafeInteger(child.pid) && child.pid > 0 &&",
|
|
6856
|
+
" reportStat.isFile() && Number.isSafeInteger(reportBytes) &&",
|
|
6857
|
+
" reportBytes > 0 && reportBytes <= 1_024) {",
|
|
6858
|
+
" const reportBuffer = Buffer.alloc(reportBytes);",
|
|
6859
|
+
" const reportRead = fs.readSync(reportFd, reportBuffer, 0, reportBytes, 0);",
|
|
6860
|
+
" if (reportRead === reportBytes) {",
|
|
6861
|
+
' const reportText = reportBuffer.toString("utf8");',
|
|
6862
|
+
" const report = JSON.parse(reportText);",
|
|
6863
|
+
" const canonicalReport = JSON.stringify({ sentinel: report.sentinel,",
|
|
6864
|
+
" pid: report.pid, processGroupPid: report.processGroupPid,",
|
|
6865
|
+
" sessionPid: report.sessionPid });",
|
|
6866
|
+
" runtimeDetachedChildSpawnVerified = reportText === canonicalReport &&",
|
|
6867
|
+
" report.sentinel === expectedChildSentinel &&",
|
|
6868
|
+
" Number.isSafeInteger(report.pid) && report.pid > 0 &&",
|
|
6869
|
+
" report.pid === child.pid && report.processGroupPid === report.pid &&",
|
|
6870
|
+
" report.sessionPid === report.pid;",
|
|
6871
|
+
" }",
|
|
6872
|
+
" }",
|
|
6873
|
+
"} catch {} finally {",
|
|
6874
|
+
" try { if (Number.isInteger(reportFd)) fs.closeSync(reportFd); } catch {",
|
|
6875
|
+
" runtimeDetachedChildSpawnVerified = false;",
|
|
6876
|
+
" }",
|
|
6877
|
+
" try { fs.unlinkSync(childReportPath); } catch {",
|
|
6878
|
+
" runtimeDetachedChildSpawnVerified = false;",
|
|
6879
|
+
" }",
|
|
6880
|
+
"}",
|
|
6881
|
+
`if (!runtimeDetachedChildSpawnVerified) process.exit(${LINUX_BWRAP_CHILD_RUNTIME_PROBE_FAILURE_STATUS});`,
|
|
6035
6882
|
`process.stdout.write(${JSON.stringify(LINUX_BWRAP_NODE_PROBE_SENTINEL)});`,
|
|
6036
6883
|
].join("\n");
|
|
6037
6884
|
let result;
|
|
6038
6885
|
try {
|
|
6886
|
+
// These pipes connect the host Broker to bwrap and are created before
|
|
6887
|
+
// bwrap installs the target seccomp filter. The nested runtime probe above
|
|
6888
|
+
// inherits a regular-file descriptor and never requests UV_CREATE_PIPE.
|
|
6039
6889
|
result = runtime.spawnSync(
|
|
6040
6890
|
supervisorLaunch.command,
|
|
6041
6891
|
[...policyArgs, "--", "/opt/chainless/runtime/node", "-e", probeSource],
|
|
@@ -6069,7 +6919,11 @@ function probeLinuxBubblewrapPolicy(
|
|
|
6069
6919
|
return linuxBubblewrapProbe(
|
|
6070
6920
|
true,
|
|
6071
6921
|
false,
|
|
6072
|
-
result?.error
|
|
6922
|
+
result?.error
|
|
6923
|
+
? "probe_spawn_failed"
|
|
6924
|
+
: result?.status === LINUX_BWRAP_CHILD_RUNTIME_PROBE_FAILURE_STATUS
|
|
6925
|
+
? "detached_child_runtime_probe_failed"
|
|
6926
|
+
: "probe_failed",
|
|
6073
6927
|
targetRuntime,
|
|
6074
6928
|
contentSnapshot,
|
|
6075
6929
|
supervisorBinding,
|
|
@@ -6092,6 +6946,9 @@ function probeLinuxBubblewrapPolicy(
|
|
|
6092
6946
|
targetRuntime,
|
|
6093
6947
|
contentSnapshot,
|
|
6094
6948
|
supervisorBinding,
|
|
6949
|
+
null,
|
|
6950
|
+
null,
|
|
6951
|
+
true,
|
|
6095
6952
|
);
|
|
6096
6953
|
}
|
|
6097
6954
|
|
|
@@ -6360,30 +7217,33 @@ export function applyLinuxSandbox(
|
|
|
6360
7217
|
const rawPluginMounts = pluginMountIndexes.map(
|
|
6361
7218
|
(index) => pinnedMounts[index],
|
|
6362
7219
|
);
|
|
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);
|
|
7220
|
+
const pluginSnapshotErrorPrefix = validation.entryRuntime.startsWith(
|
|
7221
|
+
"native-",
|
|
7222
|
+
)
|
|
7223
|
+
? "native_plugin_tree_snapshot"
|
|
7224
|
+
: "node_plugin_tree_snapshot";
|
|
7225
|
+
if (
|
|
7226
|
+
pluginMountIndexes.length < 1 ||
|
|
7227
|
+
pluginMountIndexes.length > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_FILES
|
|
7228
|
+
) {
|
|
7229
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_file_count_invalid`);
|
|
7230
|
+
}
|
|
7231
|
+
const pluginBytes = pluginMountIndexes.reduce((total, index) => {
|
|
7232
|
+
const bytes = pinnedMounts[index]?.bytes;
|
|
6381
7233
|
if (
|
|
6382
|
-
!Number.isSafeInteger(
|
|
6383
|
-
|
|
7234
|
+
!Number.isSafeInteger(bytes) ||
|
|
7235
|
+
bytes < 0 ||
|
|
7236
|
+
bytes > LINUX_ATTESTED_FILE_MAX_BYTES
|
|
6384
7237
|
) {
|
|
6385
|
-
throw new Error(
|
|
7238
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_member_size_invalid`);
|
|
6386
7239
|
}
|
|
7240
|
+
return total + bytes;
|
|
7241
|
+
}, 0);
|
|
7242
|
+
if (
|
|
7243
|
+
!Number.isSafeInteger(pluginBytes) ||
|
|
7244
|
+
pluginBytes > LINUX_PLUGIN_TREE_SNAPSHOT_MAX_BYTES
|
|
7245
|
+
) {
|
|
7246
|
+
throw new Error(`${pluginSnapshotErrorPrefix}_bytes_exceeded`);
|
|
6387
7247
|
}
|
|
6388
7248
|
const rawEntryMount = entryMount;
|
|
6389
7249
|
const snapshot = createLinuxEntrySnapshot(
|
|
@@ -6397,37 +7257,36 @@ export function applyLinuxSandbox(
|
|
|
6397
7257
|
finalMounts[entryIndex] = snapshot.finalMount;
|
|
6398
7258
|
probeMounts[entryIndex] = snapshot.probeMount;
|
|
6399
7259
|
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,
|
|
7260
|
+
for (const index of pluginMountIndexes) {
|
|
7261
|
+
if (index === entryIndex) continue;
|
|
7262
|
+
const rawMount = pinnedMounts[index];
|
|
7263
|
+
const source = runtime.fs.fstatSync(rawMount.fd);
|
|
7264
|
+
const fileSnapshot = createLinuxRegularFileSnapshot(
|
|
7265
|
+
runtime,
|
|
7266
|
+
rawMount,
|
|
7267
|
+
{
|
|
7268
|
+
fileId: rawMount.fileId,
|
|
7269
|
+
bytes: rawMount.bytes,
|
|
7270
|
+
mtimeMs: rawMount.mtimeMs,
|
|
7271
|
+
},
|
|
7272
|
+
linuxPluginTreeFileSnapshotContract(
|
|
7273
|
+
Number(source.mode),
|
|
7274
|
+
validation.entryRuntime,
|
|
7275
|
+
),
|
|
6422
7276
|
);
|
|
7277
|
+
pluginTreeFileSnapshots.push(fileSnapshot);
|
|
7278
|
+
finalMounts[index] = fileSnapshot.finalMount;
|
|
7279
|
+
probeMounts[index] = fileSnapshot.probeMount;
|
|
6423
7280
|
}
|
|
7281
|
+
pluginTreeSnapshot = buildLinuxPluginTreeSnapshotAttestation(
|
|
7282
|
+
finalMounts,
|
|
7283
|
+
sandboxEntry,
|
|
7284
|
+
validation.entryRuntime,
|
|
7285
|
+
);
|
|
6424
7286
|
pinnedMounts = finalMounts;
|
|
6425
7287
|
probePinnedMounts = probeMounts;
|
|
6426
7288
|
entryMount = entrySnapshot.finalMount;
|
|
6427
|
-
closeLinuxPinnedMounts(
|
|
6428
|
-
runtime,
|
|
6429
|
-
validation.entryRuntime === "node" ? rawPluginMounts : [rawEntryMount],
|
|
6430
|
-
);
|
|
7289
|
+
closeLinuxPinnedMounts(runtime, rawPluginMounts);
|
|
6431
7290
|
} catch (error) {
|
|
6432
7291
|
closeLinuxPinnedMounts(runtime, pinnedMounts);
|
|
6433
7292
|
closeLinuxPinnedMounts(runtime, [
|
|
@@ -6607,7 +7466,7 @@ export function applyLinuxSandbox(
|
|
|
6607
7466
|
.createHash("sha256")
|
|
6608
7467
|
.update(
|
|
6609
7468
|
JSON.stringify({
|
|
6610
|
-
version:
|
|
7469
|
+
version: 4,
|
|
6611
7470
|
backend: LINUX_BWRAP_BACKEND,
|
|
6612
7471
|
supervisor: supervisorPin.attestation,
|
|
6613
7472
|
contract: {
|
|
@@ -6651,9 +7510,7 @@ export function applyLinuxSandbox(
|
|
|
6651
7510
|
...(nativeDynamicClosure
|
|
6652
7511
|
? { initialDynamicLoadClosure: nativeDynamicClosure }
|
|
6653
7512
|
: {}),
|
|
6654
|
-
|
|
6655
|
-
? { pluginTreeContentSnapshot: pluginTreeSnapshot }
|
|
6656
|
-
: {}),
|
|
7513
|
+
pluginTreeContentSnapshot: pluginTreeSnapshot,
|
|
6657
7514
|
seccomp: {
|
|
6658
7515
|
arch: seccompFilter.arch,
|
|
6659
7516
|
policy: seccompFilter.policy,
|
|
@@ -6726,21 +7583,13 @@ export function applyLinuxSandbox(
|
|
|
6726
7583
|
});
|
|
6727
7584
|
}
|
|
6728
7585
|
try {
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
} else {
|
|
6737
|
-
attestLinuxEntrySnapshot(
|
|
6738
|
-
runtime,
|
|
6739
|
-
entryMount,
|
|
6740
|
-
entrySnapshot.attestation,
|
|
6741
|
-
validation.entryRuntime,
|
|
6742
|
-
);
|
|
6743
|
-
}
|
|
7586
|
+
attestLinuxPluginTreeSnapshot(
|
|
7587
|
+
runtime,
|
|
7588
|
+
pinnedMounts,
|
|
7589
|
+
sandboxEntry,
|
|
7590
|
+
validation.entryRuntime,
|
|
7591
|
+
pluginTreeSnapshot,
|
|
7592
|
+
);
|
|
6744
7593
|
} catch (error) {
|
|
6745
7594
|
closeStrongLinuxResources(supervisorPin, finalLaunch);
|
|
6746
7595
|
return createSandboxPlan({
|
|
@@ -6819,6 +7668,7 @@ export function applyLinuxSandbox(
|
|
|
6819
7668
|
supervisorBinding,
|
|
6820
7669
|
pluginTreeSnapshot,
|
|
6821
7670
|
nativeDynamicClosure,
|
|
7671
|
+
policyProbe.runtimeDetachedChildSpawnVerified === true,
|
|
6822
7672
|
);
|
|
6823
7673
|
const runtimeProbe = Object.freeze(
|
|
6824
7674
|
capsuleContract
|
|
@@ -6961,12 +7811,17 @@ export function applySandbox(
|
|
|
6961
7811
|
explicitRequest,
|
|
6962
7812
|
);
|
|
6963
7813
|
const profileName = sandboxRequest.profile;
|
|
7814
|
+
// Windows does not consume the POSIX allow-write lists. Avoid invoking an
|
|
7815
|
+
// embedding temp-directory hook while eagerly constructing three unused
|
|
7816
|
+
// profiles; applyWindowsSandbox resolves and pins that hook exactly once.
|
|
7817
|
+
const profileTempDirectory =
|
|
7818
|
+
runtime.platform === "win32" ? null : runtime.tmpdir();
|
|
6964
7819
|
const profiles = {
|
|
6965
7820
|
default: {
|
|
6966
7821
|
allowNetwork: false,
|
|
6967
7822
|
allowExec: true,
|
|
6968
7823
|
allowRead: [runtime.homedir(), "/tmp"],
|
|
6969
|
-
allowWrite: [
|
|
7824
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6970
7825
|
limits: {
|
|
6971
7826
|
// RLIMIT_NPROC is intentionally omitted. Linux applies it per real
|
|
6972
7827
|
// user ID rather than to this child tree, so it can starve unrelated
|
|
@@ -6979,7 +7834,7 @@ export function applySandbox(
|
|
|
6979
7834
|
allowNetwork: false,
|
|
6980
7835
|
allowExec: false,
|
|
6981
7836
|
allowRead: [],
|
|
6982
|
-
allowWrite: [
|
|
7837
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6983
7838
|
limits: {
|
|
6984
7839
|
cpu: 10,
|
|
6985
7840
|
as: 256 * 1024 * 1024, // 256MB address space
|
|
@@ -6990,7 +7845,7 @@ export function applySandbox(
|
|
|
6990
7845
|
allowNetwork: true,
|
|
6991
7846
|
allowExec: true,
|
|
6992
7847
|
allowRead: [runtime.homedir()],
|
|
6993
|
-
allowWrite: [
|
|
7848
|
+
allowWrite: profileTempDirectory ? [profileTempDirectory] : [],
|
|
6994
7849
|
limits: {
|
|
6995
7850
|
cpu: 60,
|
|
6996
7851
|
nofile: 512,
|