chainlesschain 0.165.4 → 0.165.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-C__nnraW.js → AIOps-vfoDb6HZ.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CsKWHEZ-.js → ActionButton-Cm5f7p93.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BhIjfjWO.js → Analytics-yRuvgWxa.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-BjuXFzWZ.js → AppLayout-BROsC4h1.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-k6whDkZJ.js → Artifacts-CimHApb1.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-C4FBMYRh.js → Audit-DI8kr8zI.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C_vRrSXk.js → BackgroundAgents-DSLzKy-n.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CngFZeVp.js → Backup-DkH_n2r1.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B_H9EBRm.js → BaseInput-CKSrUoRA.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DLEC5kt7.js → Chat-ClwfMXUS.js} +3 -3
- package/src/assets/web-panel/assets/ChatBubbleRenderer-CjMaqfL7.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-m3ClR4xh.js → Checkbox-BYTehcQG.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CmLSFrgp.js → Codegen-B8AMcp6J.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DFGQteHN.js → Col-BNSogPo3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-EUUVORHE.js → Community-JxPvGb8O.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-2PSg0Yr7.js → Compact-DF7bFAgE.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CcKur7gB.js → Compliance-Bvhqnwrq.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DK_Fezfa.js → Cowork-BDS0CJvM.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Dbw89xl7.js → Cron-DqvRfxoQ.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CH66h_Ec.js → Crosschain-CDcIvnqS.js} +1 -1
- package/src/assets/web-panel/assets/{DID-D1igRb17.js → DID-D7jaUxU3.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DVAsrobk.js → Dashboard-B5A18wEV.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-Dl7W8AQs.js → Dropdown-_6IVkGag.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D5tpaEQC.js → EmailListRenderer-C9PeiKCG.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-O_VQf8gq.js → FamilyGuardDashboard-DXjuEHrX.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BTVcgS9n.js → Federation-B4IAR9zv.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BRnkJAIz.js → FormItemContext-CxQeSNx1.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-8OWSCkDj.js → GenericCardRenderer-B8GS-Y3N.js} +1 -1
- package/src/assets/web-panel/assets/{Git-WXLYTQj2.js → Git-BS8SouUX.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BezaUhtc.js → Governance-Dq9aYb5Q.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CuRay3Gu.js → Inference-BrCEe9AJ.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DfWLDW1m.js → KnowledgeGraph-DybOpiY2.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-C55y2pPY.js → Logs-DBpzbRfx.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CFyt8iUd.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Dk08Uycg.js → Marketplace-Cc7CCSCu.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C11fSB5V.js → McpTools-DFaIhOfu.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-mr0eZc97.js → Memory-KCqlp9ol.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-C0iCSRbb.js → MobileBridge-Bys84ySN.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CY-xMG8Z.js → MobileProjects-B-6qU-je.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-Bplqc_Wc.js → Mtc-BgY-fvCq.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-BlzwsoSM.js → MtcAudit-B8pCmqQJ.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BBLmTty6.js → Multisig-BbOmYutA.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DtT1uf1W.js → NLProgramming-BocLQz_M.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DcqQAxTK.js → Notes-CSwbxEop.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-cb0kWoZe.js → NotificationSettings-CqFNSYG3.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-kw9HZQNN.js → OrderTableRenderer-C6gMfAk7.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-DlTlbkUN.js → Organization-QrDXVdMJ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-pp1qs-oY.js → Overflow-Cxntotjt.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BESLnyYz.js → P2P-CIdBOwNq.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-B-J1mGuf.js → PdhVaultBrowser-CT5aP8rD.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-B36PeT1_.js → Permissions-DH7OM8XU.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CTK2ad-3.js → PersonalDataHub-CnJqbWce.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-zJNGXsac.js → Pipeline-5C3HG1kH.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-b9fz0Qf0.js → Privacy-B4yIp7xK.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DjZbZgiB.js → ProjectInit-BMvt-2M0.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C-hHJXlD.js → ProjectSettings-CvSTEMRG.js} +2 -2
- package/src/assets/web-panel/assets/Projects-BKBJxEek.js +1 -0
- package/src/assets/web-panel/assets/Providers-BdwUqFWN.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-A0yoQ3-3.js → QrScannerModal-DtnCKlTm.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DrFgFJNC.js → QuickAsk-sTbgwyNr.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-KC8hF1g4.js → Recommend-Bzr14gSb.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DGTPIk6u.js → RemoteSession-C_TF5IaJ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-QmbwM7Yk.js → Reputation-GCAM4H2K.js} +1 -1
- package/src/assets/web-panel/assets/{Row-BxjJylia.js → Row-Dir0r6OQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-B7Wda1J6.js → RssFeed-CpGnlecX.js} +2 -2
- package/src/assets/web-panel/assets/{Search-_plQ_YSA.js → Search-DMMamC6u.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CD2YJZfx.js → Security-yqay5yG-.js} +4 -4
- package/src/assets/web-panel/assets/{Services-qQE7NmwE.js → Services-C5i2wh9G.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DgQqkFf7.js → Skeleton-vUqk35yj.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-C4C0oFFt.js → Skills-DzcSkhot.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-Px2N2B5c.js → Sla-CB6qbYNr.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DN6C3lQd.js → SpeechSettings-BHUI7cox.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-jm5aH_m7.js → SyncSettings-D-RrsxTJ.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-DlkJcv67.js → Tasks-DFxdOJiq.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-DyrTHUBs.js → Templates-SKAob_Aq.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-mC434PtL.js → Tenant-BPfsfHQ-.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-o0_s3NgK.js → Terminal-8iw7FpUf.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DAGlAXOw.js → TimelineRenderer-C4Gjj5Su.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BnulS_bs.js → Tokens-COYhN18e.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-suQbVXYM.js → Trigger-B-JzXcf6.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DbIJAPo5.js → Trust-BJ5bg0nI.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CQNpbIHi.js → UkeySign-BG_ByRMf.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-D8aWCERk.js → VideoEditing-B3kOnSiU.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CuLpYfZc.js → Wallet-CcoGFnK7.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CsfG8ZMc.js → WebAuthn-DdT8vRC_.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BYsEAZbd.js → WorkflowEditor-CZu3M9N8.js} +1 -1
- package/src/assets/web-panel/assets/{chat-FT4Um3-I.js → chat-DxJt3Qba.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CKbS_Wlt.js → colors-DdYfwJaB.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CFJtzuJM.js → compact-item-BG3SUzwj.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-B9kSRW6K.js → createContext-By51z2-1.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DorRZcI9.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DR-x0Xpi.js → hasIn-B0apZ3KJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjEP7WkH.js → index-3jWkIu5j.js} +1 -1
- package/src/assets/web-panel/assets/{index-7jHr_A_k.js → index-B3rWuPMW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcfxhKXR.js → index-B5rLiEo0.js} +1 -1
- package/src/assets/web-panel/assets/{index-tqSEHPRT.js → index-BBPf3TpM.js} +1 -1
- package/src/assets/web-panel/assets/index-BETiL0a3.js +1 -0
- package/src/assets/web-panel/assets/{index-Cr2FLUsD.js → index-BIrJfqO4.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bt4fS2eL.js → index-BLIbGOLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-BwR7yc2h.js → index-BP94CrBV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVPU43b1.js → index-BWF8_mkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DShSkww-.js → index-BYINoRLK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bzz0C1BZ.js → index-BZ_0qm4g.js} +1 -1
- package/src/assets/web-panel/assets/{index-KqDaCApw.js → index-BewKYrX9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW-t9n-q.js → index-BfWYsB9t.js} +1 -1
- package/src/assets/web-panel/assets/index-BhAVHuW8.js +1 -0
- package/src/assets/web-panel/assets/{index-B7osrXqy.js → index-Bissefox.js} +3 -3
- package/src/assets/web-panel/assets/{index-DW4BWjTk.js → index-BkvcEo4p.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdkIhfi-.js → index-Bos5_aMV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DTxNy0ZU.js → index-C1C9i79A.js} +1 -1
- package/src/assets/web-panel/assets/{index-QBSeNVO3.js → index-CAhHirVH.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4LfPvWA.js → index-CNIxXMmN.js} +1 -1
- package/src/assets/web-panel/assets/{index-C_enk9I_.js → index-CW93tZDv.js} +1 -1
- package/src/assets/web-panel/assets/{index-WE08gMJP.js → index-CWEuZWCK.js} +1 -1
- package/src/assets/web-panel/assets/{index-r2VI1Ikk.js → index-Cp6e-hGw.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dap9c3eD.js → index-CpjZhgNd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWdqx7zI.js → index-CzmQElZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGbPnC5x.js → index-DCn8IcxV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DiNUEpSQ.js → index-DE76D-kA.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtkFqPVA.js → index-DaIz0NAW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGaXUsIs.js → index-Da_WBdLX.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3ZX81mK.js → index-Dz49VOyQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BK3TiKVq.js → index-Q2SCX-MI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CJmIQ9XR.js → index-RD6O-0C6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvrCCqJt.js → index-SQqOlR3d.js} +1 -1
- package/src/assets/web-panel/assets/{index-BI5hP_qy.js → index-ZFbps9po.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeSgw-WV.js → index-ZxtP8jG7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMGmg497.js → index-bP_VlDb4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BfFCQHL3.js → index-kf57LJ93.js} +1 -1
- package/src/assets/web-panel/assets/{index-OSzjTzGM.js → index-oxRrmFO9.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2JvmYRc.js → index-p1_8SbI1.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Dd7n7VgI.js → initDefaultProps-BWJktcg8.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BwxouRw1.js → motion-BEXBzUFV.js} +1 -1
- package/src/assets/web-panel/assets/{move-DObN0PG9.js → move-BUKwn2sx.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B3nOUQc7.js → mtc-parser-FTI7btqQ.js} +1 -1
- package/src/assets/web-panel/assets/{omit-o9WU2Nbl.js → omit-CsLtSPH3.js} +1 -1
- package/src/assets/web-panel/assets/parsers-YNLm7sQ0.js +4 -0
- package/src/assets/web-panel/assets/{pickAttrs-CL_vvfQ_.js → pickAttrs-DWZxsT_S.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CYrM2tyK.js → placementArrow-Bcfs43CX.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-k_06cQ96.js → responsiveObserve-a9bcC1p-.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CPKG0pxS.js → slide-BDTzgOXN.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BDDEeLTw.js → statusUtils-Iq1j_HUg.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cmb7RTh7.js → styleChecker-DAZEYxeh.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-e2IedC4N.js → useFlexGapSupport-DVkjyDrh.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CE_gxVnm.js → useFs-wXTpNTHt.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BkMIIKMz.js → usePersonalDataHub-CtQM_aZr.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DW886XV1.js → vnode-hjEoG85v.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dwo_adaf.js → zoom-BHVpyKg_.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 +14 -2
- package/src/commands/artifacts.js +235 -0
- package/src/commands/background-session.js +100 -10
- package/src/commands/cowork.js +86 -57
- package/src/commands/mcp.js +5 -0
- package/src/commands/plugin.js +145 -29
- package/src/commands/remote-control.js +295 -110
- package/src/commands/session-location.js +20 -1
- package/src/commands/session-message.js +171 -0
- package/src/commands/session.js +75 -69
- package/src/data/changelog.json +29 -3
- package/src/gateways/ws/message-dispatcher.js +57 -0
- package/src/harness/mcp-client.js +141 -10
- package/src/lib/artifact-access-ledger.js +1 -0
- package/src/lib/artifact-deletion-ledger.js +1 -0
- package/src/lib/artifact-recovery.js +970 -0
- package/src/lib/artifact-workbench-projection.js +171 -0
- package/src/lib/background-session-transport.js +153 -27
- package/src/lib/dynamic-workflow-runtime.js +408 -1
- package/src/lib/execution-location-target.js +4 -2
- package/src/lib/mcp-client.js +2 -0
- package/src/lib/mcp-headers-helper.js +47 -5
- package/src/lib/mcp-oauth.js +55 -6
- package/src/lib/mcp-stdio-executable-identity.js +16 -0
- package/src/lib/mcp-stdio-native-code-policy.js +74 -0
- package/src/lib/mcp-stdio-package-materialization.js +12 -3
- package/src/lib/mcp-tls.js +230 -0
- package/src/lib/output-styles.js +12 -0
- package/src/lib/plugin-runtime/install.js +614 -71
- package/src/lib/plugin-runtime/marketplace-archive-source.js +331 -0
- package/src/lib/plugin-runtime/marketplace-catalog.js +52 -6
- package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1 -0
- package/src/lib/plugin-runtime/marketplace-source-adapter.js +163 -0
- package/src/lib/plugin-runtime/marketplace-source-cache.js +11 -1
- package/src/lib/plugin-runtime/remote-source.js +435 -55
- package/src/lib/plugin-security.js +393 -41
- package/src/lib/plugin-source-identity.js +850 -0
- package/src/lib/process-execution-broker/index.js +112 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +57 -3
- package/src/lib/process-execution-broker/workspace-transaction.js +26 -0
- package/src/lib/remote-approval-bridge.js +136 -46
- package/src/lib/remote-control.js +217 -31
- package/src/lib/session-message-fabric.js +1105 -0
- package/src/lib/session-projection.js +171 -12
- package/src/lib/session-runtime-retention.js +155 -0
- package/src/lib/settings-loader.cjs +29 -0
- package/src/repl/agent-repl.js +51 -14
- package/src/repl/permission-prompt.js +31 -8
- package/src/repl/repl-keybindings.js +55 -0
- package/src/runtime/agent-core.js +63 -2
- package/src/runtime/headless-runner.js +47 -66
- package/src/runtime/headless-stream.js +18 -24
- package/src/runtime/mcp-config.js +2 -0
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-xijcWkJe.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-hDpWdiBc.js +0 -1
- package/src/assets/web-panel/assets/Projects-BC9KSDM6.js +0 -1
- package/src/assets/web-panel/assets/Providers-Dz-CAYYU.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DJZ0gAMu.js +0 -1
- package/src/assets/web-panel/assets/index-Bizb4hkw.js +0 -1
- package/src/assets/web-panel/assets/index-OHSl-xCh.js +0 -1
- package/src/assets/web-panel/assets/parsers-UEvh_ShA.js +0 -4
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { gunzipSync } from "node:zlib";
|
|
5
|
+
import { getElectronUserDataDir } from "../paths.js";
|
|
6
|
+
import {
|
|
7
|
+
assertSafeInstalledPluginStructure,
|
|
8
|
+
buildMarketplacePayloadSbom,
|
|
9
|
+
} from "./marketplace-artifact-readback.js";
|
|
10
|
+
import { createMarketplaceNetworkTransport } from "./marketplace-network.js";
|
|
11
|
+
import { fetchMarketplaceRemoteArtifact } from "./marketplace-remote-artifacts.js";
|
|
12
|
+
|
|
13
|
+
export const MARKETPLACE_ARCHIVE_SOURCE_SCHEMA =
|
|
14
|
+
"cc-plugin-marketplace-archive-source/v1";
|
|
15
|
+
export const MARKETPLACE_ARCHIVE_LIMITS = Object.freeze({
|
|
16
|
+
compressedBytes: 64 * 1024 * 1024,
|
|
17
|
+
expandedBytes: 256 * 1024 * 1024,
|
|
18
|
+
entries: 10_000,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function sha256(value) {
|
|
22
|
+
return crypto.createHash("sha256").update(value).digest("hex");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function octal(header, offset, length) {
|
|
26
|
+
const text = header
|
|
27
|
+
.subarray(offset, offset + length)
|
|
28
|
+
.toString("ascii")
|
|
29
|
+
.replace(/\0.*$/u, "")
|
|
30
|
+
.trim();
|
|
31
|
+
return text ? Number.parseInt(text, 8) : 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cString(header, offset, length) {
|
|
35
|
+
const bytes = header.subarray(offset, offset + length);
|
|
36
|
+
const end = bytes.indexOf(0);
|
|
37
|
+
return bytes.subarray(0, end < 0 ? bytes.length : end).toString("utf8");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function checksumValid(header) {
|
|
41
|
+
const expected = octal(header, 148, 8);
|
|
42
|
+
let actual = 0;
|
|
43
|
+
for (let index = 0; index < 512; index += 1) {
|
|
44
|
+
actual += index >= 148 && index < 156 ? 0x20 : header[index];
|
|
45
|
+
}
|
|
46
|
+
return expected === actual;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function safeArchivePath(raw) {
|
|
50
|
+
const value = String(raw || "");
|
|
51
|
+
if (
|
|
52
|
+
!value ||
|
|
53
|
+
value.includes("\\") ||
|
|
54
|
+
value.includes("\0") ||
|
|
55
|
+
value.startsWith("/") ||
|
|
56
|
+
/^[a-z]:/iu.test(value)
|
|
57
|
+
) {
|
|
58
|
+
throw new Error("archive contains an unsafe path");
|
|
59
|
+
}
|
|
60
|
+
const segments = value.split("/").filter((part) => part && part !== ".");
|
|
61
|
+
const unsafePortableSegment = (part) =>
|
|
62
|
+
part === ".." ||
|
|
63
|
+
Buffer.byteLength(part, "utf8") > 255 ||
|
|
64
|
+
/[\u0000-\u001f:]/u.test(part) ||
|
|
65
|
+
/[ .]$/u.test(part) ||
|
|
66
|
+
/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/iu.test(part);
|
|
67
|
+
if (
|
|
68
|
+
value.length > 4096 ||
|
|
69
|
+
segments.some(unsafePortableSegment) ||
|
|
70
|
+
segments[0] !== "package"
|
|
71
|
+
) {
|
|
72
|
+
throw new Error("archive must contain only a package/ root");
|
|
73
|
+
}
|
|
74
|
+
return segments.slice(1);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parsePax(data) {
|
|
78
|
+
const values = {};
|
|
79
|
+
let offset = 0;
|
|
80
|
+
while (offset < data.length) {
|
|
81
|
+
const space = data.indexOf(0x20, offset);
|
|
82
|
+
if (space < 0) throw new Error("archive PAX header is malformed");
|
|
83
|
+
const length = Number.parseInt(data.toString("ascii", offset, space), 10);
|
|
84
|
+
if (
|
|
85
|
+
!Number.isSafeInteger(length) ||
|
|
86
|
+
length <= 0 ||
|
|
87
|
+
offset + length > data.length
|
|
88
|
+
) {
|
|
89
|
+
throw new Error("archive PAX record length is invalid");
|
|
90
|
+
}
|
|
91
|
+
const record = data.toString("utf8", space + 1, offset + length - 1);
|
|
92
|
+
const equals = record.indexOf("=");
|
|
93
|
+
if (equals > 0) values[record.slice(0, equals)] = record.slice(equals + 1);
|
|
94
|
+
offset += length;
|
|
95
|
+
}
|
|
96
|
+
return values;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function extractVerifiedTgz(bytes, destination) {
|
|
100
|
+
let tar;
|
|
101
|
+
try {
|
|
102
|
+
tar = gunzipSync(bytes, {
|
|
103
|
+
maxOutputLength: MARKETPLACE_ARCHIVE_LIMITS.expandedBytes,
|
|
104
|
+
});
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error("Marketplace archive is not a bounded valid gzip stream");
|
|
107
|
+
}
|
|
108
|
+
const seen = new Set();
|
|
109
|
+
let offset = 0;
|
|
110
|
+
let records = 0;
|
|
111
|
+
let extractedEntries = 0;
|
|
112
|
+
let extractedBytes = 0;
|
|
113
|
+
let pendingLongName = null;
|
|
114
|
+
let pendingPax = null;
|
|
115
|
+
while (offset + 512 <= tar.length) {
|
|
116
|
+
const header = tar.subarray(offset, offset + 512);
|
|
117
|
+
if (header.every((value) => value === 0)) break;
|
|
118
|
+
if (!checksumValid(header))
|
|
119
|
+
throw new Error("archive tar checksum mismatch");
|
|
120
|
+
const size = octal(header, 124, 12);
|
|
121
|
+
if (!Number.isSafeInteger(size) || size < 0) {
|
|
122
|
+
throw new Error("archive tar entry size is invalid");
|
|
123
|
+
}
|
|
124
|
+
const typeByte = header[156];
|
|
125
|
+
const type = typeByte === 0 ? "0" : String.fromCharCode(typeByte);
|
|
126
|
+
const dataStart = offset + 512;
|
|
127
|
+
const dataEnd = dataStart + size;
|
|
128
|
+
if (dataEnd > tar.length) throw new Error("archive tar entry is truncated");
|
|
129
|
+
const data = tar.subarray(dataStart, dataEnd);
|
|
130
|
+
offset = dataStart + Math.ceil(size / 512) * 512;
|
|
131
|
+
records += 1;
|
|
132
|
+
if (records > MARKETPLACE_ARCHIVE_LIMITS.entries) {
|
|
133
|
+
throw new Error("archive contains too many entries");
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (type === "L") {
|
|
137
|
+
pendingLongName = data.toString("utf8").replace(/\0+$/u, "");
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (type === "x" || type === "X") {
|
|
141
|
+
pendingPax = parsePax(data);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (type === "g") continue;
|
|
145
|
+
if (!new Set(["0", "5"]).has(type)) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
"archive links, devices, and special entries are rejected",
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const name = cString(header, 0, 100);
|
|
152
|
+
const prefix = cString(header, 345, 155);
|
|
153
|
+
let archivePath = prefix ? `${prefix}/${name}` : name;
|
|
154
|
+
if (pendingLongName != null) archivePath = pendingLongName;
|
|
155
|
+
if (typeof pendingPax?.path === "string" && pendingPax.path) {
|
|
156
|
+
archivePath = pendingPax.path;
|
|
157
|
+
}
|
|
158
|
+
pendingLongName = null;
|
|
159
|
+
pendingPax = null;
|
|
160
|
+
const parts = safeArchivePath(archivePath);
|
|
161
|
+
if (parts.length === 0) continue;
|
|
162
|
+
const relativePath = parts.join("/");
|
|
163
|
+
const collisionKey = relativePath.normalize("NFC").toLowerCase();
|
|
164
|
+
if (seen.has(collisionKey)) {
|
|
165
|
+
throw new Error("archive contains duplicate or case-colliding paths");
|
|
166
|
+
}
|
|
167
|
+
seen.add(collisionKey);
|
|
168
|
+
extractedEntries += 1;
|
|
169
|
+
const target = path.join(destination, ...parts);
|
|
170
|
+
const relative = path.relative(destination, target);
|
|
171
|
+
if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
|
|
172
|
+
throw new Error("archive extraction escaped its package root");
|
|
173
|
+
}
|
|
174
|
+
if (type === "5") {
|
|
175
|
+
fs.mkdirSync(target, { recursive: true, mode: 0o755 });
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
extractedBytes += data.length;
|
|
179
|
+
if (extractedBytes > MARKETPLACE_ARCHIVE_LIMITS.expandedBytes) {
|
|
180
|
+
throw new Error("archive expanded payload exceeds its byte limit");
|
|
181
|
+
}
|
|
182
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o755 });
|
|
183
|
+
const mode = octal(header, 100, 8) & 0o111 ? 0o755 : 0o644;
|
|
184
|
+
fs.writeFileSync(target, data, { flag: "wx", mode });
|
|
185
|
+
}
|
|
186
|
+
if (extractedEntries === 0) throw new Error("archive package is empty");
|
|
187
|
+
assertSafeInstalledPluginStructure(destination);
|
|
188
|
+
return buildMarketplacePayloadSbom(destination);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function cacheRoot(configured) {
|
|
192
|
+
return (
|
|
193
|
+
configured || path.join(getElectronUserDataDir(), "plugin-archive-sources")
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function readAuthority(root, expected) {
|
|
198
|
+
const authorityPath = path.join(root, "authority.json");
|
|
199
|
+
const packagePath = path.join(root, "package");
|
|
200
|
+
const rootStat = fs.lstatSync(root);
|
|
201
|
+
const authorityStat = fs.lstatSync(authorityPath);
|
|
202
|
+
if (
|
|
203
|
+
!rootStat.isDirectory() ||
|
|
204
|
+
rootStat.isSymbolicLink() ||
|
|
205
|
+
!authorityStat.isFile() ||
|
|
206
|
+
authorityStat.isSymbolicLink() ||
|
|
207
|
+
authorityStat.nlink !== 1 ||
|
|
208
|
+
authorityStat.size <= 0 ||
|
|
209
|
+
authorityStat.size > 64 * 1024
|
|
210
|
+
) {
|
|
211
|
+
throw new Error("Marketplace archive source cache structure is unsafe");
|
|
212
|
+
}
|
|
213
|
+
const authority = JSON.parse(fs.readFileSync(authorityPath, "utf8"));
|
|
214
|
+
if (
|
|
215
|
+
authority.schemaVersion !== MARKETPLACE_ARCHIVE_SOURCE_SCHEMA ||
|
|
216
|
+
authority.status !== "digest-verified-and-extracted" ||
|
|
217
|
+
authority.url !== expected.url ||
|
|
218
|
+
authority.registryOrigin !== expected.registryOrigin ||
|
|
219
|
+
authority.archiveSha256 !== expected.archiveSha256 ||
|
|
220
|
+
authority.compressedBytes !== expected.compressedBytes
|
|
221
|
+
) {
|
|
222
|
+
throw new Error("Marketplace archive source cache authority mismatch");
|
|
223
|
+
}
|
|
224
|
+
assertSafeInstalledPluginStructure(packagePath);
|
|
225
|
+
const payload = buildMarketplacePayloadSbom(packagePath);
|
|
226
|
+
if (
|
|
227
|
+
payload.digest !== authority.payloadSha256 ||
|
|
228
|
+
payload.fileCount !== authority.fileCount ||
|
|
229
|
+
payload.totalBytes !== authority.totalBytes
|
|
230
|
+
) {
|
|
231
|
+
throw new Error("Marketplace archive extracted payload digest mismatch");
|
|
232
|
+
}
|
|
233
|
+
return { authority, packagePath };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export async function fetchAndMaterializeMarketplaceArchive(options = {}) {
|
|
237
|
+
const networkTransport =
|
|
238
|
+
options.offline !== true &&
|
|
239
|
+
typeof options.fetchImpl !== "function" &&
|
|
240
|
+
(options.proxyUrl || options.pacFile || options.caFile)
|
|
241
|
+
? createMarketplaceNetworkTransport(options)
|
|
242
|
+
: null;
|
|
243
|
+
let artifact;
|
|
244
|
+
try {
|
|
245
|
+
artifact = await fetchMarketplaceRemoteArtifact({
|
|
246
|
+
kind: "archive",
|
|
247
|
+
url: options.url,
|
|
248
|
+
expectedSha256: options.sha256,
|
|
249
|
+
registryOrigin: new URL(options.registryUrl).origin,
|
|
250
|
+
authorizationOrigin: new URL(options.registryUrl).origin,
|
|
251
|
+
token: options.token,
|
|
252
|
+
allowInsecure: options.allowInsecure === true,
|
|
253
|
+
offline: options.offline === true,
|
|
254
|
+
cacheDir: options.artifactCacheDir,
|
|
255
|
+
timeoutMs: options.timeoutMs,
|
|
256
|
+
fetchImpl: options.fetchImpl || networkTransport?.fetch,
|
|
257
|
+
resolveHostname: options.resolveHostname,
|
|
258
|
+
});
|
|
259
|
+
} finally {
|
|
260
|
+
await networkTransport?.close();
|
|
261
|
+
}
|
|
262
|
+
if (artifact.bytes.length > MARKETPLACE_ARCHIVE_LIMITS.compressedBytes) {
|
|
263
|
+
throw new Error("Marketplace archive exceeds its compressed byte limit");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const root = cacheRoot(options.sourceCacheDir);
|
|
267
|
+
fs.mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
268
|
+
const rootStat = fs.lstatSync(root);
|
|
269
|
+
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
|
|
270
|
+
throw new Error("Marketplace archive source cache root is unsafe");
|
|
271
|
+
}
|
|
272
|
+
const key = sha256(`${artifact.url}\0${artifact.sha256}`);
|
|
273
|
+
const finalRoot = path.join(root, key);
|
|
274
|
+
const expected = {
|
|
275
|
+
url: artifact.url,
|
|
276
|
+
registryOrigin: new URL(options.registryUrl).origin,
|
|
277
|
+
archiveSha256: artifact.sha256,
|
|
278
|
+
compressedBytes: artifact.bytes.length,
|
|
279
|
+
};
|
|
280
|
+
if (fs.existsSync(finalRoot)) {
|
|
281
|
+
try {
|
|
282
|
+
const cached = readAuthority(finalRoot, expected);
|
|
283
|
+
return {
|
|
284
|
+
dir: cached.packagePath,
|
|
285
|
+
authority: { ...cached.authority, fromCache: artifact.fromCache },
|
|
286
|
+
};
|
|
287
|
+
} catch (error) {
|
|
288
|
+
const stat = fs.lstatSync(finalRoot);
|
|
289
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) throw error;
|
|
290
|
+
fs.rmSync(finalRoot, { recursive: true, force: true });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const temporary = fs.mkdtempSync(path.join(root, `.tmp-${key}-`));
|
|
295
|
+
try {
|
|
296
|
+
const packagePath = path.join(temporary, "package");
|
|
297
|
+
fs.mkdirSync(packagePath, { mode: 0o755 });
|
|
298
|
+
const payload = extractVerifiedTgz(artifact.bytes, packagePath);
|
|
299
|
+
const authority = {
|
|
300
|
+
schemaVersion: MARKETPLACE_ARCHIVE_SOURCE_SCHEMA,
|
|
301
|
+
status: "digest-verified-and-extracted",
|
|
302
|
+
url: artifact.url,
|
|
303
|
+
registryOrigin: new URL(options.registryUrl).origin,
|
|
304
|
+
archiveSha256: artifact.sha256,
|
|
305
|
+
compressedBytes: artifact.bytes.length,
|
|
306
|
+
payloadSha256: payload.digest,
|
|
307
|
+
fileCount: payload.fileCount,
|
|
308
|
+
totalBytes: payload.totalBytes,
|
|
309
|
+
fromCache: artifact.fromCache,
|
|
310
|
+
};
|
|
311
|
+
fs.writeFileSync(
|
|
312
|
+
path.join(temporary, "authority.json"),
|
|
313
|
+
`${JSON.stringify(authority)}\n`,
|
|
314
|
+
{ encoding: "utf8", flag: "wx", mode: 0o600 },
|
|
315
|
+
);
|
|
316
|
+
try {
|
|
317
|
+
fs.renameSync(temporary, finalRoot);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
if (!fs.existsSync(finalRoot)) throw error;
|
|
320
|
+
}
|
|
321
|
+
const cached = readAuthority(finalRoot, expected);
|
|
322
|
+
return {
|
|
323
|
+
dir: cached.packagePath,
|
|
324
|
+
authority: { ...cached.authority, fromCache: artifact.fromCache },
|
|
325
|
+
};
|
|
326
|
+
} finally {
|
|
327
|
+
if (fs.existsSync(temporary)) {
|
|
328
|
+
fs.rmSync(temporary, { recursive: true, force: true });
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { describeCapabilities, normalizeCapabilities } from "./capabilities.js";
|
|
17
17
|
import { normalizePublisherDeclaration } from "./publisher-trust.js";
|
|
18
18
|
import { normalizeMarketplaceNetworkAuthority } from "./marketplace-network.js";
|
|
19
|
+
import { normalizeMarketplacePackageSource } from "./marketplace-source-adapter.js";
|
|
19
20
|
|
|
20
21
|
export const PLUGIN_MARKETPLACE_CATALOG_SCHEMA =
|
|
21
22
|
"cc-plugin-marketplace-catalog/v1";
|
|
@@ -461,22 +462,50 @@ function normalizeCandidate(entry, context) {
|
|
|
461
462
|
? boundedString(raw.version, 128) || null
|
|
462
463
|
: null;
|
|
463
464
|
const description = boundedString(raw.description, 2048);
|
|
464
|
-
const sourceValue =
|
|
465
|
-
typeof raw.source === "string" ? boundedString(raw.source, 4096) : "";
|
|
466
|
-
const sourceUrl = sanitizeUrl(sourceValue) || sourceValue || null;
|
|
467
|
-
const ref = boundedString(raw.ref, 256) || null;
|
|
468
465
|
const blockers = [];
|
|
469
466
|
const warnings = [];
|
|
467
|
+
let packageSource = null;
|
|
468
|
+
try {
|
|
469
|
+
packageSource = normalizeMarketplacePackageSource(raw);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
blockers.push(issue(error?.code || "INVALID_PACKAGE_SOURCE"));
|
|
472
|
+
}
|
|
473
|
+
if (packageSource?.type === "dynamic-disabled") {
|
|
474
|
+
blockers.push(
|
|
475
|
+
issue("DYNAMIC_SOURCE_DISABLED", packageSource.requestedType),
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
const sourceValue =
|
|
479
|
+
packageSource?.type === "archive"
|
|
480
|
+
? packageSource.url
|
|
481
|
+
: packageSource?.type === "git"
|
|
482
|
+
? packageSource.source
|
|
483
|
+
: "";
|
|
484
|
+
const sourceUrl = sanitizeUrl(sourceValue) || sourceValue || null;
|
|
485
|
+
const ref = packageSource?.type === "git" ? packageSource.ref : null;
|
|
470
486
|
|
|
471
487
|
if (!rawName || !/^[a-zA-Z0-9._@/-]+$/.test(rawName)) {
|
|
472
488
|
blockers.push(issue("INVALID_NAME"));
|
|
473
489
|
}
|
|
474
490
|
if (!version || !semver.valid(version))
|
|
475
491
|
blockers.push(issue("INVALID_VERSION"));
|
|
476
|
-
if (!sourceValue
|
|
492
|
+
if (!sourceValue && packageSource?.type !== "dynamic-disabled") {
|
|
493
|
+
blockers.push(issue("MISSING_PACKAGE_SOURCE"));
|
|
494
|
+
}
|
|
477
495
|
if (hasUrlCredentials(sourceValue)) {
|
|
478
496
|
blockers.push(issue("SOURCE_CREDENTIALS_EMBEDDED"));
|
|
479
497
|
}
|
|
498
|
+
if (packageSource?.type === "archive") {
|
|
499
|
+
try {
|
|
500
|
+
if (
|
|
501
|
+
new URL(packageSource.url).origin !== new URL(context.source.url).origin
|
|
502
|
+
) {
|
|
503
|
+
blockers.push(issue("ARCHIVE_SOURCE_ORIGIN_MISMATCH"));
|
|
504
|
+
}
|
|
505
|
+
} catch {
|
|
506
|
+
blockers.push(issue("ARCHIVE_SOURCE_ORIGIN_MISMATCH"));
|
|
507
|
+
}
|
|
508
|
+
}
|
|
480
509
|
|
|
481
510
|
const integrity = normalizeIntegrity(raw, blockers, warnings);
|
|
482
511
|
const license = normalizeLicense(raw.license);
|
|
@@ -601,7 +630,24 @@ function normalizeCandidate(entry, context) {
|
|
|
601
630
|
? { networkAuthority: context.source.networkAuthority }
|
|
602
631
|
: {}),
|
|
603
632
|
},
|
|
604
|
-
package:
|
|
633
|
+
package:
|
|
634
|
+
packageSource?.type === "archive"
|
|
635
|
+
? {
|
|
636
|
+
type: "archive",
|
|
637
|
+
source: sourceUrl,
|
|
638
|
+
ref: null,
|
|
639
|
+
archiveSha256: packageSource.sha256,
|
|
640
|
+
format: packageSource.format,
|
|
641
|
+
}
|
|
642
|
+
: packageSource?.type === "dynamic-disabled"
|
|
643
|
+
? {
|
|
644
|
+
type: "dynamic-disabled",
|
|
645
|
+
source: null,
|
|
646
|
+
ref: null,
|
|
647
|
+
requestedType: packageSource.requestedType,
|
|
648
|
+
enabled: false,
|
|
649
|
+
}
|
|
650
|
+
: { source: sourceUrl, ref },
|
|
605
651
|
integrity,
|
|
606
652
|
license,
|
|
607
653
|
capabilities: {
|
|
@@ -21,6 +21,7 @@ export const MAX_MARKETPLACE_ARTIFACT_REDIRECTS = 3;
|
|
|
21
21
|
export const DEFAULT_MARKETPLACE_ARTIFACT_TIMEOUT_MS = 15_000;
|
|
22
22
|
export const MAX_MARKETPLACE_ARTIFACT_URL_LENGTH = 4096;
|
|
23
23
|
export const MARKETPLACE_REMOTE_ARTIFACT_LIMITS = Object.freeze({
|
|
24
|
+
archive: 64 * 1024 * 1024,
|
|
24
25
|
signature: 16 * 1024,
|
|
25
26
|
publicKey: 64 * 1024,
|
|
26
27
|
sbom: 16 * 1024 * 1024,
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const SHA256_RE = /^[a-f0-9]{64}$/u;
|
|
2
|
+
const DYNAMIC_SOURCE_TYPES = new Set([
|
|
3
|
+
"command",
|
|
4
|
+
"headers-helper",
|
|
5
|
+
"headershelper",
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
export const MARKETPLACE_DYNAMIC_SOURCE_DISABLED_CODE =
|
|
9
|
+
"CC_MARKETPLACE_DYNAMIC_SOURCE_DISABLED";
|
|
10
|
+
|
|
11
|
+
function sourceError(code, message) {
|
|
12
|
+
const error = new Error(message);
|
|
13
|
+
error.code = code;
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function bounded(value, max) {
|
|
18
|
+
const text = typeof value === "string" ? value.trim() : "";
|
|
19
|
+
return text.length > 0 && text.length <= max ? text : null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function safeUrl(value, label) {
|
|
23
|
+
const raw = bounded(value, 4096);
|
|
24
|
+
if (!raw) throw sourceError("INVALID_SOURCE_URL", `${label} URL is missing`);
|
|
25
|
+
let url;
|
|
26
|
+
try {
|
|
27
|
+
url = new URL(raw);
|
|
28
|
+
} catch {
|
|
29
|
+
throw sourceError("INVALID_SOURCE_URL", `${label} URL is invalid`);
|
|
30
|
+
}
|
|
31
|
+
if (url.username || url.password) {
|
|
32
|
+
throw sourceError(
|
|
33
|
+
"SOURCE_CREDENTIALS_REJECTED",
|
|
34
|
+
`${label} URL must not contain credentials`,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (!new Set(["https:", "http:"]).has(url.protocol)) {
|
|
38
|
+
throw sourceError(
|
|
39
|
+
"INVALID_SOURCE_URL",
|
|
40
|
+
`${label} URL must use HTTPS or loopback HTTP`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const requestUrl = url.href;
|
|
44
|
+
url.search = "";
|
|
45
|
+
url.hash = "";
|
|
46
|
+
return { url: url.href, requestUrl };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function disabledDynamicSource(type) {
|
|
50
|
+
return Object.freeze({
|
|
51
|
+
type: "dynamic-disabled",
|
|
52
|
+
requestedType: type,
|
|
53
|
+
enabled: false,
|
|
54
|
+
code: MARKETPLACE_DYNAMIC_SOURCE_DISABLED_CODE,
|
|
55
|
+
reason:
|
|
56
|
+
"dynamic Marketplace source/auth adapters are disabled by product policy",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Normalize one registry package source without executing it. Git strings keep
|
|
62
|
+
* the legacy shape. Archive sources require an HTTPS URL and exact compressed
|
|
63
|
+
* byte digest. Dynamic command/helper adapters remain representable for UI
|
|
64
|
+
* diagnostics but can never become executable materialization authority.
|
|
65
|
+
*/
|
|
66
|
+
export function normalizeMarketplacePackageSource(entry, options = {}) {
|
|
67
|
+
const row = entry && typeof entry === "object" ? entry : {};
|
|
68
|
+
if (row.command != null || row.headersHelper != null) {
|
|
69
|
+
const disabled = disabledDynamicSource(
|
|
70
|
+
row.command != null ? "command" : "headers-helper",
|
|
71
|
+
);
|
|
72
|
+
if (options.forExecution === true) {
|
|
73
|
+
throw sourceError(disabled.code, disabled.reason);
|
|
74
|
+
}
|
|
75
|
+
return disabled;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const source = row.source;
|
|
79
|
+
if (typeof source === "string") {
|
|
80
|
+
const value = bounded(source, 4096);
|
|
81
|
+
if (!value) {
|
|
82
|
+
throw sourceError("MISSING_PACKAGE_SOURCE", "plugin source is missing");
|
|
83
|
+
}
|
|
84
|
+
return Object.freeze({
|
|
85
|
+
type: "git",
|
|
86
|
+
source: value,
|
|
87
|
+
ref: bounded(row.ref, 256),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (!source || typeof source !== "object" || Array.isArray(source)) {
|
|
91
|
+
throw sourceError("MISSING_PACKAGE_SOURCE", "plugin source is missing");
|
|
92
|
+
}
|
|
93
|
+
if (source.command != null || source.headersHelper != null) {
|
|
94
|
+
const disabled = disabledDynamicSource(
|
|
95
|
+
source.command != null ? "command" : "headers-helper",
|
|
96
|
+
);
|
|
97
|
+
if (options.forExecution === true) {
|
|
98
|
+
throw sourceError(disabled.code, disabled.reason);
|
|
99
|
+
}
|
|
100
|
+
return disabled;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const type = String(source.type || "")
|
|
104
|
+
.trim()
|
|
105
|
+
.toLowerCase();
|
|
106
|
+
if (DYNAMIC_SOURCE_TYPES.has(type)) {
|
|
107
|
+
const disabled = disabledDynamicSource(type);
|
|
108
|
+
if (options.forExecution === true) {
|
|
109
|
+
throw sourceError(disabled.code, disabled.reason);
|
|
110
|
+
}
|
|
111
|
+
return disabled;
|
|
112
|
+
}
|
|
113
|
+
if (type === "git") {
|
|
114
|
+
const value = bounded(source.url ?? source.source, 4096);
|
|
115
|
+
if (!value) {
|
|
116
|
+
throw sourceError("MISSING_PACKAGE_SOURCE", "git source URL is missing");
|
|
117
|
+
}
|
|
118
|
+
return Object.freeze({
|
|
119
|
+
type: "git",
|
|
120
|
+
source: value,
|
|
121
|
+
ref: bounded(source.ref ?? row.ref, 256),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (type !== "archive") {
|
|
125
|
+
throw sourceError(
|
|
126
|
+
"UNSUPPORTED_PACKAGE_SOURCE",
|
|
127
|
+
`unsupported Marketplace source type: ${type || "unknown"}`,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const archiveUrl = safeUrl(source.url, "archive source");
|
|
132
|
+
const sha256 = String(source.sha256 || "")
|
|
133
|
+
.trim()
|
|
134
|
+
.toLowerCase();
|
|
135
|
+
if (!SHA256_RE.test(sha256)) {
|
|
136
|
+
throw sourceError(
|
|
137
|
+
"ARCHIVE_DIGEST_REQUIRED",
|
|
138
|
+
"archive source requires a 64-character lowercase SHA-256 digest",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const format = String(source.format || "tgz").toLowerCase();
|
|
142
|
+
if (!new Set(["tgz", "tar.gz"]).has(format)) {
|
|
143
|
+
throw sourceError(
|
|
144
|
+
"ARCHIVE_FORMAT_UNSUPPORTED",
|
|
145
|
+
"archive source format must be tgz or tar.gz",
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
const normalized = {
|
|
149
|
+
type: "archive",
|
|
150
|
+
url: archiveUrl.url,
|
|
151
|
+
sha256,
|
|
152
|
+
format: "tgz",
|
|
153
|
+
};
|
|
154
|
+
Object.defineProperty(normalized, "requestUrl", {
|
|
155
|
+
value: archiveUrl.requestUrl,
|
|
156
|
+
enumerable: false,
|
|
157
|
+
});
|
|
158
|
+
return Object.freeze(normalized);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function assertMarketplaceSourceExecutable(entry) {
|
|
162
|
+
return normalizeMarketplacePackageSource(entry, { forExecution: true });
|
|
163
|
+
}
|
|
@@ -29,6 +29,9 @@ export function marketplaceSourceCacheSpec(
|
|
|
29
29
|
if (sourceMetadata?.type !== "registry") return null;
|
|
30
30
|
const source = cleanString(sourceMetadata.resolvedSource, 4096);
|
|
31
31
|
const ref = cleanString(sourceMetadata.ref, 256);
|
|
32
|
+
const sourceDigest =
|
|
33
|
+
normalizeSha256(sourceMetadata.resolvedSourceDigest) ||
|
|
34
|
+
(source ? sha256Canonical({ source, ref }) : null);
|
|
32
35
|
const expectations = sourceMetadata.catalogAuthority?.artifactExpectations;
|
|
33
36
|
const manifestSha256 = normalizeSha256(expectations?.manifest?.sha256);
|
|
34
37
|
let payloadSchemaVersion = cleanString(expectations?.sbom?.format, 128);
|
|
@@ -57,6 +60,7 @@ export function marketplaceSourceCacheSpec(
|
|
|
57
60
|
}
|
|
58
61
|
if (
|
|
59
62
|
!source ||
|
|
63
|
+
!sourceDigest ||
|
|
60
64
|
!manifestSha256 ||
|
|
61
65
|
!payloadSha256 ||
|
|
62
66
|
!isMarketplacePayloadSbomFormat(payloadSchemaVersion)
|
|
@@ -66,6 +70,7 @@ export function marketplaceSourceCacheSpec(
|
|
|
66
70
|
const keyAuthority = {
|
|
67
71
|
schemaVersion: PLUGIN_MARKETPLACE_SOURCE_CACHE_SCHEMA,
|
|
68
72
|
source,
|
|
73
|
+
sourceDigest,
|
|
69
74
|
ref,
|
|
70
75
|
manifestSha256,
|
|
71
76
|
payload: {
|
|
@@ -180,6 +185,7 @@ function buildCacheAuthority(payload, spec) {
|
|
|
180
185
|
schemaVersion: PLUGIN_MARKETPLACE_SOURCE_CACHE_SCHEMA,
|
|
181
186
|
cacheKey: spec.cacheKey,
|
|
182
187
|
source: spec.source,
|
|
188
|
+
sourceDigest: spec.sourceDigest,
|
|
183
189
|
ref: spec.ref,
|
|
184
190
|
manifestSha256,
|
|
185
191
|
payload: {
|
|
@@ -279,7 +285,11 @@ function readAuthority(file) {
|
|
|
279
285
|
} catch {
|
|
280
286
|
throw new Error("Marketplace source cache authority is not valid UTF-8");
|
|
281
287
|
}
|
|
282
|
-
|
|
288
|
+
try {
|
|
289
|
+
return JSON.parse(text);
|
|
290
|
+
} catch {
|
|
291
|
+
throw new Error("Marketplace source cache authority is invalid JSON");
|
|
292
|
+
}
|
|
283
293
|
} finally {
|
|
284
294
|
if (descriptor != null) fs.closeSync(descriptor);
|
|
285
295
|
}
|