chainlesschain 0.162.180 → 0.162.182
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 +7 -3
- package/scripts/build-windows-sandbox-helper.ps1 +163 -0
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-DzZ5_xoj.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-DeLXddIg.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-rvDCmet1.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-Yt6QVDnp.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-BS112K5L.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-0wJuQrNu.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-B90PMgbn.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-TkgMglQQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-CQVS9p1l.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-YTjQHoC7.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-5bwaOabq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-BFmNQ_Xe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-BT_u4Dzp.js} +1 -1
- package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-CJP52u5b.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BpzhJE9i.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-Cg7rfslp.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-JisxL6Yy.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-Bu7Ug_MC.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CvuqoLdj.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-C2u9G8Pu.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-BFt5Yv3d.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-DIaT9Wxq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-D_A601Y4.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard--IyQ8I8w.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-BihWHxuY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-BB8dOXc_.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-DQYhj1HH.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-B-2QZxGf.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-CusSb-nG.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BAjmvPPL.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-DoNYWKYq.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-B8fKWD-n.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-BlJm1J7E.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-CyOscbSL.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-Ex6Om5da.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CDcD8l8r.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-xfBqoFDg.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-BwObMMA2.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-CIWd36OV.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPX0ouuA.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-BDJsIUuW.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-DEkbuD3N.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-DJpHRhH1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-C1UZ-Qd5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-GlJtqLjT.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-Bbhj0fac.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-1H04x3kx.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CDpPe3et.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-MUX0Bpvw.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-A07-0Ci1.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-DhhDWxAZ.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-CyfbJfe-.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-CNFps3ED.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit--T-u3Dr5.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-DJmrK2fC.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-DLzzbOZd.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-CexOomKS.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-QklGi19_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-Bb9kwSw8.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-DZuVNNN5.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-Dr5cLP0N.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-oCQHYv5t.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-IFDEykIR.js} +3 -3
- package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-CspVIxDc.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Bm66o_DH.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-DvanNqXl.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-CHjEe9_O.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-Cb8jWjMX.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-DxZm1491.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-DWxEEclC.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-FIvOFw5p.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +1 -0
- package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-CaNjzV_t.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-DjlP6TmH.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-DoZ8y7y8.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-390cg2TC.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-CLrtCj5_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-DpESfSPn.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-CN6279O0.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DPtAUlSe.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-nKOMV_1n.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-HuXlSOc4.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-CUfFjARi.js} +1 -1
- package/src/assets/web-panel/assets/chat-6Eq0PcZJ.js +1 -0
- package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BP_Sf6Iy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-BufMKIh5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BBMXxWSW.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-fUK_tRO2.js} +1 -1
- package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-2A5ph8b2.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-3FuwBewR.js} +1 -1
- package/src/assets/web-panel/assets/{index-53z1YUxX.js → index-6GmBa4Kg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-B2vS66eI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-B7Etk0Te.js} +1 -1
- package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-B9WYSn4m.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-BHy6yNiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-BMLHv389.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-BWKoBU8R.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-Bkg4ZePE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-C44atv4k.js} +1 -1
- package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-CE2DdTP2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-CGjmILDm.js} +1 -1
- package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-CW_G-MXw.js} +1 -1
- package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-CZ6Yt17L.js} +1 -1
- package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-CeASi4fr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-CgiECHu2.js} +1 -1
- package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Cp8vhyZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-CwSf5pnf.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-D4_MuGkd.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-D9vU25ij.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-DASluSqY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-DLpIs_P_.js} +1 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +1 -0
- package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DPdUNk1i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-DVsqvI4M.js} +1 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +1 -0
- package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-DmkMXwnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-DocdRcbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-Dyngbd29.js} +20 -20
- package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-GwdRUZ-x.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-LvCBfTLo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhXay17G.js → index-cpEli44X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-fzaMtSJ8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-pum7HYBA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-t8N1tSeX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5IytTND.js → index-uCbEN7cY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-wKTLbKyc.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-wV6iGdpY.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-V5Fo95FM.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-BYWU0hUT.js} +1 -1
- package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Cr1M-J2h.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-BysQKi2N.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-D1zHU8GC.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-CUArxtfi.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-ChoYEys0.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-BQRs3Mo7.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-B8zbsCkg.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-C2U8Gs6h.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-Dd8MgL_V.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-hknqY5NH.js} +1 -1
- package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-CLTAmcfw.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-ByegHbiB.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-DqlAy5DP.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-DiXiztQn.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +115 -3
- package/src/commands/agent.js +10 -1
- package/src/commands/background-session.js +2 -1
- package/src/commands/context.js +69 -2
- package/src/data/changelog.json +26 -0
- package/src/gateways/terminal/PtyManager.js +45 -4
- package/src/gateways/ws/background-agent-protocol.js +38 -0
- package/src/gateways/ws/message-dispatcher.js +1 -0
- package/src/gateways/ws/remote-session-protocol.js +65 -6
- package/src/gateways/ws/ws-server.js +18 -2
- package/src/harness/background-task-manager.js +70 -2
- package/src/harness/feature-flags.js +15 -8
- package/src/harness/mcp-client.js +618 -43
- package/src/harness/worktree-isolator.js +14 -3
- package/src/lazy-dispatch.js +30 -1
- package/src/lib/agent-authority.js +22 -0
- package/src/lib/agent-core.js +3 -0
- package/src/lib/agent-sandbox.js +161 -0
- package/src/lib/agent-schedule-store.js +45 -22
- package/src/lib/agent-team/team-runner.js +22 -0
- package/src/lib/artifact-store.js +35 -0
- package/src/lib/async-hook-supervisor.cjs +13 -2
- package/src/lib/audit-mtc.js +3 -2
- package/src/lib/background-agent-supervisor.js +104 -3
- package/src/lib/background-interaction-journal.js +407 -0
- package/src/lib/background-interaction-resolver.js +23 -34
- package/src/lib/background-session-transport.js +12 -2
- package/src/lib/capability-manifest.js +41 -3
- package/src/lib/capability-negotiation.js +1 -1
- package/src/lib/channels/channel-manager.js +33 -0
- package/src/lib/channels/telegram-channel.js +23 -5
- package/src/lib/channels/webhook-channel.js +38 -15
- package/src/lib/chrome-connector.js +169 -23
- package/src/lib/config-manager.js +23 -9
- package/src/lib/context-breakdown.js +31 -0
- package/src/lib/cowork-cron.js +274 -32
- package/src/lib/cross-chain-mtc.js +3 -2
- package/src/lib/durable-security-store.js +124 -0
- package/src/lib/event-runtime-host.js +67 -6
- package/src/lib/event-runtime-store.js +163 -0
- package/src/lib/goal-store.js +32 -12
- package/src/lib/headless-manifest.js +2 -1
- package/src/lib/hook-environment.cjs +58 -0
- package/src/lib/hook-manager.js +12 -5
- package/src/lib/hook-runner.cjs +122 -28
- package/src/lib/hook-runner.js +7 -2
- package/src/lib/hooks-v2-producers.js +36 -0
- package/src/lib/hooks-v2-runtime.js +329 -52
- package/src/lib/interaction-adapter.js +60 -19
- package/src/lib/interaction-binding.js +64 -0
- package/src/lib/json-schema-output.js +439 -14
- package/src/lib/json-schema-validate.js +514 -883
- package/src/lib/lan-pairing-tokens.js +115 -57
- package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
- package/src/lib/lsp/lsp-client.js +4 -0
- package/src/lib/lsp/lsp-manager.js +3 -0
- package/src/lib/lsp/lsp-server-registry.js +18 -0
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-oauth.js +35 -21
- package/src/lib/packer/pkg-config-generator.js +3 -0
- package/src/lib/permission-decision.js +99 -0
- package/src/lib/plugin-monitor-supervisor.js +3 -0
- package/src/lib/plugin-runtime/bin.js +1148 -70
- package/src/lib/plugin-runtime/capabilities.js +50 -0
- package/src/lib/plugin-runtime/capability-consent.js +42 -35
- package/src/lib/plugin-runtime/hooks.js +36 -9
- package/src/lib/plugin-runtime/install.js +23 -7
- package/src/lib/plugin-runtime/lsp.js +4 -0
- package/src/lib/plugin-runtime/manifest.js +112 -8
- package/src/lib/plugin-runtime/mcp.js +30 -5
- package/src/lib/plugin-runtime/monitors.js +11 -0
- package/src/lib/plugin-runtime/plugin-options.js +94 -38
- package/src/lib/plugin-runtime/policy.js +29 -2
- package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
- package/src/lib/plugin-runtime/scopes.js +27 -13
- package/src/lib/plugin-runtime/trust.js +36 -31
- package/src/lib/process-execution-broker/credential-agent.js +114 -16
- package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
- package/src/lib/process-execution-broker/credential-transport.js +378 -0
- package/src/lib/process-execution-broker/index.js +765 -49
- package/src/lib/process-execution-broker/platform-sandbox.js +4233 -138
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +3856 -0
- package/src/lib/project-mcp-trust.js +21 -26
- package/src/lib/protocol-replay.js +19 -12
- package/src/lib/repl-bang-memorize.js +67 -10
- package/src/lib/settings-hooks.cjs +13 -0
- package/src/lib/skill-loader.js +83 -10
- package/src/lib/slash-commands.js +27 -4
- package/src/lib/sync-credentials.js +5 -4
- package/src/lib/turn-binding.js +8 -6
- package/src/repl/agent-repl.js +117 -31
- package/src/repl/repl-turn-binding.js +5 -2
- package/src/runtime/agent-core.js +699 -70
- package/src/runtime/agent-runtime.js +13 -1
- package/src/runtime/coding-agent-contract-shared.cjs +3 -3
- package/src/runtime/headless-runner.js +105 -7
- package/src/runtime/headless-stream.js +209 -23
- package/src/runtime/mcp-config.js +29 -17
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/workers/background-agent-worker.js +128 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C7bofJuA.js +0 -1
- package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
- package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
- package/src/assets/web-panel/assets/Terminal-CsMLKIoV.js +0 -3
- package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
- package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
- package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
- package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* 统一拦截所有子进程执行:
|
|
6
6
|
* - M1: 所有子进程唯一入口,权限检查
|
|
7
|
-
* - **P0-1**: 可审计的平台执行计划(macOS Seatbelt / Linux prlimit
|
|
8
|
-
* Windows
|
|
7
|
+
* - **P0-1**: 可审计的平台执行计划(macOS Seatbelt / Linux prlimit /
|
|
8
|
+
* Windows Job Object + Restricted Token)
|
|
9
9
|
* - **P0-1**: 凭据代理 default-on(secrets 永远不裸传给子进程)
|
|
10
10
|
* - M3: 集成W3C trace context自动传播
|
|
11
11
|
* - M4: 自动写入Runtime Provenance Ledger (RPL)
|
|
@@ -36,9 +36,18 @@ const require = createRequire(import.meta.url);
|
|
|
36
36
|
import {
|
|
37
37
|
applySandbox as _applySandbox,
|
|
38
38
|
postSpawnSandbox as _postSpawnSandbox,
|
|
39
|
+
SANDBOX_BOUNDARIES,
|
|
39
40
|
} from "./platform-sandbox.js";
|
|
41
|
+
import { consumeIssuedPluginSandboxExecutionContract } from "../plugin-runtime/bin.js";
|
|
40
42
|
import { credentialAgent } from "./credential-agent.js";
|
|
41
43
|
|
|
44
|
+
const SUPPORTED_SANDBOX_BOUNDARIES = new Set(Object.values(SANDBOX_BOUNDARIES));
|
|
45
|
+
const SUPPORTED_SANDBOX_PROFILES = new Set([
|
|
46
|
+
"default",
|
|
47
|
+
"strict",
|
|
48
|
+
"network-only",
|
|
49
|
+
]);
|
|
50
|
+
|
|
42
51
|
// 延迟导入避免循环依赖
|
|
43
52
|
let _traceCtx = null;
|
|
44
53
|
let _rpl = null;
|
|
@@ -189,6 +198,222 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
189
198
|
return safe;
|
|
190
199
|
}
|
|
191
200
|
|
|
201
|
+
_normalizePluginExecutableIdentity(raw) {
|
|
202
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
203
|
+
const realPath =
|
|
204
|
+
typeof raw.realPath === "string" && raw.realPath ? raw.realPath : null;
|
|
205
|
+
const sha256 =
|
|
206
|
+
typeof raw.sha256 === "string" && /^[a-f0-9]{64}$/i.test(raw.sha256)
|
|
207
|
+
? raw.sha256.toLowerCase()
|
|
208
|
+
: null;
|
|
209
|
+
if (!realPath || !sha256) return null;
|
|
210
|
+
return {
|
|
211
|
+
contractVersion: 1,
|
|
212
|
+
realPath,
|
|
213
|
+
sha256,
|
|
214
|
+
bytes:
|
|
215
|
+
Number.isSafeInteger(raw.bytes) && raw.bytes >= 0 ? raw.bytes : null,
|
|
216
|
+
fileId:
|
|
217
|
+
raw.dev !== undefined && raw.ino !== undefined
|
|
218
|
+
? {
|
|
219
|
+
dev: String(raw.dev),
|
|
220
|
+
ino: String(raw.ino),
|
|
221
|
+
}
|
|
222
|
+
: null,
|
|
223
|
+
mtimeMs: Number.isFinite(raw.mtimeMs) ? raw.mtimeMs : null,
|
|
224
|
+
attestation: "realpath-file-id-sha256",
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_freezeExecutableIdentity(identity) {
|
|
229
|
+
if (!identity) return null;
|
|
230
|
+
return Object.freeze({
|
|
231
|
+
...identity,
|
|
232
|
+
fileId: identity.fileId ? Object.freeze({ ...identity.fileId }) : null,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
_normalizePluginRootIdentity(raw) {
|
|
237
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
|
238
|
+
if (
|
|
239
|
+
typeof raw.realPath !== "string" ||
|
|
240
|
+
!raw.realPath ||
|
|
241
|
+
raw.dev === undefined ||
|
|
242
|
+
raw.ino === undefined
|
|
243
|
+
) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
return Object.freeze({
|
|
247
|
+
realPath: raw.realPath,
|
|
248
|
+
fileId: Object.freeze({
|
|
249
|
+
dev: String(raw.dev),
|
|
250
|
+
ino: String(raw.ino),
|
|
251
|
+
}),
|
|
252
|
+
attestation: "realpath-directory-file-id",
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Normalize the private execution contract produced by agent-core for one
|
|
258
|
+
* direct, already-attested strict Plugin Node or static-native bin. Plugin
|
|
259
|
+
* manifests cannot provide this top-level option; they can only add
|
|
260
|
+
* required boundaries.
|
|
261
|
+
*/
|
|
262
|
+
_normalizeSandboxExecutionContract(raw, options, requiredBoundaries, launch) {
|
|
263
|
+
if (raw === undefined) return null;
|
|
264
|
+
const invalid = (message) => {
|
|
265
|
+
throw this._sandboxBoundaryError(
|
|
266
|
+
"invalid_sandbox_execution_contract",
|
|
267
|
+
message,
|
|
268
|
+
{
|
|
269
|
+
requiredBoundaries,
|
|
270
|
+
missingBoundaries: requiredBoundaries,
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
};
|
|
274
|
+
if (
|
|
275
|
+
!consumeIssuedPluginSandboxExecutionContract(raw, {
|
|
276
|
+
origin: options.origin,
|
|
277
|
+
command: launch?.command,
|
|
278
|
+
args: launch?.args,
|
|
279
|
+
cwd: options.cwd,
|
|
280
|
+
pluginId: options.pluginId,
|
|
281
|
+
pluginVersion: options.pluginVersion,
|
|
282
|
+
pluginSource: options.pluginSource,
|
|
283
|
+
pluginExecutableIdentity: options.pluginExecutableIdentity,
|
|
284
|
+
requiredBoundaries,
|
|
285
|
+
sync: launch?.sync === true,
|
|
286
|
+
})
|
|
287
|
+
) {
|
|
288
|
+
return invalid(
|
|
289
|
+
"sandboxExecutionContract was not issued for this plugin launch provenance",
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
293
|
+
return invalid("sandboxExecutionContract must be an object");
|
|
294
|
+
}
|
|
295
|
+
const supportedKeys = new Set([
|
|
296
|
+
"contractVersion",
|
|
297
|
+
"kind",
|
|
298
|
+
"pluginRoot",
|
|
299
|
+
"workingDirectory",
|
|
300
|
+
"runtimePath",
|
|
301
|
+
"rootIdentity",
|
|
302
|
+
"entryIdentity",
|
|
303
|
+
"runtimeIdentity",
|
|
304
|
+
]);
|
|
305
|
+
const unsupportedKey = Object.keys(raw).find(
|
|
306
|
+
(key) => !supportedKeys.has(key),
|
|
307
|
+
);
|
|
308
|
+
if (unsupportedKey) {
|
|
309
|
+
return invalid(
|
|
310
|
+
`sandboxExecutionContract contains unsupported field: ${unsupportedKey}`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
if (
|
|
314
|
+
raw.contractVersion !== 1 ||
|
|
315
|
+
![
|
|
316
|
+
"strict-plugin-node-bin",
|
|
317
|
+
"strict-plugin-native-static-elf-bin",
|
|
318
|
+
].includes(raw.kind)
|
|
319
|
+
) {
|
|
320
|
+
return invalid("unsupported sandboxExecutionContract kind or version");
|
|
321
|
+
}
|
|
322
|
+
if (
|
|
323
|
+
options.origin !== "plugin:bin" ||
|
|
324
|
+
typeof options.pluginId !== "string" ||
|
|
325
|
+
!options.pluginId ||
|
|
326
|
+
options.shell !== false
|
|
327
|
+
) {
|
|
328
|
+
return invalid(
|
|
329
|
+
"sandboxExecutionContract is restricted to direct plugin:bin shell:false launches",
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
for (const [label, value] of [
|
|
334
|
+
["pluginRoot", raw.pluginRoot],
|
|
335
|
+
["workingDirectory", raw.workingDirectory],
|
|
336
|
+
["runtimePath", raw.runtimePath],
|
|
337
|
+
]) {
|
|
338
|
+
if (
|
|
339
|
+
typeof value !== "string" ||
|
|
340
|
+
!value ||
|
|
341
|
+
value.includes("\0") ||
|
|
342
|
+
!path.isAbsolute(value)
|
|
343
|
+
) {
|
|
344
|
+
return invalid(`sandboxExecutionContract.${label} must be absolute`);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const entryIdentity = this._normalizePluginExecutableIdentity(
|
|
348
|
+
raw.entryIdentity,
|
|
349
|
+
);
|
|
350
|
+
const runtimeIdentity = this._normalizePluginExecutableIdentity(
|
|
351
|
+
raw.runtimeIdentity,
|
|
352
|
+
);
|
|
353
|
+
const rootIdentity = this._normalizePluginRootIdentity(raw.rootIdentity);
|
|
354
|
+
if (!rootIdentity || !entryIdentity || !runtimeIdentity) {
|
|
355
|
+
return invalid(
|
|
356
|
+
"sandboxExecutionContract requires attested root, entry, and runtime identities",
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
if (
|
|
360
|
+
rootIdentity.realPath !== raw.pluginRoot ||
|
|
361
|
+
raw.runtimeIdentity?.requestedPath !== path.resolve(process.execPath) ||
|
|
362
|
+
raw.runtimePath !== runtimeIdentity.realPath ||
|
|
363
|
+
!path.isAbsolute(entryIdentity.realPath) ||
|
|
364
|
+
!path.isAbsolute(runtimeIdentity.realPath)
|
|
365
|
+
) {
|
|
366
|
+
return invalid(
|
|
367
|
+
"sandboxExecutionContract identity paths do not match the launch contract",
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const provenanceIdentity = this._normalizePluginExecutableIdentity(
|
|
372
|
+
options.pluginExecutableIdentity,
|
|
373
|
+
);
|
|
374
|
+
if (
|
|
375
|
+
!provenanceIdentity ||
|
|
376
|
+
provenanceIdentity.realPath !== entryIdentity.realPath ||
|
|
377
|
+
provenanceIdentity.sha256 !== entryIdentity.sha256
|
|
378
|
+
) {
|
|
379
|
+
return invalid(
|
|
380
|
+
"sandboxExecutionContract entry identity does not match plugin provenance",
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
const entryRelative = path.relative(
|
|
384
|
+
path.resolve(raw.pluginRoot),
|
|
385
|
+
path.resolve(entryIdentity.realPath),
|
|
386
|
+
);
|
|
387
|
+
if (
|
|
388
|
+
entryRelative === "" ||
|
|
389
|
+
entryRelative === ".." ||
|
|
390
|
+
entryRelative.startsWith(`..${path.sep}`) ||
|
|
391
|
+
path.isAbsolute(entryRelative)
|
|
392
|
+
) {
|
|
393
|
+
return invalid(
|
|
394
|
+
"sandboxExecutionContract entry must be inside pluginRoot",
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return Object.freeze({
|
|
399
|
+
contractVersion: 1,
|
|
400
|
+
kind: raw.kind,
|
|
401
|
+
pluginRoot: raw.pluginRoot,
|
|
402
|
+
workingDirectory: raw.workingDirectory,
|
|
403
|
+
runtimePath: raw.runtimePath,
|
|
404
|
+
rootIdentity,
|
|
405
|
+
entryIdentity: this._freezeExecutableIdentity(entryIdentity),
|
|
406
|
+
runtimeIdentity: this._freezeExecutableIdentity(runtimeIdentity),
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
_stripPluginControlOptions(options) {
|
|
411
|
+
delete options.pluginId;
|
|
412
|
+
delete options.pluginVersion;
|
|
413
|
+
delete options.pluginSource;
|
|
414
|
+
delete options.pluginExecutableIdentity;
|
|
415
|
+
}
|
|
416
|
+
|
|
192
417
|
_sandboxStrictEnabled() {
|
|
193
418
|
return process.env.CC_SANDBOX_STRICT === "1";
|
|
194
419
|
}
|
|
@@ -197,16 +422,98 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
197
422
|
return process.env.CC_SANDBOX_DISABLE === "1";
|
|
198
423
|
}
|
|
199
424
|
|
|
200
|
-
|
|
425
|
+
/**
|
|
426
|
+
* Normalize the public per-spawn boundary contract. `requiredBoundaries` is
|
|
427
|
+
* retained as a top-level alias so callers can adopt the contract without
|
|
428
|
+
* migrating all spawn options to `sandboxPolicy` in one change.
|
|
429
|
+
*
|
|
430
|
+
* @param {Object} options
|
|
431
|
+
* @returns {{profile: "default"|"strict"|"network-only"|null, requiredBoundaries: string[]}}
|
|
432
|
+
*/
|
|
433
|
+
_normalizeSandboxPolicy(options = {}, launch = {}) {
|
|
434
|
+
const rawPolicy = options.sandboxPolicy;
|
|
435
|
+
if (
|
|
436
|
+
rawPolicy !== undefined &&
|
|
437
|
+
(rawPolicy === null ||
|
|
438
|
+
typeof rawPolicy !== "object" ||
|
|
439
|
+
Array.isArray(rawPolicy))
|
|
440
|
+
) {
|
|
441
|
+
throw this._sandboxBoundaryError(
|
|
442
|
+
"invalid_sandbox_policy",
|
|
443
|
+
"sandboxPolicy must be an object",
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const direct = options.requiredBoundaries;
|
|
448
|
+
const nested = rawPolicy?.requiredBoundaries;
|
|
449
|
+
for (const [name, value] of [
|
|
450
|
+
["requiredBoundaries", direct],
|
|
451
|
+
["sandboxPolicy.requiredBoundaries", nested],
|
|
452
|
+
]) {
|
|
453
|
+
if (value !== undefined && !Array.isArray(value)) {
|
|
454
|
+
throw this._sandboxBoundaryError(
|
|
455
|
+
"invalid_required_boundaries",
|
|
456
|
+
`${name} must be an array`,
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const requiredBoundaries = [];
|
|
462
|
+
for (const boundary of [...(direct || []), ...(nested || [])]) {
|
|
463
|
+
if (
|
|
464
|
+
typeof boundary !== "string" ||
|
|
465
|
+
!SUPPORTED_SANDBOX_BOUNDARIES.has(boundary)
|
|
466
|
+
) {
|
|
467
|
+
throw this._sandboxBoundaryError(
|
|
468
|
+
"invalid_required_boundary",
|
|
469
|
+
`Unsupported sandbox boundary: ${String(boundary)}`,
|
|
470
|
+
{
|
|
471
|
+
requiredBoundaries,
|
|
472
|
+
missingBoundaries: [String(boundary)],
|
|
473
|
+
},
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
if (!requiredBoundaries.includes(boundary)) {
|
|
477
|
+
requiredBoundaries.push(boundary);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const profile = rawPolicy?.profile ?? null;
|
|
482
|
+
if (profile !== null && !SUPPORTED_SANDBOX_PROFILES.has(profile)) {
|
|
483
|
+
throw this._sandboxBoundaryError(
|
|
484
|
+
"invalid_sandbox_profile",
|
|
485
|
+
`Unsupported sandbox profile: ${String(profile)}`,
|
|
486
|
+
{ requiredBoundaries },
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
const executionContract = this._normalizeSandboxExecutionContract(
|
|
490
|
+
options.sandboxExecutionContract,
|
|
491
|
+
options,
|
|
492
|
+
requiredBoundaries,
|
|
493
|
+
launch,
|
|
494
|
+
);
|
|
495
|
+
return { profile, requiredBoundaries, executionContract };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
_stripSandboxControlOptions(options) {
|
|
499
|
+
delete options.sandboxPolicy;
|
|
500
|
+
delete options.requiredBoundaries;
|
|
501
|
+
delete options.sandboxExecutionContract;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
_sandboxUnavailablePlan(command, args, options, reason, sandboxPolicy = {}) {
|
|
201
505
|
return {
|
|
202
506
|
contractVersion: 1,
|
|
203
507
|
applied: false,
|
|
204
508
|
platform: process.platform,
|
|
205
|
-
profile: "default",
|
|
509
|
+
profile: sandboxPolicy.profile || "default",
|
|
206
510
|
command,
|
|
207
511
|
args: [...(args || [])],
|
|
208
512
|
options: { ...options },
|
|
209
513
|
enforcement: null,
|
|
514
|
+
backend: null,
|
|
515
|
+
guarantees: [],
|
|
516
|
+
requiredBoundaries: [...(sandboxPolicy.requiredBoundaries || [])],
|
|
210
517
|
reason,
|
|
211
518
|
postSpawn: { required: false, mode: "none" },
|
|
212
519
|
};
|
|
@@ -219,6 +526,27 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
219
526
|
return error;
|
|
220
527
|
}
|
|
221
528
|
|
|
529
|
+
_sandboxBoundaryError(reason, message = reason, metadata = {}) {
|
|
530
|
+
const error = this._sandboxError(reason, message);
|
|
531
|
+
error.code = "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
|
|
532
|
+
error.sandboxFailClosed = true;
|
|
533
|
+
error.requiredBoundaries = [...(metadata.requiredBoundaries || [])];
|
|
534
|
+
error.actualGuarantees = [...(metadata.actualGuarantees || [])];
|
|
535
|
+
error.missingBoundaries = [...(metadata.missingBoundaries || [])];
|
|
536
|
+
error.sandboxBackend = metadata.sandboxBackend || null;
|
|
537
|
+
error.sandboxCandidateBackend = metadata.sandboxCandidateBackend || null;
|
|
538
|
+
error.sandboxRuntimeProbe = metadata.sandboxRuntimeProbe
|
|
539
|
+
? { ...metadata.sandboxRuntimeProbe }
|
|
540
|
+
: null;
|
|
541
|
+
error.sandboxPolicyAttested =
|
|
542
|
+
typeof metadata.sandboxPolicyAttested === "boolean"
|
|
543
|
+
? metadata.sandboxPolicyAttested
|
|
544
|
+
: null;
|
|
545
|
+
error.sandboxPolicyDigest = metadata.sandboxPolicyDigest || null;
|
|
546
|
+
error.sandboxCandidateReason = metadata.sandboxCandidateReason || null;
|
|
547
|
+
return error;
|
|
548
|
+
}
|
|
549
|
+
|
|
222
550
|
_validateSandboxPlan(plan) {
|
|
223
551
|
if (!plan || typeof plan !== "object") {
|
|
224
552
|
throw this._sandboxError(
|
|
@@ -262,6 +590,125 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
262
590
|
"Unavailable sandbox spawn plan must provide a reason",
|
|
263
591
|
);
|
|
264
592
|
}
|
|
593
|
+
const backend = plan.backend ?? plan.enforcement ?? null;
|
|
594
|
+
if (plan.applied && typeof backend !== "string") {
|
|
595
|
+
throw this._sandboxError(
|
|
596
|
+
"invalid_sandbox_plan",
|
|
597
|
+
"Applied sandbox spawn plan must name its backend",
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
const guarantees = plan.guarantees ?? [];
|
|
601
|
+
if (
|
|
602
|
+
!Array.isArray(guarantees) ||
|
|
603
|
+
guarantees.some(
|
|
604
|
+
(boundary) =>
|
|
605
|
+
typeof boundary !== "string" ||
|
|
606
|
+
!SUPPORTED_SANDBOX_BOUNDARIES.has(boundary),
|
|
607
|
+
)
|
|
608
|
+
) {
|
|
609
|
+
throw this._sandboxError(
|
|
610
|
+
"invalid_sandbox_plan",
|
|
611
|
+
"Sandbox spawn plan guarantees must use supported boundary identifiers",
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
const candidateBackend = plan.candidateBackend ?? null;
|
|
615
|
+
if (candidateBackend !== null && typeof candidateBackend !== "string") {
|
|
616
|
+
throw this._sandboxError(
|
|
617
|
+
"invalid_sandbox_plan",
|
|
618
|
+
"Sandbox candidate backend must be a string",
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
const policyAttested = plan.policyAttested ?? null;
|
|
622
|
+
if (policyAttested !== null && typeof policyAttested !== "boolean") {
|
|
623
|
+
throw this._sandboxError(
|
|
624
|
+
"invalid_sandbox_plan",
|
|
625
|
+
"Sandbox policy attestation must be boolean",
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
const policyDigest = plan.policyDigest ?? null;
|
|
629
|
+
if (
|
|
630
|
+
policyDigest !== null &&
|
|
631
|
+
(typeof policyDigest !== "string" || !/^[a-f0-9]{64}$/.test(policyDigest))
|
|
632
|
+
) {
|
|
633
|
+
throw this._sandboxError(
|
|
634
|
+
"invalid_sandbox_plan",
|
|
635
|
+
"Sandbox policy digest must be a lowercase SHA-256 value",
|
|
636
|
+
);
|
|
637
|
+
}
|
|
638
|
+
let runtimeProbe = null;
|
|
639
|
+
if (plan.runtimeProbe !== null && plan.runtimeProbe !== undefined) {
|
|
640
|
+
if (
|
|
641
|
+
typeof plan.runtimeProbe !== "object" ||
|
|
642
|
+
Array.isArray(plan.runtimeProbe) ||
|
|
643
|
+
typeof plan.runtimeProbe.kind !== "string" ||
|
|
644
|
+
typeof plan.runtimeProbe.attempted !== "boolean" ||
|
|
645
|
+
typeof plan.runtimeProbe.runnable !== "boolean" ||
|
|
646
|
+
(plan.runtimeProbe.reason !== null &&
|
|
647
|
+
typeof plan.runtimeProbe.reason !== "string")
|
|
648
|
+
) {
|
|
649
|
+
throw this._sandboxError(
|
|
650
|
+
"invalid_sandbox_plan",
|
|
651
|
+
"Sandbox runtime probe must use the typed probe contract",
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
for (const field of [
|
|
655
|
+
"probeRuntime",
|
|
656
|
+
"targetRuntime",
|
|
657
|
+
"contentSnapshotScope",
|
|
658
|
+
"contentSnapshotMechanism",
|
|
659
|
+
]) {
|
|
660
|
+
if (
|
|
661
|
+
plan.runtimeProbe[field] !== undefined &&
|
|
662
|
+
(typeof plan.runtimeProbe[field] !== "string" ||
|
|
663
|
+
!plan.runtimeProbe[field])
|
|
664
|
+
) {
|
|
665
|
+
throw this._sandboxError(
|
|
666
|
+
"invalid_sandbox_plan",
|
|
667
|
+
`Sandbox runtime probe ${field} must be a non-empty string`,
|
|
668
|
+
);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
for (const field of ["contentSnapshot", "handleAtomic"]) {
|
|
672
|
+
if (
|
|
673
|
+
plan.runtimeProbe[field] !== undefined &&
|
|
674
|
+
typeof plan.runtimeProbe[field] !== "boolean"
|
|
675
|
+
) {
|
|
676
|
+
throw this._sandboxError(
|
|
677
|
+
"invalid_sandbox_plan",
|
|
678
|
+
`Sandbox runtime probe ${field} must be boolean`,
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
runtimeProbe = {
|
|
683
|
+
kind: plan.runtimeProbe.kind,
|
|
684
|
+
attempted: plan.runtimeProbe.attempted,
|
|
685
|
+
runnable: plan.runtimeProbe.runnable,
|
|
686
|
+
reason: plan.runtimeProbe.reason,
|
|
687
|
+
...(plan.runtimeProbe.probeRuntime !== undefined
|
|
688
|
+
? { probeRuntime: plan.runtimeProbe.probeRuntime }
|
|
689
|
+
: {}),
|
|
690
|
+
...(plan.runtimeProbe.targetRuntime !== undefined
|
|
691
|
+
? { targetRuntime: plan.runtimeProbe.targetRuntime }
|
|
692
|
+
: {}),
|
|
693
|
+
...(plan.runtimeProbe.contentSnapshot !== undefined
|
|
694
|
+
? { contentSnapshot: plan.runtimeProbe.contentSnapshot }
|
|
695
|
+
: {}),
|
|
696
|
+
...(plan.runtimeProbe.handleAtomic !== undefined
|
|
697
|
+
? { handleAtomic: plan.runtimeProbe.handleAtomic }
|
|
698
|
+
: {}),
|
|
699
|
+
...(plan.runtimeProbe.contentSnapshotScope !== undefined
|
|
700
|
+
? {
|
|
701
|
+
contentSnapshotScope: plan.runtimeProbe.contentSnapshotScope,
|
|
702
|
+
}
|
|
703
|
+
: {}),
|
|
704
|
+
...(plan.runtimeProbe.contentSnapshotMechanism !== undefined
|
|
705
|
+
? {
|
|
706
|
+
contentSnapshotMechanism:
|
|
707
|
+
plan.runtimeProbe.contentSnapshotMechanism,
|
|
708
|
+
}
|
|
709
|
+
: {}),
|
|
710
|
+
};
|
|
711
|
+
}
|
|
265
712
|
const postSpawn = plan.postSpawn || { required: false, mode: "none" };
|
|
266
713
|
if (
|
|
267
714
|
postSpawn.required &&
|
|
@@ -277,12 +724,54 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
277
724
|
...plan,
|
|
278
725
|
args: [...plan.args],
|
|
279
726
|
options: { ...plan.options },
|
|
727
|
+
backend,
|
|
728
|
+
guarantees: [...new Set(guarantees)],
|
|
729
|
+
candidateBackend,
|
|
730
|
+
policyAttested,
|
|
731
|
+
policyDigest,
|
|
732
|
+
runtimeProbe,
|
|
280
733
|
postSpawn: { ...postSpawn },
|
|
281
734
|
};
|
|
282
735
|
}
|
|
283
736
|
|
|
284
|
-
|
|
737
|
+
_assertRequiredSandboxBoundaries(plan, requiredBoundaries) {
|
|
738
|
+
const actualGuarantees = plan.applied ? plan.guarantees : [];
|
|
739
|
+
const guaranteed = new Set(actualGuarantees);
|
|
740
|
+
const missingBoundaries = requiredBoundaries.filter(
|
|
741
|
+
(boundary) => !guaranteed.has(boundary),
|
|
742
|
+
);
|
|
743
|
+
if (missingBoundaries.length > 0) {
|
|
744
|
+
throw this._sandboxBoundaryError(
|
|
745
|
+
"required_boundaries_unsatisfied",
|
|
746
|
+
`Sandbox backend ${plan.backend || plan.candidateBackend || "unavailable"} cannot satisfy required boundaries: ${missingBoundaries.join(", ")}`,
|
|
747
|
+
{
|
|
748
|
+
requiredBoundaries,
|
|
749
|
+
actualGuarantees,
|
|
750
|
+
missingBoundaries,
|
|
751
|
+
sandboxBackend: plan.backend,
|
|
752
|
+
sandboxCandidateBackend: plan.candidateBackend,
|
|
753
|
+
sandboxRuntimeProbe: plan.runtimeProbe,
|
|
754
|
+
sandboxPolicyAttested: plan.policyAttested,
|
|
755
|
+
sandboxPolicyDigest: plan.policyDigest,
|
|
756
|
+
sandboxCandidateReason: plan.candidateBackend ? plan.reason : null,
|
|
757
|
+
},
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
return {
|
|
761
|
+
...plan,
|
|
762
|
+
requiredBoundaries: [...requiredBoundaries],
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
_prepareSandboxPlan(
|
|
767
|
+
command,
|
|
768
|
+
args,
|
|
769
|
+
options,
|
|
770
|
+
{ sync = false, sandboxPolicy = {} } = {},
|
|
771
|
+
) {
|
|
285
772
|
const strict = this._sandboxStrictEnabled();
|
|
773
|
+
const requiredBoundaries = sandboxPolicy.requiredBoundaries || [];
|
|
774
|
+
const profile = strict ? "strict" : sandboxPolicy.profile || "default";
|
|
286
775
|
let disabledReason = null;
|
|
287
776
|
if (this._sandboxDisabledByEnvironment()) {
|
|
288
777
|
disabledReason = "disabled_by_environment";
|
|
@@ -300,58 +789,104 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
300
789
|
`Sandbox is unavailable in strict mode: ${disabledReason}`,
|
|
301
790
|
);
|
|
302
791
|
}
|
|
303
|
-
|
|
792
|
+
const unavailablePlan = this._sandboxUnavailablePlan(
|
|
304
793
|
command,
|
|
305
794
|
args,
|
|
306
795
|
options,
|
|
307
796
|
disabledReason,
|
|
797
|
+
{ ...sandboxPolicy, profile },
|
|
798
|
+
);
|
|
799
|
+
return this._assertRequiredSandboxBoundaries(
|
|
800
|
+
unavailablePlan,
|
|
801
|
+
requiredBoundaries,
|
|
308
802
|
);
|
|
309
803
|
}
|
|
310
804
|
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
805
|
+
const adapterRequest = Object.freeze({
|
|
806
|
+
profile,
|
|
807
|
+
requiredBoundaries: Object.freeze([...requiredBoundaries]),
|
|
808
|
+
sync,
|
|
809
|
+
...(sandboxPolicy.executionContract
|
|
810
|
+
? { executionContract: sandboxPolicy.executionContract }
|
|
811
|
+
: {}),
|
|
812
|
+
});
|
|
813
|
+
// Keep the legacy string profile in argument four. The built-in adapter
|
|
814
|
+
// reserves argument five for runtime injection, so the typed request is
|
|
815
|
+
// additive in argument six and legacy injected adapters can ignore it.
|
|
816
|
+
const rawPlan = this._sandboxAdapter.applySandbox(
|
|
817
|
+
command,
|
|
818
|
+
args || [],
|
|
819
|
+
options,
|
|
820
|
+
profile,
|
|
821
|
+
undefined,
|
|
822
|
+
adapterRequest,
|
|
318
823
|
);
|
|
824
|
+
let plan;
|
|
825
|
+
try {
|
|
826
|
+
plan = this._validateSandboxPlan(rawPlan);
|
|
827
|
+
plan = this._assertRequiredSandboxBoundaries(plan, requiredBoundaries);
|
|
319
828
|
|
|
320
|
-
|
|
321
|
-
throw this._sandboxError(
|
|
322
|
-
plan.reason || "sandbox_unavailable",
|
|
323
|
-
`Sandbox is unavailable in strict mode: ${
|
|
324
|
-
plan.reason || "unknown reason"
|
|
325
|
-
}`,
|
|
326
|
-
);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (plan.applied && plan.postSpawn.required) {
|
|
330
|
-
if (typeof this._sandboxAdapter.postSpawnSandbox !== "function") {
|
|
829
|
+
if (!plan.applied && strict) {
|
|
331
830
|
throw this._sandboxError(
|
|
332
|
-
"
|
|
333
|
-
|
|
831
|
+
plan.reason || "sandbox_unavailable",
|
|
832
|
+
`Sandbox is unavailable in strict mode: ${
|
|
833
|
+
plan.reason || "unknown reason"
|
|
834
|
+
}`,
|
|
334
835
|
);
|
|
335
836
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
837
|
+
|
|
838
|
+
if (plan.applied && plan.postSpawn.required) {
|
|
839
|
+
if (typeof this._sandboxAdapter.postSpawnSandbox !== "function") {
|
|
840
|
+
throw this._sandboxError(
|
|
841
|
+
"post_spawn_adapter_unavailable",
|
|
842
|
+
"Required post-spawn sandbox adapter is unavailable",
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
if (sync) {
|
|
846
|
+
throw this._sandboxError(
|
|
847
|
+
"post_spawn_unavailable_for_sync",
|
|
848
|
+
"spawnSync cannot satisfy required post-spawn sandbox enforcement",
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
if (
|
|
852
|
+
(strict || requiredBoundaries.length > 0) &&
|
|
853
|
+
plan.postSpawn.mode !== "sync"
|
|
854
|
+
) {
|
|
855
|
+
throw this._sandboxError(
|
|
856
|
+
"async_post_spawn_disallowed_in_strict_mode",
|
|
857
|
+
"Fail-closed sandbox enforcement requires synchronous post-spawn enforcement",
|
|
858
|
+
);
|
|
859
|
+
}
|
|
341
860
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
861
|
+
return plan;
|
|
862
|
+
} catch (error) {
|
|
863
|
+
if (typeof rawPlan?.cleanup === "function") {
|
|
864
|
+
try {
|
|
865
|
+
rawPlan.cleanup();
|
|
866
|
+
} catch {
|
|
867
|
+
// Preserve the boundary/contract failure that prevented execution.
|
|
868
|
+
}
|
|
347
869
|
}
|
|
870
|
+
throw error;
|
|
348
871
|
}
|
|
349
|
-
return plan;
|
|
350
872
|
}
|
|
351
873
|
|
|
352
874
|
_applySandboxAudit(auditEntry, plan, applied) {
|
|
353
875
|
auditEntry.sandboxed = applied;
|
|
354
876
|
auditEntry.sandboxProfile = plan?.profile || null;
|
|
877
|
+
auditEntry.sandboxRequired = [...(plan?.requiredBoundaries || [])];
|
|
878
|
+
auditEntry.sandboxGuarantees = applied ? [...(plan?.guarantees || [])] : [];
|
|
879
|
+
auditEntry.sandboxBackend = plan?.backend || null;
|
|
880
|
+
auditEntry.sandboxCandidateBackend = plan?.candidateBackend || null;
|
|
881
|
+
auditEntry.sandboxRuntimeProbe = plan?.runtimeProbe
|
|
882
|
+
? { ...plan.runtimeProbe }
|
|
883
|
+
: null;
|
|
884
|
+
auditEntry.sandboxPolicyAttested =
|
|
885
|
+
typeof plan?.policyAttested === "boolean" ? plan.policyAttested : null;
|
|
886
|
+
auditEntry.sandboxPolicyDigest = plan?.policyDigest || null;
|
|
887
|
+
auditEntry.sandboxCandidateReason = plan?.candidateBackend
|
|
888
|
+
? plan?.reason || null
|
|
889
|
+
: null;
|
|
355
890
|
auditEntry.sandboxEnforcement = applied
|
|
356
891
|
? plan?.enforcement || "platform"
|
|
357
892
|
: null;
|
|
@@ -365,6 +900,30 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
365
900
|
auditEntry.sandboxState = "denied";
|
|
366
901
|
auditEntry.sandboxReason =
|
|
367
902
|
error.sandboxReason || "sandbox_initialization_failed";
|
|
903
|
+
auditEntry.sandboxRequired = [
|
|
904
|
+
...(error.requiredBoundaries || auditEntry.sandboxRequired || []),
|
|
905
|
+
];
|
|
906
|
+
auditEntry.sandboxGuarantees = [
|
|
907
|
+
...(error.actualGuarantees || auditEntry.sandboxGuarantees || []),
|
|
908
|
+
];
|
|
909
|
+
auditEntry.sandboxMissing = [...(error.missingBoundaries || [])];
|
|
910
|
+
auditEntry.sandboxBackend =
|
|
911
|
+
error.sandboxBackend || auditEntry.sandboxBackend || null;
|
|
912
|
+
auditEntry.sandboxCandidateBackend =
|
|
913
|
+
error.sandboxCandidateBackend ||
|
|
914
|
+
auditEntry.sandboxCandidateBackend ||
|
|
915
|
+
null;
|
|
916
|
+
auditEntry.sandboxRuntimeProbe = error.sandboxRuntimeProbe
|
|
917
|
+
? { ...error.sandboxRuntimeProbe }
|
|
918
|
+
: auditEntry.sandboxRuntimeProbe || null;
|
|
919
|
+
auditEntry.sandboxPolicyAttested =
|
|
920
|
+
typeof error.sandboxPolicyAttested === "boolean"
|
|
921
|
+
? error.sandboxPolicyAttested
|
|
922
|
+
: (auditEntry.sandboxPolicyAttested ?? null);
|
|
923
|
+
auditEntry.sandboxPolicyDigest =
|
|
924
|
+
error.sandboxPolicyDigest || auditEntry.sandboxPolicyDigest || null;
|
|
925
|
+
auditEntry.sandboxCandidateReason =
|
|
926
|
+
error.sandboxCandidateReason || auditEntry.sandboxCandidateReason || null;
|
|
368
927
|
auditEntry.deniedReason = `sandbox-init-failed: ${error.message}`;
|
|
369
928
|
auditEntry.endTime = Date.now();
|
|
370
929
|
auditEntry.durationMs = auditEntry.endTime - startTime;
|
|
@@ -391,7 +950,11 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
391
950
|
if (!plan.applied || !plan.postSpawn.required) {
|
|
392
951
|
this._applySandboxAudit(auditEntry, plan, plan.applied);
|
|
393
952
|
if (plan.applied) this._stats.sandboxed++;
|
|
394
|
-
const ready = Promise.resolve({
|
|
953
|
+
const ready = Promise.resolve({
|
|
954
|
+
applied: plan.applied,
|
|
955
|
+
backend: plan.backend,
|
|
956
|
+
guarantees: plan.applied ? [...plan.guarantees] : [],
|
|
957
|
+
});
|
|
395
958
|
proc.sandboxReady = ready;
|
|
396
959
|
return;
|
|
397
960
|
}
|
|
@@ -403,7 +966,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
403
966
|
this._applySandboxAudit(auditEntry, plan, false);
|
|
404
967
|
auditEntry.sandboxState = "failed";
|
|
405
968
|
auditEntry.sandboxReason = `post_spawn_failed: ${error.message}`;
|
|
406
|
-
if (this._sandboxStrictEnabled()) {
|
|
969
|
+
if (this._sandboxStrictEnabled() || plan.requiredBoundaries.length > 0) {
|
|
407
970
|
if (typeof proc.once === "function") proc.once("error", () => {});
|
|
408
971
|
try {
|
|
409
972
|
proc.kill?.();
|
|
@@ -423,7 +986,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
423
986
|
}
|
|
424
987
|
|
|
425
988
|
if (postSpawnResult && typeof postSpawnResult.then === "function") {
|
|
426
|
-
if (this._sandboxStrictEnabled()) {
|
|
989
|
+
if (this._sandboxStrictEnabled() || plan.requiredBoundaries.length > 0) {
|
|
427
990
|
Promise.resolve(postSpawnResult).catch(() => {});
|
|
428
991
|
if (typeof proc.once === "function") proc.once("error", () => {});
|
|
429
992
|
try {
|
|
@@ -442,7 +1005,11 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
442
1005
|
() => {
|
|
443
1006
|
this._applySandboxAudit(auditEntry, plan, true);
|
|
444
1007
|
this._stats.sandboxed++;
|
|
445
|
-
return {
|
|
1008
|
+
return {
|
|
1009
|
+
applied: true,
|
|
1010
|
+
backend: plan.backend,
|
|
1011
|
+
guarantees: [...plan.guarantees],
|
|
1012
|
+
};
|
|
446
1013
|
},
|
|
447
1014
|
(error) => {
|
|
448
1015
|
this._applySandboxAudit(auditEntry, plan, false);
|
|
@@ -463,7 +1030,11 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
463
1030
|
|
|
464
1031
|
this._applySandboxAudit(auditEntry, plan, true);
|
|
465
1032
|
this._stats.sandboxed++;
|
|
466
|
-
proc.sandboxReady = Promise.resolve({
|
|
1033
|
+
proc.sandboxReady = Promise.resolve({
|
|
1034
|
+
applied: true,
|
|
1035
|
+
backend: plan.backend,
|
|
1036
|
+
guarantees: [...plan.guarantees],
|
|
1037
|
+
});
|
|
467
1038
|
}
|
|
468
1039
|
|
|
469
1040
|
_credentialBoundaryEnabled() {
|
|
@@ -527,6 +1098,35 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
527
1098
|
};
|
|
528
1099
|
}
|
|
529
1100
|
|
|
1101
|
+
_createCredentialContext(command, options, executionId, decision) {
|
|
1102
|
+
if (typeof this._credentialAgent.createBrokerContext !== "function") {
|
|
1103
|
+
return options.credentialContext;
|
|
1104
|
+
}
|
|
1105
|
+
const supplied = options.credentialContext || {};
|
|
1106
|
+
return this._credentialAgent.createBrokerContext({
|
|
1107
|
+
approvalId: `${executionId}:${decision}`,
|
|
1108
|
+
process: command,
|
|
1109
|
+
host:
|
|
1110
|
+
supplied.target?.host ??
|
|
1111
|
+
supplied.host ??
|
|
1112
|
+
options.credentialTarget?.host ??
|
|
1113
|
+
options.credentialTargetHost ??
|
|
1114
|
+
null,
|
|
1115
|
+
ttlMs: supplied.ttlMs ?? options.credentialTtlMs,
|
|
1116
|
+
maxUses: supplied.maxUses ?? options.credentialMaxUses,
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
_stripCredentialControlOptions(options) {
|
|
1121
|
+
delete options.credentialContext;
|
|
1122
|
+
delete options.credentialApproved;
|
|
1123
|
+
delete options.credentialApprovalId;
|
|
1124
|
+
delete options.credentialTarget;
|
|
1125
|
+
delete options.credentialTargetHost;
|
|
1126
|
+
delete options.credentialTtlMs;
|
|
1127
|
+
delete options.credentialMaxUses;
|
|
1128
|
+
}
|
|
1129
|
+
|
|
530
1130
|
_recordCredentialReport(auditEntry, report) {
|
|
531
1131
|
const filtered = report?.filtered === true;
|
|
532
1132
|
auditEntry.credentialFiltered = filtered;
|
|
@@ -629,8 +1229,28 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
629
1229
|
pluginId: options.pluginId || null,
|
|
630
1230
|
pluginVersion: options.pluginVersion || null,
|
|
631
1231
|
pluginSource: options.pluginSource || null,
|
|
1232
|
+
pluginExecutableIdentity: this._normalizePluginExecutableIdentity(
|
|
1233
|
+
options.pluginExecutableIdentity,
|
|
1234
|
+
),
|
|
1235
|
+
sandboxRequired: [],
|
|
1236
|
+
sandboxGuarantees: [],
|
|
1237
|
+
sandboxBackend: null,
|
|
632
1238
|
};
|
|
633
1239
|
|
|
1240
|
+
let sandboxPolicy;
|
|
1241
|
+
try {
|
|
1242
|
+
sandboxPolicy = this._normalizeSandboxPolicy(options, {
|
|
1243
|
+
command,
|
|
1244
|
+
args,
|
|
1245
|
+
sync: false,
|
|
1246
|
+
});
|
|
1247
|
+
auditEntry.sandboxRequired = [...sandboxPolicy.requiredBoundaries];
|
|
1248
|
+
} catch (sandboxPolicyError) {
|
|
1249
|
+
this._recordSandboxDenial(auditEntry, sandboxPolicyError, startTime);
|
|
1250
|
+
sandboxPolicyError.auditEntry = auditEntry;
|
|
1251
|
+
throw sandboxPolicyError;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
634
1254
|
if (decision === "deny" || decision === "prompt") {
|
|
635
1255
|
auditEntry.deniedReason = isDangerous
|
|
636
1256
|
? "dangerous_command"
|
|
@@ -658,6 +1278,8 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
658
1278
|
|
|
659
1279
|
// 传播traceparent环境变量
|
|
660
1280
|
const spawnOpts = this._sanitizeOptions(options);
|
|
1281
|
+
this._stripSandboxControlOptions(spawnOpts);
|
|
1282
|
+
this._stripPluginControlOptions(spawnOpts);
|
|
661
1283
|
if (traceCtx) {
|
|
662
1284
|
spawnOpts.env = { ...(spawnOpts.env || process.env) };
|
|
663
1285
|
spawnOpts.env.TRACEPARENT = traceCtx.traceparent;
|
|
@@ -665,6 +1287,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
665
1287
|
|
|
666
1288
|
// P0-1: Credential filtering (default-on) — strip secrets from env/args
|
|
667
1289
|
if (this._credentialBoundaryEnabled()) {
|
|
1290
|
+
spawnOpts.credentialContext = this._createCredentialContext(
|
|
1291
|
+
command,
|
|
1292
|
+
spawnOpts,
|
|
1293
|
+
executionId,
|
|
1294
|
+
decision,
|
|
1295
|
+
);
|
|
668
1296
|
const credentialBoundary = this._applyCredentialBoundary(
|
|
669
1297
|
command,
|
|
670
1298
|
args,
|
|
@@ -675,14 +1303,21 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
675
1303
|
args = credentialBoundary.args;
|
|
676
1304
|
auditEntry.args = [...args];
|
|
677
1305
|
this._recordCredentialReport(auditEntry, credentialBoundary.report);
|
|
1306
|
+
this._stripCredentialControlOptions(spawnOpts);
|
|
678
1307
|
}
|
|
679
1308
|
|
|
680
1309
|
// P0-1: Platform sandbox wrapping — apply macOS/Windows/Linux sandbox
|
|
681
1310
|
let sandboxPlan;
|
|
682
1311
|
try {
|
|
683
|
-
sandboxPlan = this._prepareSandboxPlan(command, args || [], spawnOpts
|
|
1312
|
+
sandboxPlan = this._prepareSandboxPlan(command, args || [], spawnOpts, {
|
|
1313
|
+
sandboxPolicy,
|
|
1314
|
+
});
|
|
684
1315
|
} catch (sandboxErr) {
|
|
685
|
-
if (
|
|
1316
|
+
if (
|
|
1317
|
+
this._sandboxStrictEnabled() ||
|
|
1318
|
+
sandboxErr.sandboxFailClosed ||
|
|
1319
|
+
sandboxPolicy.requiredBoundaries.length > 0
|
|
1320
|
+
) {
|
|
686
1321
|
this._recordSandboxDenial(auditEntry, sandboxErr, startTime);
|
|
687
1322
|
sandboxErr.auditEntry = auditEntry;
|
|
688
1323
|
throw sandboxErr;
|
|
@@ -695,6 +1330,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
695
1330
|
args || [],
|
|
696
1331
|
spawnOpts,
|
|
697
1332
|
`sandbox_init_failed: ${sandboxErr.message}`,
|
|
1333
|
+
sandboxPolicy,
|
|
698
1334
|
);
|
|
699
1335
|
}
|
|
700
1336
|
command = sandboxPlan.command;
|
|
@@ -722,6 +1358,13 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
722
1358
|
);
|
|
723
1359
|
try {
|
|
724
1360
|
this._runPostSpawnSandbox(proc, sandboxPlan, auditEntry);
|
|
1361
|
+
auditEntry.pid = proc.pid;
|
|
1362
|
+
if (Number.isSafeInteger(proc.sandboxWrapperPid)) {
|
|
1363
|
+
auditEntry.sandboxWrapperPid = proc.sandboxWrapperPid;
|
|
1364
|
+
}
|
|
1365
|
+
if (Number.isSafeInteger(proc.sandboxTargetPid)) {
|
|
1366
|
+
auditEntry.sandboxTargetPid = proc.sandboxTargetPid;
|
|
1367
|
+
}
|
|
725
1368
|
} catch (postSpawnError) {
|
|
726
1369
|
cleanupSandbox();
|
|
727
1370
|
this._recordSandboxDenial(auditEntry, postSpawnError, startTime);
|
|
@@ -809,8 +1452,28 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
809
1452
|
pluginId: options.pluginId || null,
|
|
810
1453
|
pluginVersion: options.pluginVersion || null,
|
|
811
1454
|
pluginSource: options.pluginSource || null,
|
|
1455
|
+
pluginExecutableIdentity: this._normalizePluginExecutableIdentity(
|
|
1456
|
+
options.pluginExecutableIdentity,
|
|
1457
|
+
),
|
|
1458
|
+
sandboxRequired: [],
|
|
1459
|
+
sandboxGuarantees: [],
|
|
1460
|
+
sandboxBackend: null,
|
|
812
1461
|
};
|
|
813
1462
|
|
|
1463
|
+
let sandboxPolicy;
|
|
1464
|
+
try {
|
|
1465
|
+
sandboxPolicy = this._normalizeSandboxPolicy(options, {
|
|
1466
|
+
command,
|
|
1467
|
+
args,
|
|
1468
|
+
sync: true,
|
|
1469
|
+
});
|
|
1470
|
+
auditEntry.sandboxRequired = [...sandboxPolicy.requiredBoundaries];
|
|
1471
|
+
} catch (sandboxPolicyError) {
|
|
1472
|
+
this._recordSandboxDenial(auditEntry, sandboxPolicyError, startTime);
|
|
1473
|
+
sandboxPolicyError.auditEntry = auditEntry;
|
|
1474
|
+
throw sandboxPolicyError;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
814
1477
|
if (decision === "deny" || decision === "prompt") {
|
|
815
1478
|
auditEntry.deniedReason = isDangerous
|
|
816
1479
|
? "dangerous_command"
|
|
@@ -831,6 +1494,8 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
831
1494
|
}
|
|
832
1495
|
|
|
833
1496
|
const spawnOpts = this._sanitizeOptions(options);
|
|
1497
|
+
this._stripSandboxControlOptions(spawnOpts);
|
|
1498
|
+
this._stripPluginControlOptions(spawnOpts);
|
|
834
1499
|
if (traceCtx) {
|
|
835
1500
|
spawnOpts.env = { ...(spawnOpts.env || process.env) };
|
|
836
1501
|
spawnOpts.env.TRACEPARENT = traceCtx.traceparent;
|
|
@@ -838,6 +1503,12 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
838
1503
|
|
|
839
1504
|
// P0-1: Credential filtering agent — strip secrets from env/args before spawn
|
|
840
1505
|
if (this._credentialBoundaryEnabled()) {
|
|
1506
|
+
spawnOpts.credentialContext = this._createCredentialContext(
|
|
1507
|
+
command,
|
|
1508
|
+
spawnOpts,
|
|
1509
|
+
executionId,
|
|
1510
|
+
decision,
|
|
1511
|
+
);
|
|
841
1512
|
const credentialBoundary = this._applyCredentialBoundary(
|
|
842
1513
|
command,
|
|
843
1514
|
args,
|
|
@@ -848,6 +1519,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
848
1519
|
args = credentialBoundary.args;
|
|
849
1520
|
auditEntry.args = [...args];
|
|
850
1521
|
this._recordCredentialReport(auditEntry, credentialBoundary.report);
|
|
1522
|
+
this._stripCredentialControlOptions(spawnOpts);
|
|
851
1523
|
}
|
|
852
1524
|
|
|
853
1525
|
// P0-1: Platform sandbox wrapping (sync path)
|
|
@@ -855,9 +1527,14 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
855
1527
|
try {
|
|
856
1528
|
sandboxPlan = this._prepareSandboxPlan(command, args || [], spawnOpts, {
|
|
857
1529
|
sync: true,
|
|
1530
|
+
sandboxPolicy,
|
|
858
1531
|
});
|
|
859
1532
|
} catch (sandboxErr) {
|
|
860
|
-
if (
|
|
1533
|
+
if (
|
|
1534
|
+
this._sandboxStrictEnabled() ||
|
|
1535
|
+
sandboxErr.sandboxFailClosed ||
|
|
1536
|
+
sandboxPolicy.requiredBoundaries.length > 0
|
|
1537
|
+
) {
|
|
861
1538
|
this._recordSandboxDenial(auditEntry, sandboxErr, startTime);
|
|
862
1539
|
sandboxErr.auditEntry = auditEntry;
|
|
863
1540
|
throw sandboxErr;
|
|
@@ -870,6 +1547,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
870
1547
|
args || [],
|
|
871
1548
|
spawnOpts,
|
|
872
1549
|
`sandbox_init_failed: ${sandboxErr.message}`,
|
|
1550
|
+
sandboxPolicy,
|
|
873
1551
|
);
|
|
874
1552
|
}
|
|
875
1553
|
command = sandboxPlan.command;
|
|
@@ -929,10 +1607,29 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
929
1607
|
pty: true,
|
|
930
1608
|
sandboxed: false,
|
|
931
1609
|
sandboxReason: "native_pty_host_boundary",
|
|
1610
|
+
sandboxRequired: [],
|
|
1611
|
+
sandboxGuarantees: [],
|
|
1612
|
+
sandboxBackend: null,
|
|
932
1613
|
pluginId: options.pluginId || null,
|
|
933
1614
|
pluginVersion: options.pluginVersion || null,
|
|
934
1615
|
pluginSource: options.pluginSource || null,
|
|
1616
|
+
pluginExecutableIdentity: this._normalizePluginExecutableIdentity(
|
|
1617
|
+
options.pluginExecutableIdentity,
|
|
1618
|
+
),
|
|
935
1619
|
};
|
|
1620
|
+
let sandboxPolicy;
|
|
1621
|
+
try {
|
|
1622
|
+
sandboxPolicy = this._normalizeSandboxPolicy(options, {
|
|
1623
|
+
command,
|
|
1624
|
+
args,
|
|
1625
|
+
sync: false,
|
|
1626
|
+
});
|
|
1627
|
+
auditEntry.sandboxRequired = [...sandboxPolicy.requiredBoundaries];
|
|
1628
|
+
} catch (sandboxPolicyError) {
|
|
1629
|
+
this._recordSandboxDenial(auditEntry, sandboxPolicyError, startTime);
|
|
1630
|
+
sandboxPolicyError.auditEntry = auditEntry;
|
|
1631
|
+
throw sandboxPolicyError;
|
|
1632
|
+
}
|
|
936
1633
|
if (policy === "deny" || policy === "prompt") {
|
|
937
1634
|
auditEntry.deniedReason = `policy_${policy}`;
|
|
938
1635
|
auditEntry.endTime = Date.now();
|
|
@@ -947,16 +1644,34 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
947
1644
|
error.auditEntry = auditEntry;
|
|
948
1645
|
throw error;
|
|
949
1646
|
}
|
|
1647
|
+
if (sandboxPolicy.requiredBoundaries.length > 0) {
|
|
1648
|
+
const sandboxError = this._sandboxBoundaryError(
|
|
1649
|
+
"required_boundaries_unsatisfied",
|
|
1650
|
+
`Native PTY host cannot satisfy required boundaries: ${sandboxPolicy.requiredBoundaries.join(", ")}`,
|
|
1651
|
+
{
|
|
1652
|
+
requiredBoundaries: sandboxPolicy.requiredBoundaries,
|
|
1653
|
+
missingBoundaries: sandboxPolicy.requiredBoundaries,
|
|
1654
|
+
},
|
|
1655
|
+
);
|
|
1656
|
+
this._recordSandboxDenial(auditEntry, sandboxError, startTime);
|
|
1657
|
+
sandboxError.auditEntry = auditEntry;
|
|
1658
|
+
throw sandboxError;
|
|
1659
|
+
}
|
|
950
1660
|
|
|
951
1661
|
const spawnOptions = { ...options, args: [...auditEntry.args] };
|
|
1662
|
+
this._stripSandboxControlOptions(spawnOptions);
|
|
1663
|
+
this._stripPluginControlOptions(spawnOptions);
|
|
952
1664
|
delete spawnOptions.origin;
|
|
953
1665
|
delete spawnOptions.policy;
|
|
954
1666
|
delete spawnOptions.scope;
|
|
955
|
-
delete spawnOptions.pluginId;
|
|
956
|
-
delete spawnOptions.pluginVersion;
|
|
957
|
-
delete spawnOptions.pluginSource;
|
|
958
1667
|
try {
|
|
959
1668
|
if (this._credentialBoundaryEnabled()) {
|
|
1669
|
+
spawnOptions.credentialContext = this._createCredentialContext(
|
|
1670
|
+
command,
|
|
1671
|
+
spawnOptions,
|
|
1672
|
+
executionId,
|
|
1673
|
+
policy,
|
|
1674
|
+
);
|
|
960
1675
|
const credentialBoundary = this._applyCredentialBoundary(
|
|
961
1676
|
command,
|
|
962
1677
|
args,
|
|
@@ -967,6 +1682,7 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
967
1682
|
spawnOptions.args = credentialBoundary.args;
|
|
968
1683
|
auditEntry.args = [...credentialBoundary.args];
|
|
969
1684
|
this._recordCredentialReport(auditEntry, credentialBoundary.report);
|
|
1685
|
+
this._stripCredentialControlOptions(spawnOptions);
|
|
970
1686
|
}
|
|
971
1687
|
const filteredArgs = spawnOptions.args || [];
|
|
972
1688
|
delete spawnOptions.args;
|
|
@@ -1156,5 +1872,5 @@ class ProcessExecutionBroker extends EventEmitter {
|
|
|
1156
1872
|
}
|
|
1157
1873
|
|
|
1158
1874
|
const broker = new ProcessExecutionBroker();
|
|
1159
|
-
export { broker as executionBroker };
|
|
1875
|
+
export { broker as executionBroker, SANDBOX_BOUNDARIES };
|
|
1160
1876
|
export default broker;
|