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
|
@@ -38,8 +38,18 @@ import {
|
|
|
38
38
|
} from "./scopes.js";
|
|
39
39
|
import {
|
|
40
40
|
enforcePluginPolicy,
|
|
41
|
+
enforcePluginSourcePolicy,
|
|
42
|
+
resolvePluginManagedPolicy,
|
|
41
43
|
verifyPluginManifest,
|
|
42
44
|
} from "../plugin-security.js";
|
|
45
|
+
import {
|
|
46
|
+
assertPluginGitTransportSafe,
|
|
47
|
+
canonicalizePluginSource,
|
|
48
|
+
normalizePluginLocalSourcePath,
|
|
49
|
+
parsePluginGitSource,
|
|
50
|
+
redactPluginSourceForDisplay,
|
|
51
|
+
} from "../plugin-source-identity.js";
|
|
52
|
+
import { assertRegistryResolutionAuthority } from "./remote-source.js";
|
|
43
53
|
import {
|
|
44
54
|
writePluginLock,
|
|
45
55
|
buildPluginSbom,
|
|
@@ -106,12 +116,18 @@ export const _deps = {
|
|
|
106
116
|
mkdtempSync: fs.mkdtempSync,
|
|
107
117
|
randomToken: () => crypto.randomBytes(16).toString("hex"),
|
|
108
118
|
spawnSync: (...args) => executionBroker.spawnSync(...args),
|
|
119
|
+
gitExecutable: null,
|
|
109
120
|
beforeTransactionPhaseHook: null,
|
|
110
121
|
transactionPhaseHook: null,
|
|
111
122
|
};
|
|
112
123
|
|
|
113
124
|
const pendingPluginTransactions = new WeakMap();
|
|
114
125
|
const ownedPluginLifecycleLocks = new Map();
|
|
126
|
+
const SOURCE_POLICY_PREFLIGHT = Symbol("plugin-source-policy-preflight");
|
|
127
|
+
const UPDATE_MATERIALIZATION = Symbol("plugin-update-materialization");
|
|
128
|
+
const NULL_DEVICE = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
129
|
+
let cachedTrustedGitExecutable = null;
|
|
130
|
+
let cachedTrustedSshExecutable = null;
|
|
115
131
|
|
|
116
132
|
/**
|
|
117
133
|
* Install a plugin from a local directory into a scope's immutable version dir.
|
|
@@ -120,8 +136,11 @@ const ownedPluginLifecycleLocks = new Map();
|
|
|
120
136
|
* @returns {{ name, version, scope, dir, warnings }}
|
|
121
137
|
*/
|
|
122
138
|
export function installFromDirectory(srcDir, opts = {}) {
|
|
139
|
+
opts = preflightManagedSource(srcDir, opts, "install", {
|
|
140
|
+
materializedKind: "directory",
|
|
141
|
+
});
|
|
123
142
|
const scope = opts.scope || "user";
|
|
124
|
-
const src = path.resolve(srcDir);
|
|
143
|
+
const src = path.resolve(opts.cwd || process.cwd(), srcDir);
|
|
125
144
|
if (!_deps.existsSync(src)) {
|
|
126
145
|
throw new Error(`source directory does not exist: ${src}`);
|
|
127
146
|
}
|
|
@@ -157,7 +176,10 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
157
176
|
assertInstalledNameDirectoryIdentity(name, { scope, cwd: opts.cwd });
|
|
158
177
|
assertExpectedPluginIdentity(name, version, opts.expectedIdentity);
|
|
159
178
|
let sourceMetadata = normalizeSourceMetadata(
|
|
160
|
-
|
|
179
|
+
bindSourcePolicyAuthority(
|
|
180
|
+
opts.sourceMetadata ?? { type: "local", source: src },
|
|
181
|
+
opts,
|
|
182
|
+
),
|
|
161
183
|
);
|
|
162
184
|
if (!sourceMetadata) {
|
|
163
185
|
throw new Error("plugin source metadata has no valid source identity");
|
|
@@ -184,6 +206,13 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
184
206
|
{
|
|
185
207
|
name,
|
|
186
208
|
source: opts.policySource || sourceMetadata?.source || null,
|
|
209
|
+
sourceKind:
|
|
210
|
+
sourceMetadata?.type === "registry"
|
|
211
|
+
? "registry"
|
|
212
|
+
: sourceMetadata?.type === "git"
|
|
213
|
+
? "git"
|
|
214
|
+
: null,
|
|
215
|
+
sourcePolicyPreflighted: true,
|
|
187
216
|
action: "install",
|
|
188
217
|
},
|
|
189
218
|
opts.managedPolicy,
|
|
@@ -508,17 +537,34 @@ export function installFromDirectory(srcDir, opts = {}) {
|
|
|
508
537
|
* An optional `#ref` (branch / tag / commit) pins the checkout.
|
|
509
538
|
*/
|
|
510
539
|
export function installFromSource(source, opts = {}) {
|
|
540
|
+
const governedOpts = preflightManagedSource(source, opts, "install");
|
|
511
541
|
return _withMaterializedSource(
|
|
512
542
|
source,
|
|
513
543
|
(dir, info) => {
|
|
514
544
|
const sourceMetadata =
|
|
515
|
-
normalizeSourceMetadata(opts.sourceMetadata) ||
|
|
516
545
|
normalizeSourceMetadata(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
546
|
+
bindSourcePolicyAuthority(governedOpts.sourceMetadata, governedOpts),
|
|
547
|
+
) ||
|
|
548
|
+
normalizeSourceMetadata(
|
|
549
|
+
bindSourcePolicyAuthority(
|
|
550
|
+
info
|
|
551
|
+
? { type: "git", source: info.url, ref: info.ref || null }
|
|
552
|
+
: {
|
|
553
|
+
type: "local",
|
|
554
|
+
source:
|
|
555
|
+
governedOpts[SOURCE_POLICY_PREFLIGHT]?.materialized?.path ||
|
|
556
|
+
path.resolve(
|
|
557
|
+
governedOpts.cwd || process.cwd(),
|
|
558
|
+
String(source || ""),
|
|
559
|
+
),
|
|
560
|
+
},
|
|
561
|
+
governedOpts,
|
|
562
|
+
),
|
|
520
563
|
);
|
|
521
|
-
const res = installFromDirectory(dir, {
|
|
564
|
+
const res = installFromDirectory(dir, {
|
|
565
|
+
...governedOpts,
|
|
566
|
+
sourceMetadata,
|
|
567
|
+
});
|
|
522
568
|
if (!info) return res;
|
|
523
569
|
const result = {
|
|
524
570
|
...res,
|
|
@@ -528,8 +574,166 @@ export function installFromSource(source, opts = {}) {
|
|
|
528
574
|
transferPendingTransaction(res, result);
|
|
529
575
|
return result;
|
|
530
576
|
},
|
|
577
|
+
governedOpts,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function describeMaterializedSource(source, opts, materializedKind = null) {
|
|
582
|
+
if (materializedKind !== "directory") {
|
|
583
|
+
const git = parsePluginGitSource(source);
|
|
584
|
+
if (git) return { kind: "git", git };
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
kind: "directory",
|
|
588
|
+
path: path.resolve(
|
|
589
|
+
opts.cwd || process.cwd(),
|
|
590
|
+
normalizePluginLocalSourcePath(source),
|
|
591
|
+
),
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function preflightManagedSource(source, opts, action, options = {}) {
|
|
596
|
+
if (opts[SOURCE_POLICY_PREFLIGHT]) return opts;
|
|
597
|
+
const materialized = describeMaterializedSource(
|
|
598
|
+
source,
|
|
531
599
|
opts,
|
|
600
|
+
options.materializedKind,
|
|
532
601
|
);
|
|
602
|
+
const registrySource =
|
|
603
|
+
opts.sourceMetadata?.type === "registry"
|
|
604
|
+
? opts.sourceMetadata.registry || opts.sourceMetadata.source
|
|
605
|
+
: null;
|
|
606
|
+
// Policy follows the bytes that will actually be materialized. The only
|
|
607
|
+
// intentional indirection is a registry-backed install, whose catalog URL
|
|
608
|
+
// is authoritative in sourceMetadata; a caller-supplied display/legacy
|
|
609
|
+
// policySource must never substitute a different allowlisted identity.
|
|
610
|
+
const policySource = registrySource ?? source;
|
|
611
|
+
const policyKind = registrySource ? "registry" : materialized.kind;
|
|
612
|
+
const managedPolicy = resolvePluginManagedPolicy(opts);
|
|
613
|
+
let registryAuthority = null;
|
|
614
|
+
if (registrySource) {
|
|
615
|
+
registryAuthority = assertRegistryResolutionAuthority(
|
|
616
|
+
opts.registryResolutionAuthority,
|
|
617
|
+
{
|
|
618
|
+
registryUrl: registrySource,
|
|
619
|
+
source,
|
|
620
|
+
ref: materialized.git?.ref || null,
|
|
621
|
+
},
|
|
622
|
+
);
|
|
623
|
+
if (
|
|
624
|
+
opts.sourceMetadata.resolvedSource !== registryAuthority.declaredSource ||
|
|
625
|
+
(opts.sourceMetadata.ref || null) !== (registryAuthority.ref || null)
|
|
626
|
+
) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
"registry source metadata does not match materialized source",
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
if (
|
|
632
|
+
!opts.sourceMetadata.catalogAuthority ||
|
|
633
|
+
typeof opts.sourceMetadata.catalogAuthority !== "object" ||
|
|
634
|
+
Array.isArray(opts.sourceMetadata.catalogAuthority)
|
|
635
|
+
) {
|
|
636
|
+
throw new Error("registry source metadata requires catalog authority");
|
|
637
|
+
}
|
|
638
|
+
const declaredDocumentSha256 =
|
|
639
|
+
opts.sourceMetadata.catalogAuthority?.registryDocumentSha256 || null;
|
|
640
|
+
if (
|
|
641
|
+
declaredDocumentSha256 &&
|
|
642
|
+
declaredDocumentSha256 !== registryAuthority.documentSha256
|
|
643
|
+
) {
|
|
644
|
+
throw new Error(
|
|
645
|
+
"registry source authority does not match catalog document digest",
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
if (registryAuthority.sourceType === "archive") {
|
|
649
|
+
const archive = opts.sourceMetadata.catalogAuthority?.archiveSource;
|
|
650
|
+
if (
|
|
651
|
+
!archive ||
|
|
652
|
+
archive.archiveSha256 !== registryAuthority.archiveSha256 ||
|
|
653
|
+
archive.payloadSha256 !== registryAuthority.payloadSha256
|
|
654
|
+
) {
|
|
655
|
+
throw new Error(
|
|
656
|
+
"registry source authority does not match archive digest authority",
|
|
657
|
+
);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// Resolve managed identity before exists/clone/cache/process work. The
|
|
662
|
+
// manifest name is checked again by installFromDirectory after materializing
|
|
663
|
+
// bytes; source policy cannot wait for that later gate.
|
|
664
|
+
const decision = enforcePluginSourcePolicy(policySource, managedPolicy, {
|
|
665
|
+
action,
|
|
666
|
+
cwd: opts.cwd,
|
|
667
|
+
kindHint: policyKind,
|
|
668
|
+
});
|
|
669
|
+
const policyIdentity = canonicalizePluginSource(policySource, {
|
|
670
|
+
cwd: opts.cwd,
|
|
671
|
+
kindHint: policyKind,
|
|
672
|
+
});
|
|
673
|
+
const identityDigest = (identity) =>
|
|
674
|
+
crypto
|
|
675
|
+
.createHash("sha256")
|
|
676
|
+
.update(
|
|
677
|
+
canonicalJson({
|
|
678
|
+
identity: identity.identityDigest || identity.key,
|
|
679
|
+
ref: identity.ref ?? null,
|
|
680
|
+
path: identity.path ?? null,
|
|
681
|
+
}),
|
|
682
|
+
)
|
|
683
|
+
.digest("hex");
|
|
684
|
+
const sourceAuthority = {
|
|
685
|
+
policyDigest: decision.policyDigest || null,
|
|
686
|
+
sourceDigest: identityDigest(policyIdentity),
|
|
687
|
+
resolvedSourceDigest: registrySource
|
|
688
|
+
? registryResolvedSourceAuthorityDigest(registryAuthority)
|
|
689
|
+
: null,
|
|
690
|
+
registryDocumentSha256: registryAuthority?.documentSha256 || null,
|
|
691
|
+
};
|
|
692
|
+
const preflight = {
|
|
693
|
+
decision,
|
|
694
|
+
materialized,
|
|
695
|
+
sourceAuthority,
|
|
696
|
+
};
|
|
697
|
+
const boundSourceMetadata = opts.sourceMetadata
|
|
698
|
+
? bindSourcePolicyAuthority(opts.sourceMetadata, {
|
|
699
|
+
[SOURCE_POLICY_PREFLIGHT]: preflight,
|
|
700
|
+
})
|
|
701
|
+
: null;
|
|
702
|
+
const normalizedSourceMetadata = boundSourceMetadata
|
|
703
|
+
? normalizeSourceMetadata(boundSourceMetadata)
|
|
704
|
+
: null;
|
|
705
|
+
if (opts.sourceMetadata && !normalizedSourceMetadata) {
|
|
706
|
+
throw new Error("plugin source metadata is invalid");
|
|
707
|
+
}
|
|
708
|
+
return {
|
|
709
|
+
...opts,
|
|
710
|
+
managedPolicy,
|
|
711
|
+
...(normalizedSourceMetadata
|
|
712
|
+
? { sourceMetadata: normalizedSourceMetadata }
|
|
713
|
+
: {}),
|
|
714
|
+
[SOURCE_POLICY_PREFLIGHT]: preflight,
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function bindSourcePolicyAuthority(sourceMetadata, opts) {
|
|
719
|
+
const authority = opts[SOURCE_POLICY_PREFLIGHT]?.sourceAuthority;
|
|
720
|
+
if (!authority || !sourceMetadata) return sourceMetadata;
|
|
721
|
+
return {
|
|
722
|
+
...sourceMetadata,
|
|
723
|
+
policyDigest: authority.policyDigest,
|
|
724
|
+
sourceDigest: authority.sourceDigest,
|
|
725
|
+
...(authority.resolvedSourceDigest
|
|
726
|
+
? { resolvedSourceDigest: authority.resolvedSourceDigest }
|
|
727
|
+
: {}),
|
|
728
|
+
...(authority.registryDocumentSha256 && sourceMetadata.catalogAuthority
|
|
729
|
+
? {
|
|
730
|
+
catalogAuthority: {
|
|
731
|
+
...sourceMetadata.catalogAuthority,
|
|
732
|
+
registryDocumentSha256: authority.registryDocumentSha256,
|
|
733
|
+
},
|
|
734
|
+
}
|
|
735
|
+
: {}),
|
|
736
|
+
};
|
|
533
737
|
}
|
|
534
738
|
|
|
535
739
|
/**
|
|
@@ -538,12 +742,21 @@ export function installFromSource(source, opts = {}) {
|
|
|
538
742
|
* Single fetch shared by installFromSource + updatePlugin.
|
|
539
743
|
*/
|
|
540
744
|
function _withMaterializedSource(source, fn, opts = {}) {
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
745
|
+
const materialized =
|
|
746
|
+
opts[SOURCE_POLICY_PREFLIGHT]?.materialized ||
|
|
747
|
+
describeMaterializedSource(source, opts);
|
|
748
|
+
if (materialized.kind === "directory") {
|
|
749
|
+
if (
|
|
750
|
+
_deps.existsSync(materialized.path) &&
|
|
751
|
+
_deps.lstatSync(materialized.path).isDirectory()
|
|
752
|
+
) {
|
|
753
|
+
return fn(materialized.path, null);
|
|
754
|
+
}
|
|
755
|
+
throw new Error(
|
|
756
|
+
`source directory does not exist: ${redactPluginSourceForDisplay(materialized.path)}`,
|
|
757
|
+
);
|
|
545
758
|
}
|
|
546
|
-
const git =
|
|
759
|
+
const git = materialized.git;
|
|
547
760
|
if (git) {
|
|
548
761
|
if (opts.offline === true) {
|
|
549
762
|
const cached = readMarketplaceSourceCache(opts.sourceMetadata, {
|
|
@@ -562,7 +775,7 @@ function _withMaterializedSource(source, fn, opts = {}) {
|
|
|
562
775
|
}
|
|
563
776
|
return result;
|
|
564
777
|
}
|
|
565
|
-
const cloned = fetchGitRepo(git.url, git.ref);
|
|
778
|
+
const cloned = fetchGitRepo(git.url, git.ref, { cwd: opts.cwd });
|
|
566
779
|
try {
|
|
567
780
|
const result = fn(cloned, git);
|
|
568
781
|
if (result && typeof result === "object") {
|
|
@@ -592,9 +805,7 @@ function _withMaterializedSource(source, fn, opts = {}) {
|
|
|
592
805
|
}
|
|
593
806
|
}
|
|
594
807
|
}
|
|
595
|
-
throw new Error(
|
|
596
|
-
`source not found as a local directory or git URL: ${source}`,
|
|
597
|
-
);
|
|
808
|
+
throw new Error("source is neither a local directory nor a git URL");
|
|
598
809
|
}
|
|
599
810
|
|
|
600
811
|
/**
|
|
@@ -606,6 +817,7 @@ function _withMaterializedSource(source, fn, opts = {}) {
|
|
|
606
817
|
* Returns { name, version, previousVersion, updated, reinstalled }.
|
|
607
818
|
*/
|
|
608
819
|
export function updatePlugin(source, opts = {}) {
|
|
820
|
+
opts = preflightManagedSource(source, opts, "upgrade");
|
|
609
821
|
const scope = opts.scope || "user";
|
|
610
822
|
const updateMaterialized = (dir, info) => {
|
|
611
823
|
const manifest = parsePluginManifest(dir);
|
|
@@ -627,8 +839,7 @@ export function updatePlugin(source, opts = {}) {
|
|
|
627
839
|
updatePlugin(source, {
|
|
628
840
|
...opts,
|
|
629
841
|
_lifecycleLock: lifecycleLock,
|
|
630
|
-
|
|
631
|
-
_materializedGitInfo: info,
|
|
842
|
+
[UPDATE_MATERIALIZATION]: { dir, info },
|
|
632
843
|
}),
|
|
633
844
|
);
|
|
634
845
|
}
|
|
@@ -640,11 +851,21 @@ export function updatePlugin(source, opts = {}) {
|
|
|
640
851
|
assertNoRetainedInstallRecovery(name, { scope, cwd: opts.cwd });
|
|
641
852
|
assertExpectedPluginIdentity(name, version, opts.expectedIdentity);
|
|
642
853
|
const sourceMetadata =
|
|
643
|
-
normalizeSourceMetadata(opts.sourceMetadata) ||
|
|
644
854
|
normalizeSourceMetadata(
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
855
|
+
bindSourcePolicyAuthority(opts.sourceMetadata, opts),
|
|
856
|
+
) ||
|
|
857
|
+
normalizeSourceMetadata(
|
|
858
|
+
bindSourcePolicyAuthority(
|
|
859
|
+
info
|
|
860
|
+
? { type: "git", source: info.url, ref: info.ref || null }
|
|
861
|
+
: {
|
|
862
|
+
type: "local",
|
|
863
|
+
source:
|
|
864
|
+
opts[SOURCE_POLICY_PREFLIGHT]?.materialized?.path ||
|
|
865
|
+
path.resolve(opts.cwd || process.cwd(), String(source || "")),
|
|
866
|
+
},
|
|
867
|
+
opts,
|
|
868
|
+
),
|
|
648
869
|
);
|
|
649
870
|
const previousVersion = getActiveVersion(name, { scope, cwd: opts.cwd });
|
|
650
871
|
assertReplacementApprovals({
|
|
@@ -799,10 +1020,10 @@ export function updatePlugin(source, opts = {}) {
|
|
|
799
1020
|
transferPendingTransaction(res, result);
|
|
800
1021
|
return result;
|
|
801
1022
|
};
|
|
802
|
-
if (opts
|
|
1023
|
+
if (opts[UPDATE_MATERIALIZATION]) {
|
|
803
1024
|
return updateMaterialized(
|
|
804
|
-
opts.
|
|
805
|
-
opts.
|
|
1025
|
+
opts[UPDATE_MATERIALIZATION].dir,
|
|
1026
|
+
opts[UPDATE_MATERIALIZATION].info || null,
|
|
806
1027
|
);
|
|
807
1028
|
}
|
|
808
1029
|
return _withMaterializedSource(source, updateMaterialized, opts);
|
|
@@ -1570,19 +1791,158 @@ function rollbackRecoveredWholeNameUninstall(transaction) {
|
|
|
1570
1791
|
* not remote-looking. `owner/repo` expands to a GitHub HTTPS URL.
|
|
1571
1792
|
*/
|
|
1572
1793
|
export function parseGitSource(raw) {
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1794
|
+
return parsePluginGitSource(raw);
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
function isPathWithin(child, root) {
|
|
1798
|
+
const relative = path.relative(root, child);
|
|
1799
|
+
return (
|
|
1800
|
+
relative === "" ||
|
|
1801
|
+
(!path.isAbsolute(relative) &&
|
|
1802
|
+
relative !== ".." &&
|
|
1803
|
+
!relative.startsWith(`..${path.sep}`))
|
|
1804
|
+
);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
function validateTrustedExecutable(value, cwd) {
|
|
1808
|
+
if (typeof value !== "string" || !path.isAbsolute(value)) return null;
|
|
1809
|
+
try {
|
|
1810
|
+
const executable = fs.realpathSync.native(value);
|
|
1811
|
+
const stat = fs.statSync(executable);
|
|
1812
|
+
if (!stat.isFile()) return null;
|
|
1813
|
+
if (process.platform !== "win32" && (Number(stat.mode) & 0o111) === 0) {
|
|
1814
|
+
return null;
|
|
1815
|
+
}
|
|
1816
|
+
if (isPathWithin(executable, path.resolve(cwd))) return null;
|
|
1817
|
+
return executable;
|
|
1818
|
+
} catch {
|
|
1819
|
+
return null;
|
|
1581
1820
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
function resolveTrustedExecutable({
|
|
1824
|
+
cwd,
|
|
1825
|
+
executableName,
|
|
1826
|
+
configured,
|
|
1827
|
+
configuredLabel,
|
|
1828
|
+
injected = null,
|
|
1829
|
+
cached = null,
|
|
1830
|
+
}) {
|
|
1831
|
+
if (injected != null) {
|
|
1832
|
+
const trusted = validateTrustedExecutable(injected, cwd);
|
|
1833
|
+
if (!trusted)
|
|
1834
|
+
throw new Error(`configured ${configuredLabel} is not trusted`);
|
|
1835
|
+
return trusted;
|
|
1836
|
+
}
|
|
1837
|
+
const trustedCached = validateTrustedExecutable(cached, cwd);
|
|
1838
|
+
if (trustedCached) return trustedCached;
|
|
1839
|
+
if (configured != null) {
|
|
1840
|
+
const trusted = validateTrustedExecutable(configured, cwd);
|
|
1841
|
+
if (!trusted) {
|
|
1842
|
+
throw new Error(`${configuredLabel} must be a trusted absolute file`);
|
|
1843
|
+
}
|
|
1844
|
+
return trusted;
|
|
1584
1845
|
}
|
|
1585
|
-
|
|
1846
|
+
for (const rawEntry of String(process.env.PATH || "").split(path.delimiter)) {
|
|
1847
|
+
const entry = rawEntry.replace(/^"|"$/gu, "");
|
|
1848
|
+
if (!entry || !path.isAbsolute(entry)) continue;
|
|
1849
|
+
const trusted = validateTrustedExecutable(
|
|
1850
|
+
path.join(entry, executableName),
|
|
1851
|
+
cwd,
|
|
1852
|
+
);
|
|
1853
|
+
if (trusted) return trusted;
|
|
1854
|
+
}
|
|
1855
|
+
throw new Error(
|
|
1856
|
+
`trusted ${configuredLabel} was not found on an absolute PATH entry`,
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
function resolveTrustedGitExecutable(cwd) {
|
|
1861
|
+
const executable = resolveTrustedExecutable({
|
|
1862
|
+
cwd,
|
|
1863
|
+
executableName: process.platform === "win32" ? "git.exe" : "git",
|
|
1864
|
+
configured: process.env.CHAINLESSCHAIN_GIT_BIN,
|
|
1865
|
+
configuredLabel: "Git executable",
|
|
1866
|
+
injected: _deps.gitExecutable,
|
|
1867
|
+
cached: cachedTrustedGitExecutable,
|
|
1868
|
+
});
|
|
1869
|
+
cachedTrustedGitExecutable = executable;
|
|
1870
|
+
return executable;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
function resolveTrustedSshExecutable(cwd) {
|
|
1874
|
+
const executable = resolveTrustedExecutable({
|
|
1875
|
+
cwd,
|
|
1876
|
+
executableName: process.platform === "win32" ? "ssh.exe" : "ssh",
|
|
1877
|
+
configured: process.env.CHAINLESSCHAIN_SSH_BIN,
|
|
1878
|
+
configuredLabel: "SSH executable",
|
|
1879
|
+
cached: cachedTrustedSshExecutable,
|
|
1880
|
+
});
|
|
1881
|
+
cachedTrustedSshExecutable = executable;
|
|
1882
|
+
return executable;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
function hardenedPluginGitEnvironment() {
|
|
1886
|
+
const environment = {};
|
|
1887
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
1888
|
+
if (!key.toUpperCase().startsWith("GIT_")) environment[key] = value;
|
|
1889
|
+
}
|
|
1890
|
+
return {
|
|
1891
|
+
...environment,
|
|
1892
|
+
GIT_ATTR_NOSYSTEM: "1",
|
|
1893
|
+
GIT_CONFIG_COUNT: "0",
|
|
1894
|
+
GIT_CONFIG_GLOBAL: NULL_DEVICE,
|
|
1895
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
1896
|
+
GIT_EXTERNAL_DIFF: "",
|
|
1897
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
1898
|
+
GIT_PAGER: "",
|
|
1899
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1900
|
+
LC_ALL: "C",
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
function quoteSshCommandArgument(value) {
|
|
1905
|
+
if (process.platform === "win32") {
|
|
1906
|
+
return `"${String(value).replace(/"/gu, '\\"')}"`;
|
|
1907
|
+
}
|
|
1908
|
+
return `'${String(value).replace(/'/gu, `'\\''`)}'`;
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
function hardenedPluginGitArgs(args, { url, cwd }) {
|
|
1912
|
+
const parsed = parsePluginGitSource(url);
|
|
1913
|
+
const sshTransport =
|
|
1914
|
+
/^ssh:\/\//iu.test(parsed?.url || "") ||
|
|
1915
|
+
(!(parsed?.url || "").includes("://") &&
|
|
1916
|
+
/^(?:[^@\s/:]+@)?(?:\[[^\]]+\]|[^\s/:]+):/u.test(parsed?.url || "") &&
|
|
1917
|
+
!/^[a-z]:[\\/]/iu.test(parsed?.url || ""));
|
|
1918
|
+
const sshArgs = sshTransport
|
|
1919
|
+
? [
|
|
1920
|
+
"-c",
|
|
1921
|
+
`core.sshCommand=${quoteSshCommandArgument(resolveTrustedSshExecutable(cwd))} -F ${quoteSshCommandArgument(NULL_DEVICE)} -o BatchMode=yes -o ClearAllForwardings=yes -o PermitLocalCommand=no -o ProxyCommand=none -o CanonicalizeHostname=no`,
|
|
1922
|
+
]
|
|
1923
|
+
: [];
|
|
1924
|
+
const fileTransport = /^file:\/\//iu.test(parsed?.url || "");
|
|
1925
|
+
return [
|
|
1926
|
+
"--no-pager",
|
|
1927
|
+
"-c",
|
|
1928
|
+
`core.hooksPath=${NULL_DEVICE}`,
|
|
1929
|
+
"-c",
|
|
1930
|
+
"core.fsmonitor=false",
|
|
1931
|
+
"-c",
|
|
1932
|
+
"core.untrackedCache=false",
|
|
1933
|
+
"-c",
|
|
1934
|
+
"credential.helper=",
|
|
1935
|
+
"-c",
|
|
1936
|
+
"diff.external=",
|
|
1937
|
+
"-c",
|
|
1938
|
+
"http.followRedirects=false",
|
|
1939
|
+
"-c",
|
|
1940
|
+
"protocol.ext.allow=never",
|
|
1941
|
+
"-c",
|
|
1942
|
+
`protocol.file.allow=${fileTransport ? "always" : "never"}`,
|
|
1943
|
+
...sshArgs,
|
|
1944
|
+
...args,
|
|
1945
|
+
];
|
|
1586
1946
|
}
|
|
1587
1947
|
|
|
1588
1948
|
/**
|
|
@@ -1590,30 +1950,46 @@ export function parseGitSource(raw) {
|
|
|
1590
1950
|
* checkout path. Uses `git` via spawn WITHOUT a shell (url/ref are argv, not a
|
|
1591
1951
|
* command line — no injection). Caller removes the temp dir's parent.
|
|
1592
1952
|
*/
|
|
1593
|
-
export function fetchGitRepo(url, ref) {
|
|
1953
|
+
export function fetchGitRepo(url, ref, options = {}) {
|
|
1594
1954
|
// git argv-injection guard: a value starting with "-" is parsed by git as an
|
|
1595
1955
|
// OPTION, not a URL/ref — e.g. a registry-supplied ref "-f" reaches
|
|
1596
1956
|
// `git checkout <ref>` on the full-clone retry path, and an option-looking
|
|
1597
1957
|
// url reaches `git clone`. Real git URLs/refs never start with "-"
|
|
1598
1958
|
// (check-ref-format forbids it), so reject instead of trying to escape.
|
|
1599
1959
|
if (String(url).startsWith("-")) {
|
|
1600
|
-
throw new Error(
|
|
1960
|
+
throw new Error("refusing git source that looks like an option");
|
|
1601
1961
|
}
|
|
1602
1962
|
if (ref != null && String(ref).startsWith("-")) {
|
|
1603
|
-
throw new Error(
|
|
1963
|
+
throw new Error("refusing git ref that looks like an option");
|
|
1604
1964
|
}
|
|
1965
|
+
const git = parsePluginGitSource(ref ? `${url}#${ref}` : url);
|
|
1966
|
+
assertPluginGitTransportSafe(git, { allowFile: true });
|
|
1967
|
+
const cwd = path.resolve(options.cwd || process.cwd());
|
|
1968
|
+
const executable = resolveTrustedGitExecutable(cwd);
|
|
1605
1969
|
const base = _deps.mkdtempSync(path.join(os.tmpdir(), "cc-plugin-git-"));
|
|
1606
1970
|
const dir = path.join(base, "repo");
|
|
1607
|
-
const run = (args) =>
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1971
|
+
const run = (args) => {
|
|
1972
|
+
const hardenedArgs = hardenedPluginGitArgs(args, { url, cwd });
|
|
1973
|
+
const auditRedactArgIndexes = hardenedArgs.flatMap((argument, index) =>
|
|
1974
|
+
argument === url || (ref != null && argument === ref) ? [index] : [],
|
|
1975
|
+
);
|
|
1976
|
+
try {
|
|
1977
|
+
return _deps.spawnSync(executable, hardenedArgs, {
|
|
1978
|
+
cwd: base,
|
|
1979
|
+
encoding: "utf8",
|
|
1980
|
+
env: hardenedPluginGitEnvironment(),
|
|
1981
|
+
timeout: 120000,
|
|
1982
|
+
windowsHide: true,
|
|
1983
|
+
origin: "plugin:install-git",
|
|
1984
|
+
policy: "allow",
|
|
1985
|
+
scope: "plugin-install",
|
|
1986
|
+
shell: false,
|
|
1987
|
+
auditRedactArgIndexes,
|
|
1988
|
+
});
|
|
1989
|
+
} catch (error) {
|
|
1990
|
+
return { error, status: null, stdout: "", stderr: "" };
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1617
1993
|
|
|
1618
1994
|
// Reduce host-specific checkout differences before the v2 inventory hashes
|
|
1619
1995
|
// the materialized staged bytes. Repository .gitattributes may still define
|
|
@@ -1636,6 +2012,11 @@ export function fetchGitRepo(url, ref) {
|
|
|
1636
2012
|
if (res.error && res.error.code === "ENOENT") {
|
|
1637
2013
|
throw new Error("git is not installed (needed to fetch a remote plugin)");
|
|
1638
2014
|
}
|
|
2015
|
+
if (res.error) {
|
|
2016
|
+
throw new Error(
|
|
2017
|
+
`git process failed for ${redactPluginSourceForDisplay(url)}`,
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
1639
2020
|
if (res.status !== 0) {
|
|
1640
2021
|
// A commit SHA can't be used with --branch/--depth; retry with a full clone
|
|
1641
2022
|
// then checkout the ref explicitly.
|
|
@@ -1651,8 +2032,9 @@ export function fetchGitRepo(url, ref) {
|
|
|
1651
2032
|
if (co.status === 0) return dir;
|
|
1652
2033
|
}
|
|
1653
2034
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
2035
|
+
throw new Error(
|
|
2036
|
+
`git clone failed for ${redactPluginSourceForDisplay(url)} (git exited ${res.status})`,
|
|
2037
|
+
);
|
|
1656
2038
|
}
|
|
1657
2039
|
return dir;
|
|
1658
2040
|
}
|
|
@@ -2987,13 +3369,19 @@ function sameSourceAuthority(current, candidate) {
|
|
|
2987
3369
|
if (current?.type !== candidate?.type) return false;
|
|
2988
3370
|
if (current.type === "registry") {
|
|
2989
3371
|
return Boolean(
|
|
2990
|
-
current.
|
|
2991
|
-
candidate.
|
|
2992
|
-
current.
|
|
3372
|
+
current.sourceDigest &&
|
|
3373
|
+
candidate.sourceDigest &&
|
|
3374
|
+
current.sourceDigest === candidate.sourceDigest &&
|
|
3375
|
+
current.resolvedSourceDigest &&
|
|
3376
|
+
candidate.resolvedSourceDigest &&
|
|
3377
|
+
current.resolvedSourceDigest === candidate.resolvedSourceDigest,
|
|
2993
3378
|
);
|
|
2994
3379
|
}
|
|
2995
|
-
|
|
2996
|
-
|
|
3380
|
+
return Boolean(
|
|
3381
|
+
current.sourceDigest &&
|
|
3382
|
+
candidate.sourceDigest &&
|
|
3383
|
+
current.sourceDigest === candidate.sourceDigest,
|
|
3384
|
+
);
|
|
2997
3385
|
}
|
|
2998
3386
|
|
|
2999
3387
|
function effectivePluginRecord(
|
|
@@ -3229,23 +3617,119 @@ function normalizeSourceMetadata(value) {
|
|
|
3229
3617
|
type,
|
|
3230
3618
|
source,
|
|
3231
3619
|
ref: cleanBounded(value.ref, 256),
|
|
3620
|
+
sourceDigest:
|
|
3621
|
+
normalizeSourceAuthorityDigest(value.sourceDigest) ||
|
|
3622
|
+
pluginSourceAuthorityDigest(value.source, type, value.ref),
|
|
3232
3623
|
};
|
|
3233
3624
|
const registry = sanitizeSource(value.registry);
|
|
3234
3625
|
const resolvedSource = sanitizeSource(value.resolvedSource);
|
|
3235
3626
|
const packageName = cleanBounded(value.package, 256);
|
|
3627
|
+
const catalogAuthority =
|
|
3628
|
+
value.catalogAuthority != null
|
|
3629
|
+
? normalizeCatalogAuthority(value.catalogAuthority)
|
|
3630
|
+
: null;
|
|
3236
3631
|
if (registry) metadata.registry = registry;
|
|
3237
3632
|
if (resolvedSource) metadata.resolvedSource = resolvedSource;
|
|
3633
|
+
const resolvedSourceDigest =
|
|
3634
|
+
normalizeSourceAuthorityDigest(value.resolvedSourceDigest) ||
|
|
3635
|
+
(value.resolvedSource
|
|
3636
|
+
? catalogAuthority?.archiveSource
|
|
3637
|
+
? archiveSourceAuthorityDigest(
|
|
3638
|
+
value.resolvedSource,
|
|
3639
|
+
catalogAuthority.archiveSource,
|
|
3640
|
+
)
|
|
3641
|
+
: pluginSourceAuthorityDigest(value.resolvedSource, "git", value.ref)
|
|
3642
|
+
: null);
|
|
3643
|
+
const policyDigest = normalizeSourceAuthorityDigest(value.policyDigest);
|
|
3644
|
+
if (resolvedSourceDigest) {
|
|
3645
|
+
metadata.resolvedSourceDigest = resolvedSourceDigest;
|
|
3646
|
+
}
|
|
3647
|
+
if (policyDigest) metadata.policyDigest = policyDigest;
|
|
3238
3648
|
if (packageName) metadata.package = packageName;
|
|
3239
3649
|
if (value.offline === true) metadata.offline = true;
|
|
3240
|
-
if (
|
|
3241
|
-
metadata.catalogAuthority = normalizeCatalogAuthority(
|
|
3242
|
-
value.catalogAuthority,
|
|
3243
|
-
);
|
|
3244
|
-
}
|
|
3650
|
+
if (catalogAuthority) metadata.catalogAuthority = catalogAuthority;
|
|
3245
3651
|
assertCatalogRemoteArtifactBindings(metadata);
|
|
3652
|
+
assertCatalogArchiveSourceBindings(metadata);
|
|
3246
3653
|
return metadata;
|
|
3247
3654
|
}
|
|
3248
3655
|
|
|
3656
|
+
function assertCatalogArchiveSourceBindings(metadata) {
|
|
3657
|
+
const archive = metadata.catalogAuthority?.archiveSource;
|
|
3658
|
+
if (!archive) return;
|
|
3659
|
+
if (
|
|
3660
|
+
metadata.type !== "registry" ||
|
|
3661
|
+
!metadata.registry ||
|
|
3662
|
+
!metadata.resolvedSource
|
|
3663
|
+
) {
|
|
3664
|
+
throw new Error(
|
|
3665
|
+
"marketplace archive authority requires registry and resolved source metadata",
|
|
3666
|
+
);
|
|
3667
|
+
}
|
|
3668
|
+
let registryOrigin;
|
|
3669
|
+
try {
|
|
3670
|
+
registryOrigin = new URL(metadata.registry).origin;
|
|
3671
|
+
} catch {
|
|
3672
|
+
throw new Error("marketplace archive registry URL is invalid");
|
|
3673
|
+
}
|
|
3674
|
+
if (
|
|
3675
|
+
archive.registryOrigin !== registryOrigin ||
|
|
3676
|
+
archive.url !== metadata.resolvedSource
|
|
3677
|
+
) {
|
|
3678
|
+
throw new Error(
|
|
3679
|
+
"marketplace archive authority does not match the selected registry and source",
|
|
3680
|
+
);
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
|
|
3684
|
+
function normalizeSourceAuthorityDigest(value) {
|
|
3685
|
+
const normalized = cleanBounded(value, 64)?.toLowerCase() || null;
|
|
3686
|
+
return normalized && /^[a-f0-9]{64}$/u.test(normalized) ? normalized : null;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
function pluginSourceAuthorityDigest(source, type, ref = null) {
|
|
3690
|
+
const identity = canonicalizePluginSource(source, {
|
|
3691
|
+
kindHint:
|
|
3692
|
+
type === "registry" ? "registry" : type === "local" ? "directory" : "git",
|
|
3693
|
+
ref: ref || undefined,
|
|
3694
|
+
});
|
|
3695
|
+
return crypto
|
|
3696
|
+
.createHash("sha256")
|
|
3697
|
+
.update(
|
|
3698
|
+
canonicalJson({
|
|
3699
|
+
identity: identity.identityDigest || identity.key,
|
|
3700
|
+
ref: identity.ref ?? null,
|
|
3701
|
+
path: identity.path ?? null,
|
|
3702
|
+
}),
|
|
3703
|
+
)
|
|
3704
|
+
.digest("hex");
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
function archiveSourceAuthorityDigest(source, archive) {
|
|
3708
|
+
return crypto
|
|
3709
|
+
.createHash("sha256")
|
|
3710
|
+
.update(
|
|
3711
|
+
canonicalJson({
|
|
3712
|
+
kind: "archive",
|
|
3713
|
+
source: sanitizeSource(source),
|
|
3714
|
+
archiveSha256: archive.archiveSha256,
|
|
3715
|
+
payloadSha256: archive.payloadSha256,
|
|
3716
|
+
}),
|
|
3717
|
+
)
|
|
3718
|
+
.digest("hex");
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
function registryResolvedSourceAuthorityDigest(authority) {
|
|
3722
|
+
if (!authority) return null;
|
|
3723
|
+
if (authority.sourceType === "archive") {
|
|
3724
|
+
return archiveSourceAuthorityDigest(authority.declaredSource, authority);
|
|
3725
|
+
}
|
|
3726
|
+
return pluginSourceAuthorityDigest(
|
|
3727
|
+
authority.declaredSource,
|
|
3728
|
+
"git",
|
|
3729
|
+
authority.ref,
|
|
3730
|
+
);
|
|
3731
|
+
}
|
|
3732
|
+
|
|
3249
3733
|
function normalizeMigrationIssuedAt(value) {
|
|
3250
3734
|
const date = value == null ? new Date() : new Date(String(value));
|
|
3251
3735
|
if (!Number.isFinite(date.getTime())) {
|
|
@@ -3545,6 +4029,7 @@ function normalizeCatalogAuthority(value) {
|
|
|
3545
4029
|
const remoteArtifactEvidence = normalizeRemoteArtifactEvidence(
|
|
3546
4030
|
value.remoteArtifactEvidence,
|
|
3547
4031
|
);
|
|
4032
|
+
const archiveSource = normalizeArchiveSourceAuthority(value.archiveSource);
|
|
3548
4033
|
const remoteSbomPayloadComparison = assertRemoteSbomPayloadComparison(
|
|
3549
4034
|
value.remoteSbomPayloadComparison,
|
|
3550
4035
|
{
|
|
@@ -3651,6 +4136,7 @@ function normalizeCatalogAuthority(value) {
|
|
|
3651
4136
|
...(registryNetworkAuthority ? { registryNetworkAuthority } : {}),
|
|
3652
4137
|
...(artifactExpectations ? { artifactExpectations } : {}),
|
|
3653
4138
|
...(remoteArtifactEvidence ? { remoteArtifactEvidence } : {}),
|
|
4139
|
+
...(archiveSource ? { archiveSource } : {}),
|
|
3654
4140
|
...(remoteSbomPayloadComparison ? { remoteSbomPayloadComparison } : {}),
|
|
3655
4141
|
...(publisherDeclaration ? { publisherDeclaration } : {}),
|
|
3656
4142
|
...(publisherAuthority ? { publisherAuthority } : {}),
|
|
@@ -3661,6 +4147,72 @@ function normalizeCatalogAuthority(value) {
|
|
|
3661
4147
|
};
|
|
3662
4148
|
}
|
|
3663
4149
|
|
|
4150
|
+
function normalizeArchiveSourceAuthority(value) {
|
|
4151
|
+
if (value == null) return null;
|
|
4152
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4153
|
+
throw new Error("catalogAuthority.archiveSource must be an object");
|
|
4154
|
+
}
|
|
4155
|
+
const url = cleanBounded(value.url, 4096);
|
|
4156
|
+
const registryOrigin = cleanBounded(value.registryOrigin, 4096);
|
|
4157
|
+
const archiveSha256 = cleanBounded(value.archiveSha256, 64);
|
|
4158
|
+
const payloadSha256 = cleanBounded(value.payloadSha256, 64);
|
|
4159
|
+
let archiveUrl;
|
|
4160
|
+
let selectedOrigin;
|
|
4161
|
+
try {
|
|
4162
|
+
archiveUrl = new URL(url);
|
|
4163
|
+
selectedOrigin = new URL(registryOrigin).origin;
|
|
4164
|
+
} catch {
|
|
4165
|
+
throw new Error("catalogAuthority.archiveSource URL authority is invalid");
|
|
4166
|
+
}
|
|
4167
|
+
if (
|
|
4168
|
+
archiveUrl.origin !== selectedOrigin ||
|
|
4169
|
+
archiveUrl.username ||
|
|
4170
|
+
archiveUrl.password
|
|
4171
|
+
) {
|
|
4172
|
+
throw new Error(
|
|
4173
|
+
"catalogAuthority.archiveSource must use the selected registry origin",
|
|
4174
|
+
);
|
|
4175
|
+
}
|
|
4176
|
+
if (
|
|
4177
|
+
value.schemaVersion !== "cc-plugin-marketplace-archive-source/v1" ||
|
|
4178
|
+
value.status !== "digest-verified-and-extracted" ||
|
|
4179
|
+
!/^[a-f0-9]{64}$/u.test(archiveSha256 || "") ||
|
|
4180
|
+
!/^[a-f0-9]{64}$/u.test(payloadSha256 || "")
|
|
4181
|
+
) {
|
|
4182
|
+
throw new Error(
|
|
4183
|
+
"catalogAuthority.archiveSource digest authority is invalid",
|
|
4184
|
+
);
|
|
4185
|
+
}
|
|
4186
|
+
const compressedBytes = Number(value.compressedBytes);
|
|
4187
|
+
const fileCount = Number(value.fileCount);
|
|
4188
|
+
const totalBytes = Number(value.totalBytes);
|
|
4189
|
+
if (
|
|
4190
|
+
!Number.isSafeInteger(compressedBytes) ||
|
|
4191
|
+
compressedBytes <= 0 ||
|
|
4192
|
+
compressedBytes > 64 * 1024 * 1024 ||
|
|
4193
|
+
!Number.isSafeInteger(fileCount) ||
|
|
4194
|
+
fileCount <= 0 ||
|
|
4195
|
+
fileCount > 10_000 ||
|
|
4196
|
+
!Number.isSafeInteger(totalBytes) ||
|
|
4197
|
+
totalBytes <= 0 ||
|
|
4198
|
+
totalBytes > 256 * 1024 * 1024
|
|
4199
|
+
) {
|
|
4200
|
+
throw new Error("catalogAuthority.archiveSource size authority is invalid");
|
|
4201
|
+
}
|
|
4202
|
+
return {
|
|
4203
|
+
schemaVersion: value.schemaVersion,
|
|
4204
|
+
status: value.status,
|
|
4205
|
+
url: archiveUrl.href,
|
|
4206
|
+
registryOrigin: selectedOrigin,
|
|
4207
|
+
archiveSha256,
|
|
4208
|
+
payloadSha256,
|
|
4209
|
+
compressedBytes,
|
|
4210
|
+
fileCount,
|
|
4211
|
+
totalBytes,
|
|
4212
|
+
fromCache: value.fromCache === true,
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
|
|
3664
4216
|
function normalizeArtifactExpectations(value) {
|
|
3665
4217
|
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
3666
4218
|
const digest = (candidate, label) => {
|
|
@@ -3928,16 +4480,7 @@ function sanitizeSource(value, preservePath = false) {
|
|
|
3928
4480
|
const raw = cleanBounded(value, 4096);
|
|
3929
4481
|
if (!raw) return null;
|
|
3930
4482
|
if (preservePath) return raw;
|
|
3931
|
-
|
|
3932
|
-
const parsed = new URL(raw);
|
|
3933
|
-
parsed.username = "";
|
|
3934
|
-
parsed.password = "";
|
|
3935
|
-
parsed.search = "";
|
|
3936
|
-
parsed.hash = "";
|
|
3937
|
-
return parsed.toString();
|
|
3938
|
-
} catch {
|
|
3939
|
-
return raw;
|
|
3940
|
-
}
|
|
4483
|
+
return redactPluginSourceForDisplay(raw);
|
|
3941
4484
|
}
|
|
3942
4485
|
|
|
3943
4486
|
function cleanBounded(value, max) {
|