chainlesschain 0.165.6 → 0.165.8
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 +2 -1
- package/src/assets/web-panel/assets/{AIOps-vfoDb6HZ.js → AIOps-B3gWmXjo.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Cm5f7p93.js → ActionButton-G9pdT-0e.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-yRuvgWxa.js → Analytics-Ca30J7m2.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BROsC4h1.js → AppLayout-BtVTm8PU.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CimHApb1.js → Artifacts-BhViNa6u.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DI8kr8zI.js → Audit-DhKq-FnY.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DSLzKy-n.js → BackgroundAgents-BL0SVbnE.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DkH_n2r1.js → Backup-BWkblnFi.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CKSrUoRA.js → BaseInput-ClaRNrF8.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-ClwfMXUS.js → Chat-sZE2Z77o.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CjMaqfL7.js → ChatBubbleRenderer-BAfKOnRe.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BYTehcQG.js → Checkbox-Cvhay5WM.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B8AMcp6J.js → Codegen-BpejL-4N.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BNSogPo3.js → Col-g2Ki6g_1.js} +1 -1
- package/src/assets/web-panel/assets/{Community-JxPvGb8O.js → Community-D2xKbNo9.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DF7bFAgE.js → Compact-DbL1Pds3.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bvhqnwrq.js → Compliance-DGtZCV4c.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BDS0CJvM.js → Cowork-4kQpoNsI.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DqvRfxoQ.js → Cron-BxTYXXAN.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CDcIvnqS.js → Crosschain-k0J0LT2N.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D7jaUxU3.js → DID-BaqhoPgN.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-B5A18wEV.js → Dashboard-T4RVPIM1.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-_6IVkGag.js → Dropdown-DszOToWq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-C9PeiKCG.js → EmailListRenderer-C9KJU7HF.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DXjuEHrX.js → FamilyGuardDashboard-Cmr0JnsA.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B4IAR9zv.js → Federation-BI-_2xZX.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CxQeSNx1.js → FormItemContext-DsnDEPsl.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B8GS-Y3N.js → GenericCardRenderer-BLncAKKK.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BS8SouUX.js → Git-BPQhGXDn.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Dq9aYb5Q.js → Governance-CyElbUQW.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BrCEe9AJ.js → Inference-CcSntBQD.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DybOpiY2.js → KnowledgeGraph-p6e7HZ2r.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DBpzbRfx.js → Logs-CNEtr-wz.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-Bj9xI0YH.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Cc7CCSCu.js → Marketplace-Dd2notAJ.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DFaIhOfu.js → McpTools-BBsN2CSW.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-KCqlp9ol.js → Memory-Dg2uTvwx.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-Bys84ySN.js → MobileBridge-r1YMRiJV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-6qU-je.js → MobileProjects-C-89kSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BgY-fvCq.js → Mtc-CrPZcVgf.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-B8pCmqQJ.js → MtcAudit-sxi7hqJb.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BbOmYutA.js → Multisig-CAgOMfF1.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-BocLQz_M.js → NLProgramming-DafaPts3.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CSwbxEop.js → Notes-CJM-xUW_.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-CqFNSYG3.js → NotificationSettings-L0ZjfAh_.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-C6gMfAk7.js → OrderTableRenderer-BB1CvCK0.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-QrDXVdMJ.js → Organization-DeLtgAD8.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-Cxntotjt.js → Overflow-CKC2b10z.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CIdBOwNq.js → P2P-eyf2rzY1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CT5aP8rD.js → PdhVaultBrowser-5upNIOW8.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DH7OM8XU.js → Permissions-B8p3W6JK.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CnJqbWce.js → PersonalDataHub-BnRMxTq3.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-5C3HG1kH.js → Pipeline-DjCNI1li.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-B4yIp7xK.js → Privacy-u6gXLbWT.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BMvt-2M0.js → ProjectInit-BZAw4tkM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CvSTEMRG.js → ProjectSettings-B1Tr72q7.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BKBJxEek.js → Projects-DO7HFuBQ.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-BdwUqFWN.js → Providers-BjCztyhj.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DtnCKlTm.js → QrScannerModal-ZWyCUbR1.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-sTbgwyNr.js → QuickAsk-DhDjeW4G.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bzr14gSb.js → Recommend-R_CN2MuU.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-C_TF5IaJ.js → RemoteSession-uxFcujeu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-GCAM4H2K.js → Reputation-DX8D7rks.js} +1 -1
- package/src/assets/web-panel/assets/{Row-Dir0r6OQ.js → Row-BYahQILD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CpGnlecX.js → RssFeed-CUSQ2tm-.js} +3 -3
- package/src/assets/web-panel/assets/{Search-DMMamC6u.js → Search-CnN_S8Cs.js} +1 -1
- package/src/assets/web-panel/assets/{Security-yqay5yG-.js → Security-DcZlh-O4.js} +4 -4
- package/src/assets/web-panel/assets/{Services-C5i2wh9G.js → Services-ZhBOjzIX.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-vUqk35yj.js → Skeleton-D5Qnq_lI.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DzcSkhot.js → Skills-BC9cZ3Be.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CB6qbYNr.js → Sla-DauJ3E8_.js} +1 -1
- package/src/assets/web-panel/assets/SpeechSettings-DbNeZ_fh.js +1 -0
- package/src/assets/web-panel/assets/{SyncSettings-D-RrsxTJ.js → SyncSettings-DUvJbSct.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DFxdOJiq.js → Tasks-BXa-5noQ.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-SKAob_Aq.js → Templates-JxaiUt_3.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-BPfsfHQ-.js → Tenant-BW4ERQN8.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-8iw7FpUf.js → Terminal-DltUbdtv.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-C4Gjj5Su.js → TimelineRenderer-CMoZz0Tc.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-COYhN18e.js → Tokens-D_VEV60K.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-B-JzXcf6.js → Trigger-CCXTCs0R.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BJ5bg0nI.js → Trust-lkGRIPcG.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-BG_ByRMf.js → UkeySign-BfGeQ9HX.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B3kOnSiU.js → VideoEditing-BEC7pYvc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CcoGFnK7.js → Wallet-BZdG24o0.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DdT8vRC_.js → WebAuthn-yoEddedp.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-CZu3M9N8.js → WorkflowEditor-BXZEprOW.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DxJt3Qba.js → chat-gg0bzZal.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DdYfwJaB.js → colors-DjRVRuin.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BG3SUzwj.js → compact-item-su-dieAc.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-By51z2-1.js → createContext-Ci3S8oKF.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CxbFX9uH.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-B0apZ3KJ.js → hasIn-D8F10heH.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE76D-kA.js → index-11wtYQiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkvcEo4p.js → index-1IgxgALJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bos5_aMV.js → index-1agckWSq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfWYsB9t.js → index-5JqhujnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CAhHirVH.js → index-6Tniqk1s.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYINoRLK.js → index-8Z-8BIlN.js} +1 -1
- package/src/assets/web-panel/assets/{index-B5rLiEo0.js → index-BCI5v0yB.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZ_0qm4g.js → index-BDJntYmJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CWEuZWCK.js → index-BGzixOGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BLIbGOLX.js → index-BMMOVoLD.js} +1 -1
- package/src/assets/web-panel/assets/{index-bP_VlDb4.js → index-BTFpdBtt.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3rWuPMW.js → index-Ba_HyPnE.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da_WBdLX.js → index-BbF1IHof.js} +1 -1
- package/src/assets/web-panel/assets/{index-RD6O-0C6.js → index-BgT9su_e.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpjZhgNd.js → index-BzPB6kGZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Q2SCX-MI.js → index-C6ygzplr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIrJfqO4.js → index-CC3ZDjzY.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBPf3TpM.js → index-CEmXRhAE.js} +1 -1
- package/src/assets/web-panel/assets/index-CJGOlduY.js +1 -0
- package/src/assets/web-panel/assets/{index-Cp6e-hGw.js → index-CMPqd5Cv.js} +1 -1
- package/src/assets/web-panel/assets/{index-kf57LJ93.js → index-COWd8uGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW93tZDv.js → index-CPIYSp4H.js} +1 -1
- package/src/assets/web-panel/assets/{index-BewKYrX9.js → index-CR0h6seT.js} +1 -1
- package/src/assets/web-panel/assets/{index-p1_8SbI1.js → index-CUmDhC4j.js} +1 -1
- package/src/assets/web-panel/assets/index-CkBCN5yH.js +1 -0
- package/src/assets/web-panel/assets/{index-ZxtP8jG7.js → index-CusgZPtc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWF8_mkx.js → index-DBAETXft.js} +1 -1
- package/src/assets/web-panel/assets/{index-BP94CrBV.js → index-DIJ2qFOC.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNIxXMmN.js → index-Dbq4NMET.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzmQElZy.js → index-DkMEF5VT.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZFbps9po.js → index-DkfQR3hM.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dz49VOyQ.js → index-DnLucOn9.js} +1 -1
- package/src/assets/web-panel/assets/{index-oxRrmFO9.js → index-DugB_aKa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bissefox.js → index-DyLUarhZ.js} +3 -3
- package/src/assets/web-panel/assets/{index-DaIz0NAW.js → index-T6_6ygJZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-SQqOlR3d.js → index-Tu8eWqh-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCn8IcxV.js → index-W7XUgdVL.js} +1 -1
- package/src/assets/web-panel/assets/{index-3jWkIu5j.js → index-dHZinfko.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1C9i79A.js → index-jyNgp5FI.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BWJktcg8.js → initDefaultProps-Cv4p8e4w.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BEXBzUFV.js → motion-B8EwmMOw.js} +1 -1
- package/src/assets/web-panel/assets/{move-BUKwn2sx.js → move-B8R9F8TK.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-FTI7btqQ.js → mtc-parser-DoFYz5Bp.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CsLtSPH3.js → omit-CHWYLBjI.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-DWZxsT_S.js → pickAttrs-CXuPDO77.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Bcfs43CX.js → placementArrow-BaAtUUon.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-a9bcC1p-.js → responsiveObserve-DCc4zcDD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BDTzgOXN.js → slide-B_lx89oZ.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Iq1j_HUg.js → statusUtils-DE8DPmR0.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DAZEYxeh.js → styleChecker-1ktXOsL_.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-DVkjyDrh.js → useFlexGapSupport-xH89-wjh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-wXTpNTHt.js → useFs-DscvfW5p.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CtQM_aZr.js → usePersonalDataHub-RdMKQta7.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-hjEoG85v.js → vnode-Z3wTni2T.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BHVpyKg_.js → zoom-B0iChCsm.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +3 -3
- package/src/commands/agent.js +91 -60
- package/src/commands/git.js +39 -0
- package/src/commands/plugin.js +17 -2
- package/src/commands/session-group.js +131 -0
- package/src/commands/session-location.js +48 -0
- package/src/commands/session.js +11 -0
- package/src/commands/sync-providers.js +4 -1
- package/src/commands/update.js +1 -0
- package/src/data/changelog.json +27 -1
- package/src/gateways/ws/ws-agent-handler.js +20 -12
- package/src/harness/jsonl-session-store.js +279 -37
- package/src/harness/mcp-client.js +701 -91
- package/src/harness/session-index.js +15 -4
- package/src/lib/agent-worktree.js +8 -1
- package/src/lib/auto-mode-safety-classifier.js +3 -17
- package/src/lib/background-agent-keeper-protocol.js +26 -6
- package/src/lib/background-agent-supervisor.js +17 -1
- package/src/lib/browser-evidence.js +1405 -0
- package/src/lib/chrome-connector.js +465 -79
- package/src/lib/claude-project-auto-memory.js +481 -0
- package/src/lib/claude-project-storage-layout.js +104 -0
- package/src/lib/cloud/cloud-client.js +40 -0
- package/src/lib/execution-location-local-supervisor.mjs +226 -0
- package/src/lib/execution-location-runner-lifecycle.js +991 -0
- package/src/lib/execution-location-target-preflight.js +333 -0
- package/src/lib/execution-location-target.js +783 -13
- package/src/lib/file-lines.js +6 -1
- package/src/lib/gateway-client-resilience.js +571 -0
- package/src/lib/gitlab-mr-worktree.js +176 -0
- package/src/lib/hooks-v2-producers.js +119 -15
- package/src/lib/hooks-v2-workspace-context.js +44 -1
- package/src/lib/host-resource-budget.js +295 -0
- package/src/lib/ide-context.js +15 -2
- package/src/lib/llm-config-defaults.js +43 -0
- package/src/lib/mcp-lifecycle-authority.js +887 -0
- package/src/lib/mcp-oauth.js +101 -20
- package/src/lib/mcp-tls.js +103 -3
- package/src/lib/paths.js +260 -32
- package/src/lib/permission-request.js +48 -0
- package/src/lib/plugin-runtime/bin.js +3 -1
- package/src/lib/plugin-runtime/hooks.js +3 -1
- package/src/lib/plugin-runtime/install.js +117 -1
- package/src/lib/plugin-runtime/lsp.js +4 -4
- package/src/lib/plugin-runtime/marketplace-archive-source.js +40 -5
- package/src/lib/plugin-runtime/marketplace-catalog.js +13 -2
- package/src/lib/plugin-runtime/marketplace-command-descriptor.js +343 -0
- package/src/lib/plugin-runtime/marketplace-command-source.js +580 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +33 -8
- package/src/lib/plugin-runtime/mcp.js +3 -1
- package/src/lib/plugin-runtime/monitors.js +3 -1
- package/src/lib/plugin-runtime/remote-source.js +151 -6
- package/src/lib/plugin-runtime/settings.js +3 -1
- package/src/lib/plugin-runtime/trust.js +274 -13
- package/src/lib/process-execution-broker/credential-agent.js +210 -41
- package/src/lib/process-execution-broker/index.js +15 -1
- package/src/lib/process-execution-broker/linux-cgroup-v2.js +391 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +50 -28
- package/src/lib/project-mcp-trust.js +165 -10
- package/src/lib/resume-session.js +9 -6
- package/src/lib/session-core-singletons.js +26 -11
- package/src/lib/session-host-lease.js +52 -16
- package/src/lib/session-lifecycle-scope.js +153 -0
- package/src/lib/session-message-fabric.js +168 -21
- package/src/lib/session-projection.js +244 -4
- package/src/lib/session-store-guard.js +3 -1
- package/src/lib/session-workbench-store.js +466 -0
- package/src/lib/spellcheck.js +211 -0
- package/src/lib/sub-agent-context.js +9 -9
- package/src/lib/sync-cli-db.js +485 -129
- package/src/lib/sync-credentials.js +31 -20
- package/src/lib/web-fetch.js +112 -40
- package/src/lib/web-search.js +127 -61
- package/src/lib/workspace-trust.js +793 -0
- package/src/repl/agent-repl.js +124 -5
- package/src/repl/permission-prompt.js +10 -12
- package/src/runtime/agent-core.js +311 -18
- package/src/runtime/bootstrap.js +37 -13
- package/src/runtime/coding-agent-contract-shared.cjs +11 -4
- package/src/runtime/headless-runner.js +234 -36
- package/src/runtime/headless-stream.js +181 -6
- package/src/runtime/mcp-config.js +298 -8
- package/src/runtime/policies/agent-policy.js +8 -0
- package/src/runtime/status-diagnostics-lite.js +9 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +0 -1
- package/src/assets/web-panel/assets/SpeechSettings-BHUI7cox.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +0 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +0 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +0 -1
|
@@ -215,7 +215,9 @@ export function collectPluginHooks(opts = {}) {
|
|
|
215
215
|
}
|
|
216
216
|
// A hook runs a shell command — gate it behind trust so a cloned repo's
|
|
217
217
|
// project plugin can't run commands the moment the agent starts.
|
|
218
|
-
const { trusted, skipped } = partitionByTrust(plugins
|
|
218
|
+
const { trusted, skipped } = partitionByTrust(plugins, {
|
|
219
|
+
workspaceRoot: opts.cwd,
|
|
220
|
+
});
|
|
219
221
|
warnUntrustedOnce(
|
|
220
222
|
skipped.filter((p) => p.manifest?.components?.hooks).map((p) => p.name),
|
|
221
223
|
"hooks",
|
|
@@ -326,6 +326,10 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
326
326
|
// and may ship its own `.plugin-lock.json` or provenance metadata.
|
|
327
327
|
_deps.rmSync(path.join(staged, LOCK_FILENAME), { force: true });
|
|
328
328
|
_deps.rmSync(path.join(staged, SOURCE_METADATA_FILENAME), { force: true });
|
|
329
|
+
assertStagedRegistryCommandPayload(
|
|
330
|
+
staged,
|
|
331
|
+
opts[SOURCE_POLICY_PREFLIGHT]?.registryAuthority,
|
|
332
|
+
);
|
|
329
333
|
|
|
330
334
|
const remoteSbomPayloadComparison = buildRemoteSbomPayloadComparison({
|
|
331
335
|
remoteArtifactEvidence:
|
|
@@ -657,6 +661,19 @@ function preflightManagedSource(source, opts, action, options = {}) {
|
|
|
657
661
|
);
|
|
658
662
|
}
|
|
659
663
|
}
|
|
664
|
+
if (registryAuthority.sourceType === "command") {
|
|
665
|
+
const command = opts.sourceMetadata.catalogAuthority?.commandSource;
|
|
666
|
+
if (
|
|
667
|
+
!command ||
|
|
668
|
+
command.descriptorIdentity !== registryAuthority.declaredSource ||
|
|
669
|
+
command.payloadSha256 !== registryAuthority.payloadSha256 ||
|
|
670
|
+
command.mode !== registryAuthority.commandMode
|
|
671
|
+
) {
|
|
672
|
+
throw new Error(
|
|
673
|
+
"registry source authority does not match command source authority",
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
660
677
|
}
|
|
661
678
|
// Resolve managed identity before exists/clone/cache/process work. The
|
|
662
679
|
// manifest name is checked again by installFromDirectory after materializing
|
|
@@ -693,6 +710,7 @@ function preflightManagedSource(source, opts, action, options = {}) {
|
|
|
693
710
|
decision,
|
|
694
711
|
materialized,
|
|
695
712
|
sourceAuthority,
|
|
713
|
+
registryAuthority,
|
|
696
714
|
};
|
|
697
715
|
const boundSourceMetadata = opts.sourceMetadata
|
|
698
716
|
? bindSourcePolicyAuthority(opts.sourceMetadata, {
|
|
@@ -3638,7 +3656,16 @@ function normalizeSourceMetadata(value) {
|
|
|
3638
3656
|
value.resolvedSource,
|
|
3639
3657
|
catalogAuthority.archiveSource,
|
|
3640
3658
|
)
|
|
3641
|
-
:
|
|
3659
|
+
: catalogAuthority?.commandSource
|
|
3660
|
+
? commandSourceAuthorityDigest(
|
|
3661
|
+
value.resolvedSource,
|
|
3662
|
+
catalogAuthority.commandSource,
|
|
3663
|
+
)
|
|
3664
|
+
: pluginSourceAuthorityDigest(
|
|
3665
|
+
value.resolvedSource,
|
|
3666
|
+
"git",
|
|
3667
|
+
value.ref,
|
|
3668
|
+
)
|
|
3642
3669
|
: null);
|
|
3643
3670
|
const policyDigest = normalizeSourceAuthorityDigest(value.policyDigest);
|
|
3644
3671
|
if (resolvedSourceDigest) {
|
|
@@ -3650,6 +3677,7 @@ function normalizeSourceMetadata(value) {
|
|
|
3650
3677
|
if (catalogAuthority) metadata.catalogAuthority = catalogAuthority;
|
|
3651
3678
|
assertCatalogRemoteArtifactBindings(metadata);
|
|
3652
3679
|
assertCatalogArchiveSourceBindings(metadata);
|
|
3680
|
+
assertCatalogCommandSourceBindings(metadata);
|
|
3653
3681
|
return metadata;
|
|
3654
3682
|
}
|
|
3655
3683
|
|
|
@@ -3681,6 +3709,20 @@ function assertCatalogArchiveSourceBindings(metadata) {
|
|
|
3681
3709
|
}
|
|
3682
3710
|
}
|
|
3683
3711
|
|
|
3712
|
+
function assertCatalogCommandSourceBindings(metadata) {
|
|
3713
|
+
const command = metadata.catalogAuthority?.commandSource;
|
|
3714
|
+
if (!command) return;
|
|
3715
|
+
if (
|
|
3716
|
+
metadata.type !== "registry" ||
|
|
3717
|
+
!metadata.registry ||
|
|
3718
|
+
metadata.resolvedSource !== command.descriptorIdentity
|
|
3719
|
+
) {
|
|
3720
|
+
throw new Error(
|
|
3721
|
+
"marketplace command authority requires matching registry source metadata",
|
|
3722
|
+
);
|
|
3723
|
+
}
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3684
3726
|
function normalizeSourceAuthorityDigest(value) {
|
|
3685
3727
|
const normalized = cleanBounded(value, 64)?.toLowerCase() || null;
|
|
3686
3728
|
return normalized && /^[a-f0-9]{64}$/u.test(normalized) ? normalized : null;
|
|
@@ -3718,11 +3760,28 @@ function archiveSourceAuthorityDigest(source, archive) {
|
|
|
3718
3760
|
.digest("hex");
|
|
3719
3761
|
}
|
|
3720
3762
|
|
|
3763
|
+
function commandSourceAuthorityDigest(source, command) {
|
|
3764
|
+
return crypto
|
|
3765
|
+
.createHash("sha256")
|
|
3766
|
+
.update(
|
|
3767
|
+
canonicalJson({
|
|
3768
|
+
kind: "command",
|
|
3769
|
+
descriptorIdentity: sanitizeSource(source),
|
|
3770
|
+
mode: command.mode || command.commandMode || null,
|
|
3771
|
+
payloadSha256: command.payloadSha256 || null,
|
|
3772
|
+
}),
|
|
3773
|
+
)
|
|
3774
|
+
.digest("hex");
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3721
3777
|
function registryResolvedSourceAuthorityDigest(authority) {
|
|
3722
3778
|
if (!authority) return null;
|
|
3723
3779
|
if (authority.sourceType === "archive") {
|
|
3724
3780
|
return archiveSourceAuthorityDigest(authority.declaredSource, authority);
|
|
3725
3781
|
}
|
|
3782
|
+
if (authority.sourceType === "command") {
|
|
3783
|
+
return commandSourceAuthorityDigest(authority.declaredSource, authority);
|
|
3784
|
+
}
|
|
3726
3785
|
return pluginSourceAuthorityDigest(
|
|
3727
3786
|
authority.declaredSource,
|
|
3728
3787
|
"git",
|
|
@@ -4030,6 +4089,12 @@ function normalizeCatalogAuthority(value) {
|
|
|
4030
4089
|
value.remoteArtifactEvidence,
|
|
4031
4090
|
);
|
|
4032
4091
|
const archiveSource = normalizeArchiveSourceAuthority(value.archiveSource);
|
|
4092
|
+
const commandSource = normalizeCommandSourceAuthority(value.commandSource);
|
|
4093
|
+
if (archiveSource && commandSource) {
|
|
4094
|
+
throw new Error(
|
|
4095
|
+
"catalogAuthority cannot combine archive and command source authority",
|
|
4096
|
+
);
|
|
4097
|
+
}
|
|
4033
4098
|
const remoteSbomPayloadComparison = assertRemoteSbomPayloadComparison(
|
|
4034
4099
|
value.remoteSbomPayloadComparison,
|
|
4035
4100
|
{
|
|
@@ -4137,6 +4202,7 @@ function normalizeCatalogAuthority(value) {
|
|
|
4137
4202
|
...(artifactExpectations ? { artifactExpectations } : {}),
|
|
4138
4203
|
...(remoteArtifactEvidence ? { remoteArtifactEvidence } : {}),
|
|
4139
4204
|
...(archiveSource ? { archiveSource } : {}),
|
|
4205
|
+
...(commandSource ? { commandSource } : {}),
|
|
4140
4206
|
...(remoteSbomPayloadComparison ? { remoteSbomPayloadComparison } : {}),
|
|
4141
4207
|
...(publisherDeclaration ? { publisherDeclaration } : {}),
|
|
4142
4208
|
...(publisherAuthority ? { publisherAuthority } : {}),
|
|
@@ -4213,6 +4279,42 @@ function normalizeArchiveSourceAuthority(value) {
|
|
|
4213
4279
|
};
|
|
4214
4280
|
}
|
|
4215
4281
|
|
|
4282
|
+
function normalizeCommandSourceAuthority(value) {
|
|
4283
|
+
if (value == null) return null;
|
|
4284
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4285
|
+
throw new Error("catalogAuthority.commandSource must be an object");
|
|
4286
|
+
}
|
|
4287
|
+
const descriptorIdentity = cleanBounded(value.descriptorIdentity, 128);
|
|
4288
|
+
const payloadSha256 = cleanBounded(value.payloadSha256, 64)?.toLowerCase();
|
|
4289
|
+
const mode = value.mode;
|
|
4290
|
+
const fileCount = Number(value.fileCount);
|
|
4291
|
+
const totalBytes = Number(value.totalBytes);
|
|
4292
|
+
if (
|
|
4293
|
+
value.schemaVersion !== "cc-plugin-marketplace-command-source/v1" ||
|
|
4294
|
+
value.status !== "descriptor-executed-and-verified" ||
|
|
4295
|
+
!/^command:[a-f0-9]{32}$/u.test(descriptorIdentity || "") ||
|
|
4296
|
+
!/^[a-f0-9]{64}$/u.test(payloadSha256 || "") ||
|
|
4297
|
+
!["copy", "link"].includes(mode) ||
|
|
4298
|
+
!Number.isSafeInteger(fileCount) ||
|
|
4299
|
+
fileCount <= 0 ||
|
|
4300
|
+
fileCount > 10_000 ||
|
|
4301
|
+
!Number.isSafeInteger(totalBytes) ||
|
|
4302
|
+
totalBytes <= 0 ||
|
|
4303
|
+
totalBytes > 256 * 1024 * 1024
|
|
4304
|
+
) {
|
|
4305
|
+
throw new Error("catalogAuthority.commandSource authority is invalid");
|
|
4306
|
+
}
|
|
4307
|
+
return {
|
|
4308
|
+
schemaVersion: value.schemaVersion,
|
|
4309
|
+
status: value.status,
|
|
4310
|
+
descriptorIdentity,
|
|
4311
|
+
mode,
|
|
4312
|
+
payloadSha256,
|
|
4313
|
+
fileCount,
|
|
4314
|
+
totalBytes,
|
|
4315
|
+
};
|
|
4316
|
+
}
|
|
4317
|
+
|
|
4216
4318
|
function normalizeArtifactExpectations(value) {
|
|
4217
4319
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
4218
4320
|
const digest = (candidate, label) => {
|
|
@@ -4618,6 +4720,20 @@ function writeDurableFileAtomic(directory, target, value) {
|
|
|
4618
4720
|
}
|
|
4619
4721
|
}
|
|
4620
4722
|
|
|
4723
|
+
function assertStagedRegistryCommandPayload(staged, authority) {
|
|
4724
|
+
if (authority?.sourceType !== "command") return;
|
|
4725
|
+
const payload = buildMarketplacePayloadSbom(staged);
|
|
4726
|
+
if (
|
|
4727
|
+
payload.digest !== authority.payloadSha256 ||
|
|
4728
|
+
payload.fileCount !== authority.fileCount ||
|
|
4729
|
+
payload.totalBytes !== authority.totalBytes
|
|
4730
|
+
) {
|
|
4731
|
+
throw new Error(
|
|
4732
|
+
"registry command source payload changed before immutable installation",
|
|
4733
|
+
);
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4621
4737
|
/**
|
|
4622
4738
|
* Copy `src` → `dst`, refusing to write outside `root` and skipping symlinks
|
|
4623
4739
|
* (which could later resolve outside the plugin sandbox). Directories recurse.
|
|
@@ -63,7 +63,9 @@ export function ensurePluginLspServers(opts = {}) {
|
|
|
63
63
|
|
|
64
64
|
// An LSP server spawns a binary — gate it behind trust. Untrusted project
|
|
65
65
|
// plugins (e.g. from a cloned repo) don't get to launch processes silently.
|
|
66
|
-
const { trusted, skipped } = partitionByTrust(plugins
|
|
66
|
+
const { trusted, skipped } = partitionByTrust(plugins, {
|
|
67
|
+
workspaceRoot: cwd,
|
|
68
|
+
});
|
|
67
69
|
warnUntrustedOnce(
|
|
68
70
|
skipped
|
|
69
71
|
.filter((p) => (p.manifest?.components?.lsp || []).length > 0)
|
|
@@ -95,9 +97,7 @@ export function ensurePluginLspServers(opts = {}) {
|
|
|
95
97
|
pluginId: p.name,
|
|
96
98
|
pluginVersion: p.version,
|
|
97
99
|
pluginSource: s.absPath || p.manifest.manifestPath,
|
|
98
|
-
...(s.sandboxPolicy
|
|
99
|
-
? { sandboxPolicy: s.sandboxPolicy }
|
|
100
|
-
: {}),
|
|
100
|
+
...(s.sandboxPolicy ? { sandboxPolicy: s.sandboxPolicy } : {}),
|
|
101
101
|
});
|
|
102
102
|
registered.push({ plugin: p.name, languageId: s.languageId, id: s.id });
|
|
103
103
|
} catch {
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
assertSafeInstalledPluginStructure,
|
|
8
8
|
buildMarketplacePayloadSbom,
|
|
9
9
|
} from "./marketplace-artifact-readback.js";
|
|
10
|
+
import { createSameOriginMarketplaceHeaderFetch } from "./marketplace-command-source.js";
|
|
10
11
|
import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
|
|
11
12
|
import { fetchMarketplaceRemoteArtifact } from "./marketplace-remote-artifacts.js";
|
|
12
13
|
|
|
@@ -96,11 +97,28 @@ function parsePax(data) {
|
|
|
96
97
|
return values;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
function
|
|
100
|
+
function extractionLimits(limits = MARKETPLACE_ARCHIVE_LIMITS) {
|
|
101
|
+
const expandedBytes = Number(limits?.expandedBytes);
|
|
102
|
+
const entries = Number(limits?.entries);
|
|
103
|
+
if (
|
|
104
|
+
!Number.isSafeInteger(expandedBytes) ||
|
|
105
|
+
expandedBytes <= 0 ||
|
|
106
|
+
expandedBytes > MARKETPLACE_ARCHIVE_LIMITS.expandedBytes ||
|
|
107
|
+
!Number.isSafeInteger(entries) ||
|
|
108
|
+
entries <= 0 ||
|
|
109
|
+
entries > MARKETPLACE_ARCHIVE_LIMITS.entries
|
|
110
|
+
) {
|
|
111
|
+
throw new Error("Marketplace archive extraction limits are invalid");
|
|
112
|
+
}
|
|
113
|
+
return { expandedBytes, entries };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractVerifiedTgz(bytes, destination, limits = MARKETPLACE_ARCHIVE_LIMITS) {
|
|
117
|
+
const bounded = extractionLimits(limits);
|
|
100
118
|
let tar;
|
|
101
119
|
try {
|
|
102
120
|
tar = gunzipSync(bytes, {
|
|
103
|
-
maxOutputLength:
|
|
121
|
+
maxOutputLength: bounded.expandedBytes,
|
|
104
122
|
});
|
|
105
123
|
} catch {
|
|
106
124
|
throw new Error("Marketplace archive is not a bounded valid gzip stream");
|
|
@@ -129,7 +147,7 @@ function extractVerifiedTgz(bytes, destination) {
|
|
|
129
147
|
const data = tar.subarray(dataStart, dataEnd);
|
|
130
148
|
offset = dataStart + Math.ceil(size / 512) * 512;
|
|
131
149
|
records += 1;
|
|
132
|
-
if (records >
|
|
150
|
+
if (records > bounded.entries) {
|
|
133
151
|
throw new Error("archive contains too many entries");
|
|
134
152
|
}
|
|
135
153
|
|
|
@@ -176,7 +194,7 @@ function extractVerifiedTgz(bytes, destination) {
|
|
|
176
194
|
continue;
|
|
177
195
|
}
|
|
178
196
|
extractedBytes += data.length;
|
|
179
|
-
if (extractedBytes >
|
|
197
|
+
if (extractedBytes > bounded.expandedBytes) {
|
|
180
198
|
throw new Error("archive expanded payload exceeds its byte limit");
|
|
181
199
|
}
|
|
182
200
|
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o755 });
|
|
@@ -188,6 +206,14 @@ function extractVerifiedTgz(bytes, destination) {
|
|
|
188
206
|
return buildMarketplacePayloadSbom(destination);
|
|
189
207
|
}
|
|
190
208
|
|
|
209
|
+
// Kept deliberately private-by-convention: production callers always use the
|
|
210
|
+
// immutable limits above, while focused security tests can exercise the same
|
|
211
|
+
// parser with a tiny bounded fixture instead of allocating a 256 MiB bomb.
|
|
212
|
+
export const _marketplaceArchiveTest = Object.freeze({
|
|
213
|
+
extractVerifiedTgz: (bytes, destination, limits) =>
|
|
214
|
+
extractVerifiedTgz(bytes, destination, limits),
|
|
215
|
+
});
|
|
216
|
+
|
|
191
217
|
function cacheRoot(configured) {
|
|
192
218
|
return (
|
|
193
219
|
configured || path.join(getElectronUserDataDir(), "plugin-archive-sources")
|
|
@@ -240,6 +266,15 @@ export async function fetchAndMaterializeMarketplaceArchive(options = {}) {
|
|
|
240
266
|
(options.proxyUrl || options.pacFile || options.caFile)
|
|
241
267
|
? createMarketplaceNetworkTransport(options)
|
|
242
268
|
: null;
|
|
269
|
+
const transportFetch =
|
|
270
|
+
options.fetchImpl || networkTransport?.fetch || globalThis.fetch?.bind(globalThis);
|
|
271
|
+
const fetchImpl = options.requestHeaders
|
|
272
|
+
? createSameOriginMarketplaceHeaderFetch(
|
|
273
|
+
transportFetch,
|
|
274
|
+
new URL(options.registryUrl).origin,
|
|
275
|
+
options.requestHeaders,
|
|
276
|
+
)
|
|
277
|
+
: transportFetch;
|
|
243
278
|
let artifact;
|
|
244
279
|
try {
|
|
245
280
|
artifact = await fetchMarketplaceRemoteArtifact({
|
|
@@ -253,7 +288,7 @@ export async function fetchAndMaterializeMarketplaceArchive(options = {}) {
|
|
|
253
288
|
offline: options.offline === true,
|
|
254
289
|
cacheDir: options.artifactCacheDir,
|
|
255
290
|
timeoutMs: options.timeoutMs,
|
|
256
|
-
fetchImpl
|
|
291
|
+
fetchImpl,
|
|
257
292
|
resolveHostname: options.resolveHostname,
|
|
258
293
|
});
|
|
259
294
|
} finally {
|
|
@@ -480,7 +480,9 @@ function normalizeCandidate(entry, context) {
|
|
|
480
480
|
? packageSource.url
|
|
481
481
|
: packageSource?.type === "git"
|
|
482
482
|
? packageSource.source
|
|
483
|
-
: ""
|
|
483
|
+
: packageSource?.type === "command"
|
|
484
|
+
? packageSource.identity
|
|
485
|
+
: "";
|
|
484
486
|
const sourceUrl = sanitizeUrl(sourceValue) || sourceValue || null;
|
|
485
487
|
const ref = packageSource?.type === "git" ? packageSource.ref : null;
|
|
486
488
|
|
|
@@ -639,7 +641,16 @@ function normalizeCandidate(entry, context) {
|
|
|
639
641
|
archiveSha256: packageSource.sha256,
|
|
640
642
|
format: packageSource.format,
|
|
641
643
|
}
|
|
642
|
-
: packageSource?.type === "
|
|
644
|
+
: packageSource?.type === "command"
|
|
645
|
+
? {
|
|
646
|
+
type: "command",
|
|
647
|
+
// The descriptor itself may carry local credentials. Catalog
|
|
648
|
+
// output binds only its non-secret canonical identity.
|
|
649
|
+
source: packageSource.identity,
|
|
650
|
+
ref: null,
|
|
651
|
+
mode: packageSource.mode,
|
|
652
|
+
}
|
|
653
|
+
: packageSource?.type === "dynamic-disabled"
|
|
643
654
|
? {
|
|
644
655
|
type: "dynamic-disabled",
|
|
645
656
|
source: null,
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export const MARKETPLACE_COMMAND_DESCRIPTOR_ERROR = Object.freeze({
|
|
5
|
+
INVALID: "CC_MARKETPLACE_COMMAND_DESCRIPTOR_INVALID",
|
|
6
|
+
SHELL_REJECTED: "CC_MARKETPLACE_COMMAND_DESCRIPTOR_SHELL_REJECTED",
|
|
7
|
+
LINK_UNSUPPORTED: "CC_MARKETPLACE_COMMAND_LINK_UNSUPPORTED",
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export const MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS = Object.freeze({
|
|
11
|
+
maxArgs: 128,
|
|
12
|
+
maxArgumentBytes: 8 * 1024,
|
|
13
|
+
maxTotalArgumentBytes: 32 * 1024,
|
|
14
|
+
maxEnvironmentEntries: 32,
|
|
15
|
+
maxEnvironmentValueBytes: 8 * 1024,
|
|
16
|
+
minTimeoutMs: 100,
|
|
17
|
+
maxTimeoutMs: 30_000,
|
|
18
|
+
defaultTimeoutMs: 10_000,
|
|
19
|
+
minOutputBytes: 1024,
|
|
20
|
+
maxSourceOutputBytes: 4 * 1024 * 1024,
|
|
21
|
+
maxHeadersOutputBytes: 64 * 1024,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const CONTROL = /[\u0000-\u001f\u007f]/u;
|
|
25
|
+
const ENVIRONMENT_KEY = /^[A-Za-z_][A-Za-z0-9_]{0,127}$/u;
|
|
26
|
+
export const MARKETPLACE_COMMAND_SAFE_ENVIRONMENT_KEYS = Object.freeze([
|
|
27
|
+
"COMSPEC",
|
|
28
|
+
"HOME",
|
|
29
|
+
"LANG",
|
|
30
|
+
"LC_ALL",
|
|
31
|
+
"LC_CTYPE",
|
|
32
|
+
"LC_MESSAGES",
|
|
33
|
+
"NODE_EXTRA_CA_CERTS",
|
|
34
|
+
"PATH",
|
|
35
|
+
"PATHEXT",
|
|
36
|
+
"SSL_CERT_DIR",
|
|
37
|
+
"SSL_CERT_FILE",
|
|
38
|
+
"SYSTEMROOT",
|
|
39
|
+
"TEMP",
|
|
40
|
+
"TMP",
|
|
41
|
+
"TMPDIR",
|
|
42
|
+
"USERPROFILE",
|
|
43
|
+
"WINDIR",
|
|
44
|
+
"XDG_CACHE_HOME",
|
|
45
|
+
"XDG_CONFIG_HOME",
|
|
46
|
+
]);
|
|
47
|
+
const SAFE_ENVIRONMENT_KEYS = new Set(
|
|
48
|
+
MARKETPLACE_COMMAND_SAFE_ENVIRONMENT_KEYS,
|
|
49
|
+
);
|
|
50
|
+
const SHELL_EXECUTABLES = new Set([
|
|
51
|
+
"bash",
|
|
52
|
+
"busybox",
|
|
53
|
+
"cmd",
|
|
54
|
+
"command",
|
|
55
|
+
"dash",
|
|
56
|
+
"env",
|
|
57
|
+
"fish",
|
|
58
|
+
"ksh",
|
|
59
|
+
"nu",
|
|
60
|
+
"powershell",
|
|
61
|
+
"pwsh",
|
|
62
|
+
"sh",
|
|
63
|
+
"zsh",
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
function descriptorError(code, message) {
|
|
67
|
+
const error = new Error(message);
|
|
68
|
+
error.code = code;
|
|
69
|
+
return error;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function plainObject(value, label) {
|
|
73
|
+
if (
|
|
74
|
+
!value ||
|
|
75
|
+
typeof value !== "object" ||
|
|
76
|
+
Array.isArray(value) ||
|
|
77
|
+
Object.getPrototypeOf(value) !== Object.prototype
|
|
78
|
+
) {
|
|
79
|
+
throw descriptorError(
|
|
80
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
81
|
+
`${label} must be a JSON object`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
85
|
+
for (const key of Reflect.ownKeys(descriptors)) {
|
|
86
|
+
if (typeof key !== "string" || !("value" in descriptors[key])) {
|
|
87
|
+
throw descriptorError(
|
|
88
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
89
|
+
`${label} must contain only own data properties`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return descriptors;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function exactKeys(descriptors, allowed, label) {
|
|
97
|
+
for (const key of Object.keys(descriptors)) {
|
|
98
|
+
if (!allowed.has(key)) {
|
|
99
|
+
throw descriptorError(
|
|
100
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
101
|
+
`${label} contains unsupported field: ${key}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function boundedString(value, label, maxBytes) {
|
|
108
|
+
if (
|
|
109
|
+
typeof value !== "string" ||
|
|
110
|
+
value.length === 0 ||
|
|
111
|
+
value.trim() !== value ||
|
|
112
|
+
CONTROL.test(value) ||
|
|
113
|
+
Buffer.byteLength(value, "utf8") > maxBytes
|
|
114
|
+
) {
|
|
115
|
+
throw descriptorError(
|
|
116
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
117
|
+
`${label} must be a non-empty bounded string`,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function normalizeAbsolutePath(value, label) {
|
|
124
|
+
const text = boundedString(value, label, 4096);
|
|
125
|
+
if (!path.isAbsolute(text)) {
|
|
126
|
+
throw descriptorError(
|
|
127
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
128
|
+
`${label} must be an absolute path`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return path.normalize(text);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function executableBasename(value) {
|
|
135
|
+
return path
|
|
136
|
+
.basename(value)
|
|
137
|
+
.replace(/\.(?:cmd|com|exe|bat)$/iu, "")
|
|
138
|
+
.toLowerCase();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeExecutable(value) {
|
|
142
|
+
const executable = normalizeAbsolutePath(
|
|
143
|
+
value,
|
|
144
|
+
"Marketplace command executable",
|
|
145
|
+
);
|
|
146
|
+
if (SHELL_EXECUTABLES.has(executableBasename(executable))) {
|
|
147
|
+
throw descriptorError(
|
|
148
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.SHELL_REJECTED,
|
|
149
|
+
"Marketplace command descriptors must not invoke a shell",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return executable;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function normalizeArguments(value) {
|
|
156
|
+
if (value === undefined) return Object.freeze([]);
|
|
157
|
+
if (
|
|
158
|
+
!Array.isArray(value) ||
|
|
159
|
+
value.length > MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxArgs
|
|
160
|
+
) {
|
|
161
|
+
throw descriptorError(
|
|
162
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
163
|
+
"Marketplace command args must be a bounded string array",
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
let totalBytes = 0;
|
|
167
|
+
const args = value.map((argument) => {
|
|
168
|
+
const normalized = boundedString(
|
|
169
|
+
argument,
|
|
170
|
+
"Marketplace command argument",
|
|
171
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxArgumentBytes,
|
|
172
|
+
);
|
|
173
|
+
totalBytes += Buffer.byteLength(normalized, "utf8");
|
|
174
|
+
return normalized;
|
|
175
|
+
});
|
|
176
|
+
if (
|
|
177
|
+
totalBytes > MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxTotalArgumentBytes
|
|
178
|
+
) {
|
|
179
|
+
throw descriptorError(
|
|
180
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
181
|
+
"Marketplace command arguments exceed the byte limit",
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return Object.freeze(args);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function normalizeEnvironment(value) {
|
|
188
|
+
if (value === undefined) return Object.freeze({});
|
|
189
|
+
const descriptors = plainObject(value, "Marketplace command env");
|
|
190
|
+
const keys = Object.keys(descriptors);
|
|
191
|
+
if (
|
|
192
|
+
keys.length > MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxEnvironmentEntries
|
|
193
|
+
) {
|
|
194
|
+
throw descriptorError(
|
|
195
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
196
|
+
"Marketplace command env has too many entries",
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const environment = {};
|
|
200
|
+
for (const key of keys.sort()) {
|
|
201
|
+
const normalizedKey = key.toUpperCase();
|
|
202
|
+
if (
|
|
203
|
+
!ENVIRONMENT_KEY.test(key) ||
|
|
204
|
+
key !== normalizedKey ||
|
|
205
|
+
!SAFE_ENVIRONMENT_KEYS.has(normalizedKey)
|
|
206
|
+
) {
|
|
207
|
+
throw descriptorError(
|
|
208
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
209
|
+
"Marketplace command env contains a disallowed key",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
environment[key] = boundedString(
|
|
213
|
+
descriptors[key].value,
|
|
214
|
+
"Marketplace command env value",
|
|
215
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxEnvironmentValueBytes,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
return Object.freeze(environment);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function normalizeBoundedInteger(value, fallback, minimum, maximum, label) {
|
|
222
|
+
if (value === undefined) return fallback;
|
|
223
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
224
|
+
throw descriptorError(
|
|
225
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
226
|
+
`${label} must be an integer between ${minimum} and ${maximum}`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return value;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function canonicalize(value) {
|
|
233
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
234
|
+
if (!value || typeof value !== "object") return value;
|
|
235
|
+
return Object.fromEntries(
|
|
236
|
+
Object.keys(value)
|
|
237
|
+
.sort()
|
|
238
|
+
.map((key) => [key, canonicalize(value[key])]),
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Parse the intentionally narrow Marketplace process descriptor. The
|
|
244
|
+
* descriptor is JSON-serializable, has no shell-string field, and contains
|
|
245
|
+
* the complete executable/argv/cwd/environment plan that the runner consumes.
|
|
246
|
+
*/
|
|
247
|
+
export function normalizeMarketplaceCommandDescriptor(raw, options = {}) {
|
|
248
|
+
const label = options.label || "Marketplace command descriptor";
|
|
249
|
+
const kind = options.kind || "source";
|
|
250
|
+
const descriptors = plainObject(raw, label);
|
|
251
|
+
const allowMode = kind === "source";
|
|
252
|
+
exactKeys(
|
|
253
|
+
descriptors,
|
|
254
|
+
new Set([
|
|
255
|
+
...(options.allowType === true ? ["type"] : []),
|
|
256
|
+
"executable",
|
|
257
|
+
"args",
|
|
258
|
+
"cwd",
|
|
259
|
+
"env",
|
|
260
|
+
"timeoutMs",
|
|
261
|
+
"maxOutputBytes",
|
|
262
|
+
...(allowMode ? ["mode"] : []),
|
|
263
|
+
]),
|
|
264
|
+
label,
|
|
265
|
+
);
|
|
266
|
+
if (options.allowType === true && descriptors.type?.value !== "command") {
|
|
267
|
+
throw descriptorError(
|
|
268
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
269
|
+
`${label}.type must be command`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const maxOutputBytes = normalizeBoundedInteger(
|
|
274
|
+
descriptors.maxOutputBytes?.value,
|
|
275
|
+
kind === "headers"
|
|
276
|
+
? MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxHeadersOutputBytes
|
|
277
|
+
: MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxSourceOutputBytes,
|
|
278
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.minOutputBytes,
|
|
279
|
+
kind === "headers"
|
|
280
|
+
? MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxHeadersOutputBytes
|
|
281
|
+
: MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxSourceOutputBytes,
|
|
282
|
+
`${label}.maxOutputBytes`,
|
|
283
|
+
);
|
|
284
|
+
const mode = allowMode ? descriptors.mode?.value || "copy" : null;
|
|
285
|
+
if (allowMode && !new Set(["copy", "link"]).has(mode)) {
|
|
286
|
+
throw descriptorError(
|
|
287
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.INVALID,
|
|
288
|
+
`${label}.mode must be copy or link`,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
return Object.freeze({
|
|
292
|
+
executable: normalizeExecutable(descriptors.executable?.value),
|
|
293
|
+
args: normalizeArguments(descriptors.args?.value),
|
|
294
|
+
cwd: normalizeAbsolutePath(
|
|
295
|
+
descriptors.cwd?.value,
|
|
296
|
+
"Marketplace command cwd",
|
|
297
|
+
),
|
|
298
|
+
env: normalizeEnvironment(descriptors.env?.value),
|
|
299
|
+
timeoutMs: normalizeBoundedInteger(
|
|
300
|
+
descriptors.timeoutMs?.value,
|
|
301
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.defaultTimeoutMs,
|
|
302
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.minTimeoutMs,
|
|
303
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_LIMITS.maxTimeoutMs,
|
|
304
|
+
`${label}.timeoutMs`,
|
|
305
|
+
),
|
|
306
|
+
maxOutputBytes,
|
|
307
|
+
...(allowMode ? { mode } : {}),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** A non-secret stable projection suitable for catalog identity only. */
|
|
312
|
+
export function marketplaceCommandDescriptorIdentity(descriptor) {
|
|
313
|
+
const normalized = normalizeMarketplaceCommandDescriptor(descriptor, {
|
|
314
|
+
kind: "source",
|
|
315
|
+
allowType: Object.prototype.hasOwnProperty.call(descriptor || {}, "type"),
|
|
316
|
+
});
|
|
317
|
+
const projection = {
|
|
318
|
+
executable: normalized.executable,
|
|
319
|
+
args: normalized.args,
|
|
320
|
+
cwd: normalized.cwd,
|
|
321
|
+
envKeys: Object.keys(normalized.env).sort(),
|
|
322
|
+
timeoutMs: normalized.timeoutMs,
|
|
323
|
+
maxOutputBytes: normalized.maxOutputBytes,
|
|
324
|
+
mode: normalized.mode,
|
|
325
|
+
};
|
|
326
|
+
return `command:${crypto
|
|
327
|
+
.createHash("sha256")
|
|
328
|
+
.update(JSON.stringify(canonicalize(projection)))
|
|
329
|
+
.digest("hex")
|
|
330
|
+
.slice(0, 32)}`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function assertMarketplaceCommandModeSupported(
|
|
334
|
+
mode,
|
|
335
|
+
platform = process.platform,
|
|
336
|
+
) {
|
|
337
|
+
if (mode === "link" && platform === "win32") {
|
|
338
|
+
throw descriptorError(
|
|
339
|
+
MARKETPLACE_COMMAND_DESCRIPTOR_ERROR.LINK_UNSUPPORTED,
|
|
340
|
+
"Marketplace command source mode link is not supported on Windows",
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|