chainlesschain 0.162.183 → 0.162.185

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.
Files changed (205) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-BapS7lz4.js → AIOps-Co4ipAKx.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-BpsoDrDq.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-DMUqeBik.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-C6fD_dXr.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DW0YlQlV.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit-DXtHiiFK.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-BgmF62U-.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-D8vhVLQC.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-D7nCtY2X.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-CBZLEV2G.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CI_jT0ru.js → ChatBubbleRenderer-CtDb-2Qu.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-DJLkeD4M.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-Cs6xO8Fd.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col-Dtx9wxfT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-DiigJg4n.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-B67oMDDL.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-BBDl-Jos.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-Ba98hSsa.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-DBWQZIz7.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-OcrKzxAt.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-mW2eSbjM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-BmsDPmKJ.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-CVzmCyRx.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-vVgqKzhh.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-CWY5t_ZJ.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-ZGomhjxr.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-DyaknbLr.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-CmIvN1qr.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-DQOJIYkG.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CMiY-EEp.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-D6IXuucf.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-6jkTL0hV.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-N0jD-349.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-BL1IVXm-.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-WhYuhBxm.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-DTeO28xy.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-MSHzPSE-.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-Cyqs3J3W.js} +1 -1
  41. package/src/assets/web-panel/assets/MobileProjects-7IN2hWHg.js +1 -0
  42. package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-MWBnezNM.js} +5 -5
  43. package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-DQu35Nu-.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-CQS_M-DP.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-WD16vQvY.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-DqsiSwOq.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.js → NotificationSettings-D3LoxO-P.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DdcsOqEz.js → OrderTableRenderer-C5jPoTd6.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BFDTAUgS.js → Organization-48aMBJso.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-j-a2XkhR.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-BYpBNYnh.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-CYCsrrLg.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-ZRXGXDFb.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-6OWUPHyL.js} +5 -5
  55. package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-FcjlDK9E.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-vD_6Tldp.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-B_CHOLXp.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-rEiqQ2da.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-Bg8obRAM.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-Dgtt97B4.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-DaGVylZO.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DP2_2x3u.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-D29yj4ki.js} +1 -1
  64. package/src/assets/web-panel/assets/RemoteSession-Ue2wnBLp.js +5 -0
  65. package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CrxIHqeC.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-BgSzJPCn.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-AwhYOrP9.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-D2shpEcm.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-BtKzhTbT.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-BY0DsUSm.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.js → Skeleton-B3Sb0UI9.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-DtT6rWOX.js → Skills-Ne5w0bDs.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-0B37mxVU.js → Sla-C8VkaP4T.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-BQdIEYP9.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-CwFafrUg.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-yJuKoDlR.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-DYn9qCmx.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant--17nwVNZ.js → Tenant-DkKReHvn.js} +1 -1
  79. package/src/assets/web-panel/assets/Terminal-r6bd-owt.js +3 -0
  80. package/src/assets/web-panel/assets/{TimelineRenderer-C5jx4AE3.js → TimelineRenderer-Mq2juUC3.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-BnMw7MRi.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-CTzKp4o9.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-BBrSZm9U.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-D9fDvEMq.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-DUi5ve-0.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-C8Fi1P8_.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-s1LppYi2.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-DJL2C4lj.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-D3F2ndOB.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-B7739-e3.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-CcJbFTF4.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-C326bjex.js → createContext-CuP_DEFS.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-Cj_36GHq.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-D_INJQT_.js → hasIn-6YEXfIik.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-4h3lpWJ3.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-BIID4XcP.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BOHUo3aM.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-BYYSLEjn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-Bh4hcNWW.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C327pLbw.js → index-BhIKnQAW.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-Bru_kpZl.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-BsdSnHQE.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-jYaogFE8.js → index-BxSqVMGh.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-C3ZA_h1j.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-C3pctdCb.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-CRuoRapN.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-qMA4GYdQ.js → index-CY0zd_jc.js} +1 -1
  108. package/src/assets/web-panel/assets/index-CezFrAmD.js +1 -0
  109. package/src/assets/web-panel/assets/index-Cxgguez6.js +1 -0
  110. package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-CxradkS6.js} +19 -19
  111. package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-D0K_rFC1.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-D0zdPyTD.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-D3haorUt.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-D6Ij_2uw.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-DDRlBaDt.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-C-ZCaUJc.js → index-DGsE7MQz.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-DHvxXXNA.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DV11jpeO.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-DXdnfDVK.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-D_75jYWk.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-DjI9MkId.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-DlSvpNMf.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dox44j29.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-DpEKmjoa.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Dsy7_x4s.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-Gxw09e9R.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-LfRlSmqe.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-Nz-MUHD1.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DUU2606o.js → index-b9cuvFEv.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-eR6H6NR7.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-fWgUQZ9d.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-mfWQRSLn.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DNMxIsDg.js → index-yOZYSNmG.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-CJ1Bq2L3.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-rYJN_vzf.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-DnDK2gTP.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-CKnXy7ES.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-3nRc3hWT.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Ddjj3sLu.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-CbsbphQY.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-D9aZ4VDJ.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-D4pvdFj0.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-C2W-vY-S.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-Cbq5150d.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-BA2B7R82.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CLrwg3xg.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-DQeAI5qs.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-8BDGy-YU.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-C_IMGueq.js → zoom-B2SzFprl.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/commands/agenda.js +74 -1
  152. package/src/commands/background-session.js +11 -1
  153. package/src/commands/batch.js +160 -5
  154. package/src/commands/plugin.js +90 -27
  155. package/src/commands/session.js +31 -1
  156. package/src/commands/team.js +212 -6
  157. package/src/data/changelog.json +26 -0
  158. package/src/gateways/terminal/PtyManager.js +45 -15
  159. package/src/gateways/ws/ws-agent-handler.js +15 -1
  160. package/src/gateways/ws/ws-session-gateway.js +88 -0
  161. package/src/harness/background-task-command-runner.js +234 -0
  162. package/src/harness/background-task-manager.js +150 -20
  163. package/src/harness/background-task-worker.js +17 -11
  164. package/src/harness/jsonl-session-store.js +57 -6
  165. package/src/harness/mcp-client.js +65 -43
  166. package/src/lib/agent-batch.js +4 -1
  167. package/src/lib/agent-team/team-worktree.js +3 -0
  168. package/src/lib/async-hook-supervisor.cjs +82 -10
  169. package/src/lib/async-hook-supervisor.js +50 -4
  170. package/src/lib/background-agent-supervisor.js +46 -0
  171. package/src/lib/collaboration-run-store.js +362 -0
  172. package/src/lib/event-runtime-host.js +12 -10
  173. package/src/lib/hook-runner.cjs +105 -18
  174. package/src/lib/hook-runner.js +79 -6
  175. package/src/lib/hook-shell-command.cjs +148 -0
  176. package/src/lib/hooks-v2-runtime.js +280 -81
  177. package/src/lib/hooks-v2-workspace-context.js +258 -0
  178. package/src/lib/jsonl-session-store.js +2 -0
  179. package/src/lib/lsp/__tests__/lsp-client.test.js +15 -0
  180. package/src/lib/lsp/lsp-client.js +12 -1
  181. package/src/lib/plugin-monitor-supervisor.js +53 -16
  182. package/src/lib/plugin-runtime/bin.js +21 -5
  183. package/src/lib/plugin-runtime/install.js +249 -57
  184. package/src/lib/plugin-runtime/mcp.js +21 -2
  185. package/src/lib/plugin-runtime/monitors.js +19 -3
  186. package/src/lib/plugin-runtime/sandbox-policy.js +100 -4
  187. package/src/lib/process-execution-broker/index.js +1151 -44
  188. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1557 -0
  189. package/src/lib/process-execution-broker/linux-generic-bwrap.js +1378 -0
  190. package/src/lib/process-execution-broker/platform-sandbox.js +586 -58
  191. package/src/lib/session-usage.js +202 -2
  192. package/src/lib/stream-retry.js +29 -0
  193. package/src/repl/agent-repl.js +40 -3
  194. package/src/runtime/agent-core.js +304 -42
  195. package/src/runtime/headless-runner.js +48 -1
  196. package/src/runtime/headless-stream.js +42 -2
  197. package/src/workers/background-agent-worker.js +5 -11
  198. package/src/assets/web-panel/assets/MarkdownRenderer-C0o4FtpD.js +0 -1
  199. package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
  200. package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
  201. package/src/assets/web-panel/assets/Tasks-D-R1x8vm.js +0 -1
  202. package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
  203. package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
  204. package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
  205. package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
@@ -4,10 +4,11 @@
4
4
  * Platform enforcement currently available:
5
5
  * - macOS: Seatbelt sandbox-exec profiles
6
6
  * - Linux: prlimit resource-limit wrapper, plus a narrow bubblewrap backend
7
- * for an attested direct strict Plugin Node bin or static ELF native bin.
8
- * Shells, dynamically linked binaries, broader working directories,
9
- * host-writable paths, inherited descriptors, and network egress remain
10
- * fail-closed.
7
+ * for an attested direct strict Plugin Node bin or a narrow static/dynamic
8
+ * ELF native bin. Dynamic acceptance binds PT_INTERP plus the entry's direct
9
+ * DT_NEEDED names to an attested system-file set; broader loader surfaces,
10
+ * shells, broader working directories, host-writable paths, inherited
11
+ * descriptors, and network egress remain fail-closed.
11
12
  * - Windows: Win32 Job Object + restricted-token wrapper, with an attested
12
13
  * zero-capability AppContainer for explicit filesystem/network boundaries
13
14
  *
@@ -25,6 +26,10 @@ import * as fs from "node:fs";
25
26
  import crypto from "node:crypto";
26
27
  import { spawnSync as nativeSpawnSync } from "node:child_process";
27
28
  import { fileURLToPath } from "node:url";
29
+ import {
30
+ applyLinuxGenericWorkspaceSandbox,
31
+ isLinuxGenericWorkspaceContract,
32
+ } from "./linux-generic-bwrap-runtime.js";
28
33
 
29
34
  /**
30
35
  * Stable boundary identifiers shared by broker policies, platform plans, and
@@ -169,6 +174,15 @@ const LINUX_ELF64_PROGRAM_HEADER_BYTES = 56;
169
174
  const LINUX_ELF64_DYNAMIC_ENTRY_BYTES = 16;
170
175
  const LINUX_ELF_MAX_PROGRAM_HEADERS = 128;
171
176
  const LINUX_ELF_MAX_DYNAMIC_ENTRIES = 4096;
177
+ const LINUX_ELF_MAX_NEEDED_ENTRIES = 128;
178
+ const LINUX_ELF_MAX_INTERPRETER_BYTES = 4096;
179
+ const LINUX_ELF_MAX_STRING_TABLE_BYTES = 1024 * 1024;
180
+ const LINUX_AUXV_ENTRY_BYTES = 16;
181
+ const LINUX_AUXV_MAX_BYTES = 64 * 1024;
182
+ const LINUX_AUXV_PAGE_SIZE = 6n;
183
+ const LINUX_MIN_RUNTIME_PAGE_BYTES = 4096;
184
+ const LINUX_MAX_RUNTIME_PAGE_BYTES = 1024 * 1024;
185
+ const LINUX_ELF_MAX_UINT64 = (1n << 64n) - 1n;
172
186
  const LINUX_ELF_TYPE_EXEC = 2;
173
187
  const LINUX_ELF_TYPE_DYN = 3;
174
188
  const LINUX_ELF_PROGRAM_LOAD = 1;
@@ -177,8 +191,24 @@ const LINUX_ELF_PROGRAM_INTERP = 3;
177
191
  const LINUX_ELF_PROGRAM_GNU_STACK = 0x6474e551;
178
192
  const LINUX_ELF_DYNAMIC_NULL = 0n;
179
193
  const LINUX_ELF_DYNAMIC_NEEDED = 1n;
194
+ const LINUX_ELF_DYNAMIC_STRTAB = 5n;
195
+ const LINUX_ELF_DYNAMIC_STRSZ = 10n;
196
+ const LINUX_ELF_DYNAMIC_RPATH = 15n;
197
+ const LINUX_ELF_DYNAMIC_TEXTREL = 22n;
198
+ const LINUX_ELF_DYNAMIC_FLAGS = 30n;
199
+ const LINUX_ELF_DYNAMIC_RUNPATH = 29n;
200
+ const LINUX_ELF_DYNAMIC_CONFIG = 0x6ffffefan;
201
+ const LINUX_ELF_DYNAMIC_DEPAUDIT = 0x6ffffefbn;
202
+ const LINUX_ELF_DYNAMIC_AUDIT = 0x6ffffefcn;
180
203
  const LINUX_ELF_DYNAMIC_FLAGS_1 = 0x6ffffffbn;
204
+ const LINUX_ELF_DYNAMIC_AUXILIARY = 0x7ffffffdn;
205
+ const LINUX_ELF_DYNAMIC_FILTER = 0x7fffffffn;
206
+ const LINUX_ELF_DYNAMIC_FLAG_TEXTREL = 0x4n;
181
207
  const LINUX_ELF_DYNAMIC_FLAG_PIE = 0x08000000n;
208
+ const LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_SCOPE =
209
+ "initial-pt_interp-and-direct-dt_needed-attested-system-set";
210
+ const LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_MECHANISM =
211
+ "parsed-elf-direct-system-set-to-attested-node-runtime-fds-v1";
182
212
  const LINUX_ELF_MACHINES = Object.freeze({
183
213
  x64: 62,
184
214
  arm64: 183,
@@ -291,6 +321,7 @@ function normalizeSandboxRequest(profileOrRequest, explicitRequest) {
291
321
  profile,
292
322
  requiredBoundaries,
293
323
  sync: request.sync === true,
324
+ pty: request.pty === true,
294
325
  executionContract:
295
326
  request.executionContract &&
296
327
  typeof request.executionContract === "object" &&
@@ -2600,8 +2631,11 @@ function linuxBubblewrapProbe(
2600
2631
  contentSnapshot = null,
2601
2632
  supervisorBinding = null,
2602
2633
  pluginTreeSnapshot = null,
2634
+ nativeDynamicClosure = null,
2603
2635
  ) {
2604
- const native = targetRuntime === "native-static-elf";
2636
+ const native =
2637
+ typeof targetRuntime === "string" && targetRuntime.startsWith("native-");
2638
+ const dynamicNative = targetRuntime === "native-dynamic-elf";
2605
2639
  const expectedSnapshotScope = native
2606
2640
  ? LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE
2607
2641
  : LINUX_NODE_ENTRY_SNAPSHOT_SCOPE;
@@ -2631,10 +2665,34 @@ function linuxBubblewrapProbe(
2631
2665
  LINUX_NODE_PLUGIN_TREE_SNAPSHOT_CONSISTENCY &&
2632
2666
  pluginTreeSnapshot?.contractBound === false &&
2633
2667
  pluginTreeSnapshot?.atomic === false;
2668
+ const initialDynamicLoadClosureDescriptorBound =
2669
+ dynamicNative &&
2670
+ attempted &&
2671
+ snapshot &&
2672
+ runnable &&
2673
+ reason === null &&
2674
+ supervisorDescriptorBound &&
2675
+ nativeDynamicClosure?.mechanism ===
2676
+ LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_MECHANISM &&
2677
+ nativeDynamicClosure?.scope ===
2678
+ LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_SCOPE &&
2679
+ typeof nativeDynamicClosure?.interpreter === "string" &&
2680
+ path.posix.isAbsolute(nativeDynamicClosure.interpreter) &&
2681
+ Number.isSafeInteger(nativeDynamicClosure?.dependencies) &&
2682
+ nativeDynamicClosure.dependencies >= 0 &&
2683
+ nativeDynamicClosure.dependencies <= LINUX_ELF_MAX_NEEDED_ENTRIES &&
2684
+ Number.isSafeInteger(nativeDynamicClosure?.files) &&
2685
+ nativeDynamicClosure.files >= 1 &&
2686
+ typeof nativeDynamicClosure?.digest === "string" &&
2687
+ /^[a-f0-9]{64}$/.test(nativeDynamicClosure.digest);
2634
2688
  return {
2635
- kind: native
2636
- ? "linux-bwrap-plugin-native-static-elf-policy-v1"
2637
- : "linux-bwrap-plugin-node-policy-v1",
2689
+ kind: dynamicNative
2690
+ ? "linux-bwrap-plugin-native-dynamic-elf-policy-v1"
2691
+ : targetRuntime === "native-static-elf"
2692
+ ? "linux-bwrap-plugin-native-static-elf-policy-v1"
2693
+ : native
2694
+ ? "linux-bwrap-plugin-native-elf-policy-v1"
2695
+ : "linux-bwrap-plugin-node-policy-v1",
2638
2696
  attempted,
2639
2697
  runnable,
2640
2698
  reason,
@@ -2660,6 +2718,17 @@ function linuxBubblewrapProbe(
2660
2718
  pluginTreeSnapshotAtomic: pluginTreeSnapshot.atomic,
2661
2719
  }
2662
2720
  : {}),
2721
+ ...(initialDynamicLoadClosureDescriptorBound
2722
+ ? {
2723
+ initialDynamicLoadClosureDescriptorBound: true,
2724
+ initialDynamicLoadClosureScope: nativeDynamicClosure.scope,
2725
+ initialDynamicLoadClosureMechanism: nativeDynamicClosure.mechanism,
2726
+ initialDynamicInterpreter: nativeDynamicClosure.interpreter,
2727
+ initialDynamicDependencyCount: nativeDynamicClosure.dependencies,
2728
+ initialDynamicRuntimeFileCount: nativeDynamicClosure.files,
2729
+ initialDynamicLoadClosureDigest: nativeDynamicClosure.digest,
2730
+ }
2731
+ : {}),
2663
2732
  supervisorDescriptorBound,
2664
2733
  ...(supervisorDescriptorBound
2665
2734
  ? {
@@ -2957,8 +3026,11 @@ function validateLinuxPluginContract(
2957
3026
  { sealedEntry = false } = {},
2958
3027
  ) {
2959
3028
  const nodeContract = contract?.kind === "strict-plugin-node-bin";
2960
- const nativeContract =
3029
+ const legacyStaticNativeContract =
2961
3030
  contract?.kind === "strict-plugin-native-static-elf-bin";
3031
+ const nativeContract =
3032
+ legacyStaticNativeContract ||
3033
+ contract?.kind === "strict-plugin-native-elf-bin";
2962
3034
  const entryIsSealed =
2963
3035
  (nodeContract || nativeContract) && sealedEntry === true;
2964
3036
  if (
@@ -2969,7 +3041,7 @@ function validateLinuxPluginContract(
2969
3041
  return { ok: false, reason: "execution_contract_missing" };
2970
3042
  }
2971
3043
  if (
2972
- sync !== true ||
3044
+ (sync !== true && sync !== false) ||
2973
3045
  spawnOpts?.shell !== false ||
2974
3046
  spawnOpts?.detached === true ||
2975
3047
  !linuxStdioIsNarrow(spawnOpts?.stdio) ||
@@ -3081,16 +3153,22 @@ function validateLinuxPluginContract(
3081
3153
  let entryFormat = null;
3082
3154
  if (nativeContract && !entryIsSealed) {
3083
3155
  try {
3084
- entryFormat = inspectLinuxStaticNativePath(
3085
- runtime,
3086
- contract.entryIdentity,
3087
- );
3156
+ entryFormat = inspectLinuxNativePath(runtime, contract.entryIdentity);
3088
3157
  } catch (error) {
3089
3158
  return {
3090
3159
  ok: false,
3091
3160
  reason: error.message || "native_entry_unattested",
3092
3161
  };
3093
3162
  }
3163
+ if (
3164
+ legacyStaticNativeContract &&
3165
+ entryFormat.runtime !== "native-static-elf"
3166
+ ) {
3167
+ return {
3168
+ ok: false,
3169
+ reason: "native_entry_dynamic_contract_required",
3170
+ };
3171
+ }
3094
3172
  }
3095
3173
  if (
3096
3174
  !validateLinuxPluginTree(runtime, contract.pluginRoot) ||
@@ -3101,7 +3179,12 @@ function validateLinuxPluginContract(
3101
3179
  return {
3102
3180
  ok: true,
3103
3181
  contract,
3104
- entryRuntime: nativeContract ? "native-static-elf" : "node",
3182
+ entryRuntime: nativeContract
3183
+ ? entryFormat?.runtime ||
3184
+ (legacyStaticNativeContract
3185
+ ? "native-static-elf"
3186
+ : "native-unclassified")
3187
+ : "node",
3105
3188
  entryFormat,
3106
3189
  };
3107
3190
  }
@@ -3170,7 +3253,7 @@ function hashLinuxOpenFile(runtime, fd, bytes) {
3170
3253
  }
3171
3254
 
3172
3255
  function linuxEntrySnapshotContract(targetRuntime) {
3173
- if (targetRuntime === "native-static-elf") {
3256
+ if (targetRuntime?.startsWith("native-")) {
3174
3257
  return {
3175
3258
  scope: LINUX_NATIVE_ENTRY_SNAPSHOT_SCOPE,
3176
3259
  targetMode: LINUX_NATIVE_ENTRY_SNAPSHOT_TARGET_MODE,
@@ -3663,18 +3746,169 @@ function attestLinuxNodePluginTreeSnapshot(
3663
3746
  }
3664
3747
  }
3665
3748
 
3749
+ function validLinuxRuntimePageSize(value) {
3750
+ if (
3751
+ !Number.isSafeInteger(value) ||
3752
+ value < LINUX_MIN_RUNTIME_PAGE_BYTES ||
3753
+ value > LINUX_MAX_RUNTIME_PAGE_BYTES
3754
+ ) {
3755
+ return false;
3756
+ }
3757
+ const size = BigInt(value);
3758
+ return (size & (size - 1n)) === 0n;
3759
+ }
3760
+
3761
+ function readLinuxPageSizeFromAuxv(runtimeFs) {
3762
+ // AT_PAGESZ is emitted by the kernel for this exact process and matches the
3763
+ // page granularity used by the ELF loader. Avoid filesystem block sizes or
3764
+ // an external getconf binary, neither of which is the loader contract.
3765
+ const raw = runtimeFs.readFileSync("/proc/self/auxv");
3766
+ const auxv = Buffer.isBuffer(raw) ? raw : Buffer.from(raw);
3767
+ if (
3768
+ auxv.length < LINUX_AUXV_ENTRY_BYTES ||
3769
+ auxv.length > LINUX_AUXV_MAX_BYTES ||
3770
+ auxv.length % LINUX_AUXV_ENTRY_BYTES !== 0
3771
+ ) {
3772
+ throw new Error("linux_runtime_auxv_invalid");
3773
+ }
3774
+ let pageSize = null;
3775
+ let terminated = false;
3776
+ for (let offset = 0; offset < auxv.length; offset += LINUX_AUXV_ENTRY_BYTES) {
3777
+ const tag = auxv.readBigUInt64LE(offset);
3778
+ const value = auxv.readBigUInt64LE(offset + 8);
3779
+ if (tag === 0n) {
3780
+ terminated = true;
3781
+ break;
3782
+ }
3783
+ if (tag !== LINUX_AUXV_PAGE_SIZE) continue;
3784
+ if (pageSize !== null || value > BigInt(Number.MAX_SAFE_INTEGER)) {
3785
+ throw new Error("linux_runtime_page_size_ambiguous");
3786
+ }
3787
+ pageSize = Number(value);
3788
+ }
3789
+ if (!terminated || !validLinuxRuntimePageSize(pageSize)) {
3790
+ throw new Error("linux_runtime_page_size_unattested");
3791
+ }
3792
+ return pageSize;
3793
+ }
3794
+
3795
+ function linuxElfRuntimePageSize(runtime) {
3796
+ const pageSize =
3797
+ typeof runtime.linuxPageSize === "function"
3798
+ ? runtime.linuxPageSize()
3799
+ : readLinuxPageSizeFromAuxv(runtime.fs);
3800
+ if (!validLinuxRuntimePageSize(pageSize)) {
3801
+ throw new Error("linux_runtime_page_size_unattested");
3802
+ }
3803
+ return BigInt(pageSize);
3804
+ }
3805
+
3806
+ function linuxElfAlignDown(value, alignment) {
3807
+ return value - (value % alignment);
3808
+ }
3809
+
3810
+ function linuxElfAlignUp(value, alignment) {
3811
+ const remainder = value % alignment;
3812
+ return remainder === 0n ? value : value + alignment - remainder;
3813
+ }
3814
+
3815
+ function attestLinuxElfLoadMappings(loadSegments, pageSize) {
3816
+ // Linux maps later PT_LOAD entries with MAP_FIXED. Even disjoint byte ranges
3817
+ // can therefore replace one another after the loader rounds them to pages.
3818
+ // A conservative no-overlap contract keeps our file-offset parser and the
3819
+ // in-memory dynamic-loader view identical.
3820
+ const ranges = [];
3821
+ for (const load of loadSegments) {
3822
+ if (
3823
+ load.fileOffset % pageSize !== load.virtualAddress % pageSize ||
3824
+ (load.alignment > 1n &&
3825
+ ((load.alignment & (load.alignment - 1n)) !== 0n ||
3826
+ load.fileOffset % load.alignment !==
3827
+ load.virtualAddress % load.alignment))
3828
+ ) {
3829
+ throw new Error("native_entry_load_segment_alignment_invalid");
3830
+ }
3831
+ if (
3832
+ load.virtualAddress > LINUX_ELF_MAX_UINT64 - load.memorySize ||
3833
+ load.virtualAddress + load.memorySize >
3834
+ LINUX_ELF_MAX_UINT64 - (pageSize - 1n)
3835
+ ) {
3836
+ throw new Error("native_entry_load_segment_virtual_range_overflow");
3837
+ }
3838
+ if (load.memorySize === 0n) continue;
3839
+ ranges.push({
3840
+ start: linuxElfAlignDown(load.virtualAddress, pageSize),
3841
+ end: linuxElfAlignUp(load.virtualAddress + load.memorySize, pageSize),
3842
+ });
3843
+ }
3844
+ ranges.sort((left, right) =>
3845
+ left.start < right.start ? -1 : left.start > right.start ? 1 : 0,
3846
+ );
3847
+ for (let index = 1; index < ranges.length; index += 1) {
3848
+ if (ranges[index].start < ranges[index - 1].end) {
3849
+ throw new Error("native_entry_load_segment_page_overlap");
3850
+ }
3851
+ }
3852
+ }
3853
+
3854
+ function linuxElfVirtualRangeFileMappings(loadSegments, address, bytes) {
3855
+ return loadSegments
3856
+ .filter((load) => {
3857
+ if (address < load.virtualAddress) return false;
3858
+ const delta = address - load.virtualAddress;
3859
+ return (
3860
+ delta <= load.fileSize &&
3861
+ bytes <= load.fileSize - delta &&
3862
+ delta <= load.memorySize &&
3863
+ bytes <= load.memorySize - delta
3864
+ );
3865
+ })
3866
+ .map((load) => ({
3867
+ load,
3868
+ fileOffset: load.fileOffset + (address - load.virtualAddress),
3869
+ }));
3870
+ }
3871
+
3872
+ function readLinuxElfString(table, offset, label) {
3873
+ if (!Number.isSafeInteger(offset) || offset < 0 || offset >= table.length) {
3874
+ throw new Error(`native_entry_${label}_string_out_of_bounds`);
3875
+ }
3876
+ const end = table.indexOf(0, offset);
3877
+ if (end < 0) {
3878
+ throw new Error(`native_entry_${label}_string_unterminated`);
3879
+ }
3880
+ const bytes = table.subarray(offset, end);
3881
+ if (bytes.length < 1 || bytes.some((value) => value < 0x21 || value > 0x7e)) {
3882
+ throw new Error(`native_entry_${label}_string_invalid`);
3883
+ }
3884
+ return bytes.toString("ascii");
3885
+ }
3886
+
3887
+ function linuxSystemDynamicPath(value) {
3888
+ return (
3889
+ typeof value === "string" &&
3890
+ path.posix.isAbsolute(value) &&
3891
+ path.posix.normalize(value) === value &&
3892
+ !value.includes("\0") &&
3893
+ ["/lib/", "/lib64/", "/usr/lib/", "/usr/lib64/"].some((prefix) =>
3894
+ value.startsWith(prefix),
3895
+ )
3896
+ );
3897
+ }
3898
+
3666
3899
  /**
3667
- * Classify one already-pinned plugin entry without invoking it or any host
3668
- * inspection utility. The native slice accepts conventional fully static
3669
- * ELF64 ET_EXEC images and a narrow static-PIE-shaped ELF:
3670
- * ET_DYN without PT_INTERP, with one bounded PT_DYNAMIC table that declares
3671
- * PIE but no external DT_NEEDED dependency.
3900
+ * Classify one already-pinned plugin entry without invoking it or passing it
3901
+ * to a host inspection utility. Besides conventional fully static ET_EXEC and
3902
+ * static-PIE-shaped ET_DYN images, the parser can describe a narrow dynamic
3903
+ * executable. The caller must separately prove that its PT_INTERP and every
3904
+ * direct DT_NEEDED member are present in a pinned, attested system-file set.
3672
3905
  */
3673
- function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3906
+ function inspectLinuxNativeElf(runtime, fd, identity) {
3674
3907
  const expectedMachine = LINUX_ELF_MACHINES[runtime.arch];
3675
3908
  if (!expectedMachine) {
3676
3909
  throw new Error(`unsupported_elf_architecture:${String(runtime.arch)}`);
3677
3910
  }
3911
+ const runtimePageSize = linuxElfRuntimePageSize(runtime);
3678
3912
  const before = runtime.fs.fstatSync(fd);
3679
3913
  if (
3680
3914
  !before.isFile() ||
@@ -3741,6 +3975,7 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3741
3975
  let nonExecutableStack = false;
3742
3976
  const loadSegments = [];
3743
3977
  const dynamicSegments = [];
3978
+ const interpreterSegments = [];
3744
3979
  for (let index = 0; index < programHeaderCount; index += 1) {
3745
3980
  const offset =
3746
3981
  Number(programHeaderOffset) + index * LINUX_ELF64_PROGRAM_HEADER_BYTES;
@@ -3756,6 +3991,7 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3756
3991
  const virtualAddress = program.readBigUInt64LE(16);
3757
3992
  const fileSize = program.readBigUInt64LE(32);
3758
3993
  const memorySize = program.readBigUInt64LE(40);
3994
+ const alignment = program.readBigUInt64LE(48);
3759
3995
  if (fileOffset > fileBytes || fileSize > fileBytes - fileOffset) {
3760
3996
  throw new Error("native_entry_segment_out_of_bounds");
3761
3997
  }
@@ -3763,18 +3999,21 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3763
3999
  throw new Error("native_entry_segment_out_of_bounds");
3764
4000
  }
3765
4001
  if (type === LINUX_ELF_PROGRAM_INTERP) {
3766
- throw new Error("native_entry_interpreter_unsupported");
4002
+ interpreterSegments.push({
4003
+ flags,
4004
+ fileOffset,
4005
+ fileSize,
4006
+ memorySize,
4007
+ });
3767
4008
  }
3768
4009
  if (type === LINUX_ELF_PROGRAM_DYNAMIC) {
3769
- if (elfType === LINUX_ELF_TYPE_EXEC) {
3770
- throw new Error("native_entry_dynamic_elf_unsupported");
3771
- }
3772
4010
  dynamicSegments.push({
3773
4011
  flags,
3774
4012
  fileOffset,
3775
4013
  virtualAddress,
3776
4014
  fileSize,
3777
4015
  memorySize,
4016
+ alignment,
3778
4017
  });
3779
4018
  }
3780
4019
  if (type === LINUX_ELF_PROGRAM_GNU_STACK) {
@@ -3790,6 +4029,7 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3790
4029
  virtualAddress,
3791
4030
  fileSize,
3792
4031
  memorySize,
4032
+ alignment,
3793
4033
  });
3794
4034
  }
3795
4035
  if (type === LINUX_ELF_PROGRAM_LOAD && (flags & 0x1) !== 0) {
@@ -3805,6 +4045,7 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3805
4045
  }
3806
4046
  }
3807
4047
  }
4048
+ attestLinuxElfLoadMappings(loadSegments, runtimePageSize);
3808
4049
  if (!executableLoad || !entryInExecutableLoad) {
3809
4050
  throw new Error("native_entry_has_no_executable_entry_segment");
3810
4051
  }
@@ -3812,23 +4053,65 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3812
4053
  throw new Error("native_entry_nonexecutable_stack_unattested");
3813
4054
  }
3814
4055
 
3815
- if (elfType === LINUX_ELF_TYPE_DYN) {
4056
+ if (interpreterSegments.length > 1) {
4057
+ throw new Error("native_entry_interpreter_ambiguous");
4058
+ }
4059
+ let interpreter = null;
4060
+ if (interpreterSegments.length === 1) {
4061
+ const segment = interpreterSegments[0];
4062
+ if (
4063
+ (segment.flags & 0x3) !== 0 ||
4064
+ segment.fileSize < 2n ||
4065
+ segment.fileSize > BigInt(LINUX_ELF_MAX_INTERPRETER_BYTES) ||
4066
+ segment.fileSize > segment.memorySize
4067
+ ) {
4068
+ throw new Error("native_entry_interpreter_invalid");
4069
+ }
4070
+ const contents = readLinuxFdExactly(
4071
+ runtime,
4072
+ fd,
4073
+ Number(segment.fileSize),
4074
+ Number(segment.fileOffset),
4075
+ );
4076
+ if (
4077
+ contents[contents.length - 1] !== 0 ||
4078
+ contents.indexOf(0) !== contents.length - 1
4079
+ ) {
4080
+ throw new Error("native_entry_interpreter_unterminated");
4081
+ }
4082
+ interpreter = readLinuxElfString(contents, 0, "interpreter");
4083
+ if (!linuxSystemDynamicPath(interpreter)) {
4084
+ throw new Error("native_entry_interpreter_outside_system_roots");
4085
+ }
4086
+ }
4087
+
4088
+ const staticPie = elfType === LINUX_ELF_TYPE_DYN && interpreter === null;
4089
+ const dynamicExecutable = interpreter !== null;
4090
+ let dynamicMetadata = null;
4091
+ if (elfType === LINUX_ELF_TYPE_EXEC && !dynamicExecutable) {
4092
+ if (dynamicSegments.length > 0) {
4093
+ throw new Error("native_entry_dynamic_elf_unsupported");
4094
+ }
4095
+ } else {
4096
+ const prefix = staticPie
4097
+ ? "native_entry_static_pie"
4098
+ : "native_entry_dynamic";
3816
4099
  if (dynamicSegments.length === 0) {
3817
- throw new Error("native_entry_static_pie_dynamic_segment_missing");
4100
+ throw new Error(`${prefix}_dynamic_segment_missing`);
3818
4101
  }
3819
4102
  if (dynamicSegments.length !== 1) {
3820
- throw new Error("native_entry_static_pie_dynamic_segment_ambiguous");
4103
+ throw new Error(`${prefix}_dynamic_segment_ambiguous`);
3821
4104
  }
3822
4105
  const dynamic = dynamicSegments[0];
3823
4106
  if ((dynamic.flags & 0x1) !== 0) {
3824
- throw new Error("native_entry_static_pie_dynamic_segment_executable");
4107
+ throw new Error(`${prefix}_dynamic_segment_executable`);
3825
4108
  }
3826
4109
  if (
3827
4110
  dynamic.fileSize < BigInt(LINUX_ELF64_DYNAMIC_ENTRY_BYTES) ||
3828
4111
  dynamic.fileSize % BigInt(LINUX_ELF64_DYNAMIC_ENTRY_BYTES) !== 0n ||
3829
4112
  dynamic.fileSize > dynamic.memorySize
3830
4113
  ) {
3831
- throw new Error("native_entry_static_pie_dynamic_segment_invalid");
4114
+ throw new Error(`${prefix}_dynamic_segment_invalid`);
3832
4115
  }
3833
4116
  const dynamicEntries =
3834
4117
  dynamic.fileSize / BigInt(LINUX_ELF64_DYNAMIC_ENTRY_BYTES);
@@ -3836,9 +4119,9 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3836
4119
  dynamicEntries < 1n ||
3837
4120
  dynamicEntries > BigInt(LINUX_ELF_MAX_DYNAMIC_ENTRIES)
3838
4121
  ) {
3839
- throw new Error("native_entry_static_pie_dynamic_table_too_large");
4122
+ throw new Error(`${prefix}_dynamic_table_too_large`);
3840
4123
  }
3841
- const containingLoad = loadSegments.find((load) => {
4124
+ const containingLoads = loadSegments.filter((load) => {
3842
4125
  if (
3843
4126
  dynamic.fileOffset < load.fileOffset ||
3844
4127
  dynamic.virtualAddress < load.virtualAddress
@@ -3855,8 +4138,22 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3855
4138
  dynamic.memorySize <= load.memorySize - memoryDelta
3856
4139
  );
3857
4140
  });
3858
- if (!containingLoad) {
3859
- throw new Error("native_entry_static_pie_dynamic_segment_unmapped");
4141
+ if (containingLoads.length === 0) {
4142
+ throw new Error(`${prefix}_dynamic_segment_unmapped`);
4143
+ }
4144
+ if (containingLoads.length !== 1) {
4145
+ throw new Error(`${prefix}_dynamic_segment_mapping_ambiguous`);
4146
+ }
4147
+ const dynamicLoaderMappings = linuxElfVirtualRangeFileMappings(
4148
+ loadSegments,
4149
+ dynamic.virtualAddress,
4150
+ dynamic.fileSize,
4151
+ );
4152
+ if (
4153
+ dynamicLoaderMappings.length !== 1 ||
4154
+ dynamicLoaderMappings[0].fileOffset !== dynamic.fileOffset
4155
+ ) {
4156
+ throw new Error(`${prefix}_dynamic_segment_loader_view_mismatch`);
3860
4157
  }
3861
4158
 
3862
4159
  const dynamicTable = readLinuxFdExactly(
@@ -3865,8 +4162,21 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3865
4162
  Number(dynamic.fileSize),
3866
4163
  Number(dynamic.fileOffset),
3867
4164
  );
4165
+ const neededOffsets = [];
4166
+ const forbiddenDependencyTags = new Set([
4167
+ LINUX_ELF_DYNAMIC_RPATH,
4168
+ LINUX_ELF_DYNAMIC_RUNPATH,
4169
+ LINUX_ELF_DYNAMIC_CONFIG,
4170
+ LINUX_ELF_DYNAMIC_DEPAUDIT,
4171
+ LINUX_ELF_DYNAMIC_AUDIT,
4172
+ LINUX_ELF_DYNAMIC_AUXILIARY,
4173
+ LINUX_ELF_DYNAMIC_FILTER,
4174
+ ]);
3868
4175
  let terminated = false;
4176
+ let flags = null;
3869
4177
  let flags1 = null;
4178
+ let stringTableAddress = null;
4179
+ let stringTableBytes = null;
3870
4180
  for (
3871
4181
  let offset = 0;
3872
4182
  offset < dynamicTable.length;
@@ -3878,21 +4188,113 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3878
4188
  terminated = true;
3879
4189
  break;
3880
4190
  }
3881
- if (tag === LINUX_ELF_DYNAMIC_NEEDED) {
3882
- throw new Error("native_entry_static_pie_dependency_unsupported");
4191
+ if (
4192
+ tag === LINUX_ELF_DYNAMIC_TEXTREL ||
4193
+ forbiddenDependencyTags.has(tag)
4194
+ ) {
4195
+ throw new Error(`${prefix}_loader_directive_unsupported`);
3883
4196
  }
3884
- if (tag === LINUX_ELF_DYNAMIC_FLAGS_1) {
4197
+ if (tag === LINUX_ELF_DYNAMIC_NEEDED) {
4198
+ neededOffsets.push(value);
4199
+ if (neededOffsets.length > LINUX_ELF_MAX_NEEDED_ENTRIES) {
4200
+ throw new Error(`${prefix}_dependency_count_exceeded`);
4201
+ }
4202
+ } else if (tag === LINUX_ELF_DYNAMIC_STRTAB) {
4203
+ if (stringTableAddress !== null) {
4204
+ throw new Error(`${prefix}_string_table_ambiguous`);
4205
+ }
4206
+ stringTableAddress = value;
4207
+ } else if (tag === LINUX_ELF_DYNAMIC_STRSZ) {
4208
+ if (stringTableBytes !== null) {
4209
+ throw new Error(`${prefix}_string_table_ambiguous`);
4210
+ }
4211
+ stringTableBytes = value;
4212
+ } else if (tag === LINUX_ELF_DYNAMIC_FLAGS) {
4213
+ if (flags !== null) {
4214
+ throw new Error(`${prefix}_flags_ambiguous`);
4215
+ }
4216
+ flags = value;
4217
+ } else if (tag === LINUX_ELF_DYNAMIC_FLAGS_1) {
3885
4218
  if (flags1 !== null) {
3886
- throw new Error("native_entry_static_pie_flags_ambiguous");
4219
+ throw new Error(`${prefix}_flags_ambiguous`);
3887
4220
  }
3888
4221
  flags1 = value;
3889
4222
  }
3890
4223
  }
3891
4224
  if (!terminated) {
3892
- throw new Error("native_entry_static_pie_dynamic_table_unterminated");
4225
+ throw new Error(`${prefix}_dynamic_table_unterminated`);
4226
+ }
4227
+ if (flags !== null && (flags & LINUX_ELF_DYNAMIC_FLAG_TEXTREL) !== 0n) {
4228
+ throw new Error(`${prefix}_text_relocation_unsupported`);
4229
+ }
4230
+ if (
4231
+ elfType === LINUX_ELF_TYPE_DYN &&
4232
+ (flags1 === null || (flags1 & LINUX_ELF_DYNAMIC_FLAG_PIE) === 0n)
4233
+ ) {
4234
+ throw new Error(`${prefix}_flag_missing`);
3893
4235
  }
3894
- if (flags1 === null || (flags1 & LINUX_ELF_DYNAMIC_FLAG_PIE) === 0n) {
3895
- throw new Error("native_entry_static_pie_flag_missing");
4236
+ if (staticPie && neededOffsets.length > 0) {
4237
+ throw new Error("native_entry_static_pie_dependency_unsupported");
4238
+ }
4239
+
4240
+ let needed = [];
4241
+ if (neededOffsets.length > 0) {
4242
+ if (
4243
+ stringTableAddress === null ||
4244
+ stringTableBytes === null ||
4245
+ stringTableBytes < 1n ||
4246
+ stringTableBytes > BigInt(LINUX_ELF_MAX_STRING_TABLE_BYTES)
4247
+ ) {
4248
+ throw new Error(`${prefix}_string_table_invalid`);
4249
+ }
4250
+ const stringTableMappings = linuxElfVirtualRangeFileMappings(
4251
+ loadSegments,
4252
+ stringTableAddress,
4253
+ stringTableBytes,
4254
+ );
4255
+ if (stringTableMappings.length === 0) {
4256
+ throw new Error(`${prefix}_string_table_unmapped`);
4257
+ }
4258
+ if (stringTableMappings.length !== 1) {
4259
+ throw new Error(`${prefix}_string_table_mapping_ambiguous`);
4260
+ }
4261
+ const stringTableOffset = stringTableMappings[0].fileOffset;
4262
+ if (stringTableOffset > BigInt(Number.MAX_SAFE_INTEGER)) {
4263
+ throw new Error(`${prefix}_string_table_unmapped`);
4264
+ }
4265
+ const stringTable = readLinuxFdExactly(
4266
+ runtime,
4267
+ fd,
4268
+ Number(stringTableBytes),
4269
+ Number(stringTableOffset),
4270
+ );
4271
+ needed = neededOffsets.map((offset) => {
4272
+ if (offset > BigInt(Number.MAX_SAFE_INTEGER)) {
4273
+ throw new Error(`${prefix}_dependency_string_out_of_bounds`);
4274
+ }
4275
+ const name = readLinuxElfString(
4276
+ stringTable,
4277
+ Number(offset),
4278
+ "dynamic_dependency",
4279
+ );
4280
+ if (
4281
+ !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,254}$/.test(name) ||
4282
+ path.posix.basename(name) !== name
4283
+ ) {
4284
+ throw new Error("native_entry_dynamic_dependency_name_invalid");
4285
+ }
4286
+ return name;
4287
+ });
4288
+ if (new Set(needed).size !== needed.length) {
4289
+ throw new Error("native_entry_dynamic_dependency_ambiguous");
4290
+ }
4291
+ }
4292
+
4293
+ if (dynamicExecutable) {
4294
+ dynamicMetadata = Object.freeze({
4295
+ interpreter,
4296
+ needed: Object.freeze(needed),
4297
+ });
3896
4298
  }
3897
4299
  }
3898
4300
 
@@ -3902,17 +4304,23 @@ function inspectLinuxStaticNativeElf(runtime, fd, identity) {
3902
4304
  throw new Error("native_entry_changed_during_elf_attestation");
3903
4305
  }
3904
4306
  return Object.freeze({
3905
- format:
3906
- elfType === LINUX_ELF_TYPE_DYN
4307
+ runtime: dynamicExecutable ? "native-dynamic-elf" : "native-static-elf",
4308
+ format: dynamicExecutable
4309
+ ? elfType === LINUX_ELF_TYPE_DYN
4310
+ ? "elf64-dynamic-pie-et-dyn"
4311
+ : "elf64-dynamic-et-exec"
4312
+ : elfType === LINUX_ELF_TYPE_DYN
3907
4313
  ? "elf64-static-pie-shaped-et-dyn"
3908
4314
  : "elf64-static-et-exec",
3909
4315
  architecture: runtime.arch,
3910
4316
  machine: expectedMachine,
4317
+ loaderPageBytes: Number(runtimePageSize),
3911
4318
  programHeaders: programHeaderCount,
4319
+ ...(dynamicMetadata || {}),
3912
4320
  });
3913
4321
  }
3914
4322
 
3915
- function inspectLinuxStaticNativePath(runtime, identity) {
4323
+ function inspectLinuxNativePath(runtime, identity) {
3916
4324
  let fd;
3917
4325
  try {
3918
4326
  const constants = runtime.fs.constants || fs.constants;
@@ -3921,7 +4329,7 @@ function inspectLinuxStaticNativePath(runtime, identity) {
3921
4329
  Number(constants.O_NOFOLLOW || 0) |
3922
4330
  Number(constants.O_NONBLOCK || 0);
3923
4331
  fd = runtime.fs.openSync(identity.realPath, flags);
3924
- return inspectLinuxStaticNativeElf(runtime, fd, identity);
4332
+ return inspectLinuxNativeElf(runtime, fd, identity);
3925
4333
  } finally {
3926
4334
  if (fd !== undefined) {
3927
4335
  try {
@@ -4146,7 +4554,8 @@ function buildLinuxNetworkSeccompFilter(arch) {
4146
4554
  instructions.push([0x54, 0, 0, ~architecture.x32SyscallBit]);
4147
4555
  }
4148
4556
  instructions.push(
4149
- // No socket family is needed by this direct foreground Plugin Node route.
4557
+ // No socket family is needed by this direct one-shot Plugin bin route,
4558
+ // whether the Broker call is synchronous or asynchronously supervised.
4150
4559
  // Blocking creation rather than relying only on CLONE_NEWNET also closes
4151
4560
  // non-network-namespaced transports such as AF_VSOCK.
4152
4561
  [0x15, 0, 1, architecture.socketSyscall],
@@ -4656,6 +5065,87 @@ function pinLinuxRuntimeMounts(runtime, runtimeMounts, runtimeIdentity) {
4656
5065
  }
4657
5066
  }
4658
5067
 
5068
+ // Bind only the kernel-selected interpreter and the entry ELF's direct
5069
+ // DT_NEEDED names. This deliberately does not claim transitive or dlopen-time
5070
+ // closure; handleAtomic remains false for the wider launch chain.
5071
+ function bindLinuxNativeDynamicRuntime(entryFormat, pinnedRuntimeMounts) {
5072
+ if (entryFormat?.runtime !== "native-dynamic-elf") return null;
5073
+ if (
5074
+ !linuxSystemDynamicPath(entryFormat.interpreter) ||
5075
+ !Array.isArray(entryFormat.needed) ||
5076
+ entryFormat.needed.length > LINUX_ELF_MAX_NEEDED_ENTRIES
5077
+ ) {
5078
+ throw new Error("native_dynamic_metadata_invalid");
5079
+ }
5080
+ const systemMounts = (pinnedRuntimeMounts || []).filter(
5081
+ (mount) =>
5082
+ linuxSystemDynamicPath(mount?.destination) &&
5083
+ Number.isInteger(mount?.fd) &&
5084
+ typeof mount?.fileId?.dev === "string" &&
5085
+ typeof mount?.fileId?.ino === "string" &&
5086
+ Number.isSafeInteger(mount?.bytes) &&
5087
+ mount.bytes > 0 &&
5088
+ Number.isFinite(mount?.mtimeMs),
5089
+ );
5090
+ const interpreterMount = systemMounts.find(
5091
+ (mount) => mount.destination === entryFormat.interpreter,
5092
+ );
5093
+ if (!interpreterMount) {
5094
+ throw new Error("native_dynamic_interpreter_outside_direct_system_set");
5095
+ }
5096
+ const selected = new Map([[interpreterMount.destination, interpreterMount]]);
5097
+ for (const dependency of entryFormat.needed) {
5098
+ if (
5099
+ typeof dependency !== "string" ||
5100
+ !/^[A-Za-z0-9][A-Za-z0-9._+-]{0,254}$/.test(dependency)
5101
+ ) {
5102
+ throw new Error("native_dynamic_dependency_name_invalid");
5103
+ }
5104
+ const matches = systemMounts.filter(
5105
+ (mount) => path.posix.basename(mount.destination) === dependency,
5106
+ );
5107
+ if (matches.length === 0) {
5108
+ throw new Error("native_dynamic_dependency_outside_direct_system_set");
5109
+ }
5110
+ if (matches.length !== 1) {
5111
+ throw new Error("native_dynamic_dependency_runtime_ambiguous");
5112
+ }
5113
+ selected.set(matches[0].destination, matches[0]);
5114
+ }
5115
+ const members = [...selected.values()]
5116
+ .map((mount) => ({
5117
+ destination: mount.destination,
5118
+ fileId: {
5119
+ dev: mount.fileId.dev,
5120
+ ino: mount.fileId.ino,
5121
+ },
5122
+ bytes: mount.bytes,
5123
+ mtimeMs: mount.mtimeMs,
5124
+ }))
5125
+ .sort((left, right) => left.destination.localeCompare(right.destination));
5126
+ const digest = crypto
5127
+ .createHash("sha256")
5128
+ .update(
5129
+ JSON.stringify({
5130
+ version: 1,
5131
+ scope: LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_SCOPE,
5132
+ mechanism: LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_MECHANISM,
5133
+ interpreter: entryFormat.interpreter,
5134
+ needed: [...entryFormat.needed].sort(),
5135
+ members,
5136
+ }),
5137
+ )
5138
+ .digest("hex");
5139
+ return Object.freeze({
5140
+ scope: LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_SCOPE,
5141
+ mechanism: LINUX_NATIVE_DYNAMIC_DIRECT_SYSTEM_SET_MECHANISM,
5142
+ interpreter: entryFormat.interpreter,
5143
+ dependencies: entryFormat.needed.length,
5144
+ files: members.length,
5145
+ digest,
5146
+ });
5147
+ }
5148
+
4659
5149
  function linuxSandboxEnvironment() {
4660
5150
  return {
4661
5151
  HOME: "/home/sandbox",
@@ -4932,11 +5422,22 @@ export function applyLinuxSandbox(
4932
5422
  );
4933
5423
 
4934
5424
  if (requiresStrongLinuxBoundary) {
5425
+ if (isLinuxGenericWorkspaceContract(sandboxOpts.executionContract)) {
5426
+ return applyLinuxGenericWorkspaceSandbox(
5427
+ command,
5428
+ args,
5429
+ spawnOpts,
5430
+ sandboxOpts,
5431
+ runtime,
5432
+ );
5433
+ }
4935
5434
  const requestedEntryRuntime =
4936
5435
  sandboxOpts.executionContract?.kind ===
4937
5436
  "strict-plugin-native-static-elf-bin"
4938
5437
  ? "native-static-elf"
4939
- : "node";
5438
+ : sandboxOpts.executionContract?.kind === "strict-plugin-native-elf-bin"
5439
+ ? "native-unclassified"
5440
+ : "node";
4940
5441
  const validation = validateLinuxPluginContract(
4941
5442
  command,
4942
5443
  args,
@@ -5009,6 +5510,29 @@ export function applyLinuxSandbox(
5009
5510
  guarantees: [],
5010
5511
  });
5011
5512
  }
5513
+ let nativeDynamicClosure = null;
5514
+ try {
5515
+ nativeDynamicClosure = bindLinuxNativeDynamicRuntime(
5516
+ validation.entryFormat,
5517
+ pinnedRuntimeMounts,
5518
+ );
5519
+ } catch (error) {
5520
+ closeLinuxPinnedMounts(runtime, pinnedRuntimeMounts);
5521
+ return createSandboxPlan({
5522
+ ...base,
5523
+ backend: null,
5524
+ candidateBackend: LINUX_BWRAP_BACKEND,
5525
+ policyAttested: false,
5526
+ runtimeProbe: linuxBubblewrapProbe(
5527
+ false,
5528
+ false,
5529
+ error.message || "native_dynamic_direct_system_set_unattested",
5530
+ validation.entryRuntime,
5531
+ ),
5532
+ reason: "linux_bwrap_native_runtime_unattested",
5533
+ guarantees: [],
5534
+ });
5535
+ }
5012
5536
 
5013
5537
  let pinnedMounts = [];
5014
5538
  let pluginTree;
@@ -5046,8 +5570,8 @@ export function applyLinuxSandbox(
5046
5570
  ) {
5047
5571
  throw new Error("plugin_entry_pin_mismatch");
5048
5572
  }
5049
- if (validation.entryRuntime === "native-static-elf") {
5050
- entryFormat = inspectLinuxStaticNativeElf(
5573
+ if (validation.entryRuntime.startsWith("native-")) {
5574
+ entryFormat = inspectLinuxNativeElf(
5051
5575
  runtime,
5052
5576
  entryMount.fd,
5053
5577
  validation.contract.entryIdentity,
@@ -5325,10 +5849,9 @@ export function applyLinuxSandbox(
5325
5849
  environment,
5326
5850
  seccompFilter,
5327
5851
  );
5328
- const targetArgs =
5329
- validation.entryRuntime === "native-static-elf"
5330
- ? [sandboxEntry, ...args]
5331
- : ["/opt/chainless/runtime/node", sandboxEntry, ...args.slice(1)];
5852
+ const targetArgs = validation.entryRuntime.startsWith("native-")
5853
+ ? [sandboxEntry, ...args]
5854
+ : ["/opt/chainless/runtime/node", sandboxEntry, ...args.slice(1)];
5332
5855
  const snapshotPolicyBinding = entrySnapshot
5333
5856
  ? {
5334
5857
  scope: entrySnapshot.attestation.scope,
@@ -5389,6 +5912,9 @@ export function applyLinuxSandbox(
5389
5912
  },
5390
5913
  ),
5391
5914
  contentSnapshot: snapshotPolicyBinding,
5915
+ ...(nativeDynamicClosure
5916
+ ? { initialDynamicLoadClosure: nativeDynamicClosure }
5917
+ : {}),
5392
5918
  ...(validation.entryRuntime === "node"
5393
5919
  ? { pluginTreeContentSnapshot: pluginTreeSnapshot }
5394
5920
  : {}),
@@ -5398,10 +5924,9 @@ export function applyLinuxSandbox(
5398
5924
  sha256: seccompFilter.sha256,
5399
5925
  },
5400
5926
  policyArgs,
5401
- target:
5402
- validation.entryRuntime === "native-static-elf"
5403
- ? targetArgs.slice(0, 1)
5404
- : targetArgs.slice(0, 2),
5927
+ target: validation.entryRuntime.startsWith("native-")
5928
+ ? targetArgs.slice(0, 1)
5929
+ : targetArgs.slice(0, 2),
5405
5930
  }),
5406
5931
  )
5407
5932
  .digest("hex");
@@ -5556,6 +6081,7 @@ export function applyLinuxSandbox(
5556
6081
  entrySnapshot?.attestation,
5557
6082
  supervisorBinding,
5558
6083
  pluginTreeSnapshot,
6084
+ nativeDynamicClosure,
5559
6085
  ),
5560
6086
  );
5561
6087
  const options = {
@@ -5655,6 +6181,7 @@ export function applyLinuxSandbox(
5655
6181
  * profile?: string,
5656
6182
  * requiredBoundaries?: string[],
5657
6183
  * sync?: boolean,
6184
+ * pty?: boolean,
5658
6185
  * executionContract?: Readonly<Object>|null
5659
6186
  * }|null} explicitRequest
5660
6187
  * @returns {ReturnType<typeof createSandboxPlan>}
@@ -5715,6 +6242,7 @@ export function applySandbox(
5715
6242
  profileName: profiles[profileName] ? profileName : "default",
5716
6243
  requiredBoundaries: sandboxRequest.requiredBoundaries,
5717
6244
  sync: sandboxRequest.sync,
6245
+ pty: sandboxRequest.pty,
5718
6246
  executionContract: sandboxRequest.executionContract,
5719
6247
  };
5720
6248