chainlesschain 0.166.65 → 0.166.69
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/README.md +20 -12
- package/package.json +4 -4
- package/src/assets/web-panel/assets/{AIOps-Byz17CTN.js → AIOps-FhevRfL6.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C5LiW2-J.js → ActionButton-rOeOykx5.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D1INcvZF.js → Analytics-BK_wYlfz.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DZzHO1VV.js → AppLayout-_2cCtZKV.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-WfYQL6Ia.js → Artifacts-D5MSvVzk.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DohBoGWO.js → Audit-yd-Ah-A3.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-Chtk-Hde.js → BackgroundAgents-yCaw_Gu_.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-B813PuMz.js → Backup-G3ADD4N-.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-DaDdWWgK.js → BaseInput-ByDhkLbF.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-D4pJ6oZ7.js → Chat-BMrc7qjz.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-nA7n0I6S.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-Clo9TvO6.js → Checkbox-BHYgyZWA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-uejLg8Ih.js → Codegen-DJfcy0jC.js} +1 -1
- package/src/assets/web-panel/assets/{Col-B7nbx-iJ.js → Col-C27zROxc.js} +1 -1
- package/src/assets/web-panel/assets/{Community-FEyjPK1j.js → Community-CuS5tq_1.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B6Fk0Kn6.js → Compact-BrYJNe7Q.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BQG8goUl.js → Compliance-8hfahYHT.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CbBeEfHp.js → Cowork-DdE0n2lp.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DPzlFcEZ.js → Cron-CUq3aSiX.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B8pWJrbg.js → Crosschain-BM4XSPWG.js} +1 -1
- package/src/assets/web-panel/assets/{DID-gGQHUlFN.js → DID-CdaBY57N.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-C2TMFHrn.js → Dashboard-Cwtd52So.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BVPxGbll.js → Dropdown-BvlObh6U.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CNPxs0hd.js → EmailListRenderer-e3fLhjnX.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-_XTiB7q1.js → EvolutionSettings-CTtk1Zo8.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-UyE3VWtI.js → FamilyGuardDashboard-BpPJUnCU.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-CLt6FHK-.js → Federation-vzjZ6_nQ.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CdNQPRPw.js → FormItemContext-DEDNLhkE.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BJ0AMhrh.js → GenericCardRenderer-DEl2Te5f.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CQnvMfOM.js → Git-BzsYQnCq.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-deYsHC-o.js → Governance-zxJs6Y8s.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-qaSck43z.js → Inference-5JUsrmF1.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-De0aJ3Ls.js → KnowledgeGraph-Br8b5Htz.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-ZtS98jCm.js → Logs-D-tubDMJ.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-In4itZX2.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-Byz3xCBN.js → Marketplace-s8MmWkS9.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DOZUUW6z.js → McpTools-Wu-WRg4M.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-C33wC2hA.js → Memory-B_i-eX9w.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-zZe-T_6M.js → MobileBridge-Ca64kjVx.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CRnUIume.js → MobileProjects-Cq9AFiO4.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-C1xlRVAN.js → Mtc-DKvGmG3y.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D0U07bIi.js → MtcAudit-BVvWx8pF.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-DF8uvV3h.js → Multisig-wqeJ2O9x.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-bBPGAFlt.js → NLProgramming-DX87ysYu.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-BHAaOsgg.js → Notes-ChenGdu0.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-Cl0fG_rA.js → NotificationSettings-BZRa68-x.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-BEPAs8Zv.js → OrderTableRenderer-DZ6xG1M6.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BAxSwgk5.js → Organization-BWJp4VPX.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-D68KubAu.js → Overflow-BoM4bYt5.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-bhdT17zk.js → P2P-Cvi8YV09.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DqaO6wgH.js → PdhVaultBrowser-CNVC1NIj.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-Bq8fvkak.js → Permissions-DNd1MRjW.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BYd2JEic.js → PersonalDataHub-DGSBYZ5-.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-Csg_ja69.js → Pipeline-DkcoGMBo.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-lwxwLj4M.js → Privacy-DltwvMAW.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BdBZkH8z.js → ProjectInit-ChYbpHg8.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-ClWgFy-K.js → ProjectSettings-B8FWiHTJ.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Bu-qj8N1.js → Projects-DuyhtSm4.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DEjxrxw3.js → Providers-Bo18Tx_6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-eccy-8Vi.js → QrScannerModal-6W85KlsG.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Vx3uDvFg.js → QuickAsk-DznRUedP.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-CfUivNps.js → Recommend-CQDN3_p-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BU4gk7GI.js → RemoteSession-CiJgokEj.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-CbPzdZ0C.js → Reputation-DexzGH7E.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CYLrgfKA.js → Row-BgZOIvSi.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DbY1oyuv.js → RssFeed-BWIZJ_Jr.js} +3 -3
- package/src/assets/web-panel/assets/{Search-QeW41K5r.js → Search-B376EwWJ.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CJxf1kLZ.js → Security-92mUR5OD.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CFO9ySZc.js → Services-ClSEAOHk.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CQFjeQN0.js → Skeleton-DVhLbv99.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CLLB4INW.js → Skills-JTcXs_xI.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DxeLIJk0.js → Sla-DnKY9zy9.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-D4FA9KYI.js → SpeechSettings-GO1JefAH.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-C6NOkcQK.js → SyncSettings-jX5tB2St.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-LmVPWDfQ.js → Tasks-BxRBzhUc.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-BdEfUoVB.js → Templates-CAVkbTG1.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-IUlESacN.js → Tenant-5Y7sHBkz.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B9pDe7sn.js → Terminal-CbbLShYb.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-ZCwB-Pki.js → TimelineRenderer-2HptWihL.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4qk16LX1.js → Tokens-B-jGxzHl.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-jXNyargn.js → Trigger-9DGa90DT.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D13PydtT.js → Trust-BSJEkEOO.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-yPgws6mS.js → UkeySign-C807So5R.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-B329WjpG.js → VideoEditing-ByiieAv-.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-T8VM-Ttm.js → Wallet-BixbIOUv.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-XuRibrpP.js → WebAuthn-z7akhor4.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-D1Gwz3YR.js → WorkflowEditor-KvijuUuR.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DjydtnT-.js → chat-BYKoh0YG.js} +1 -1
- package/src/assets/web-panel/assets/{colors-gFtBvPrr.js → colors-CLT8IYBt.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-CndxhGfx.js → compact-item-DlBIyYHF.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DnfGpd0L.js → createContext-5XmAnNLr.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-BmcCIHwY.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CmT5hRVW.js → hasIn-rPdRhm7K.js} +1 -1
- package/src/assets/web-panel/assets/{index-DoBKSzqu.js → index-8un-eUDI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DGbKeg8G.js → index-B-0nAxf5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DreYqebI.js → index-B1VfBUPB.js} +1 -1
- package/src/assets/web-panel/assets/{index-zQf599Uq.js → index-BBt2CSAx.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlTcxT_L.js → index-BDtlwjSr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqpR0Des.js → index-BIRl2Ii5.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3HvYeA3.js → index-BgevTkUv.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0WFjxWs.js → index-Bve7Wsgf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BNHdO6Jr.js → index-Bzd0sFkp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DV2PR3Fa.js → index-C2sFDVb_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D2d7358Y.js → index-CErvHCDe.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaEPE2DC.js → index-CM_a9spK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKyuE_Y2.js → index-CY6TMc2m.js} +1 -1
- package/src/assets/web-panel/assets/{index-BVM_w30N.js → index-ChlzaTV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZLDbPTa.js → index-CsnbOKdI.js} +1 -1
- package/src/assets/web-panel/assets/{index-HHP1zzo7.js → index-D3-NL-x-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFif4Cev.js → index-D3f9Jzx2.js} +1 -1
- package/src/assets/web-panel/assets/{index-C14jryXi.js → index-DCDSycWo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CB27echm.js → index-DIVrVGvf.js} +1 -1
- package/src/assets/web-panel/assets/{index-BGlmPbl_.js → index-DI_tYFBe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsRKmb6k.js → index-DPS4mWnI.js} +1 -1
- package/src/assets/web-panel/assets/{index-BXNUTiW8.js → index-DTslLzPJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbpk75MO.js → index-DW8vozBS.js} +1 -1
- package/src/assets/web-panel/assets/index-DY8rmd6x.js +1 -0
- package/src/assets/web-panel/assets/{index-DjY-3sMA.js → index-DZiSMYoD.js} +3 -3
- package/src/assets/web-panel/assets/{index-DV6NrfJx.js → index-DZjGcIGU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ktN-K1Tf.js → index-Dea32nOK.js} +1 -1
- package/src/assets/web-panel/assets/{index-DjcR-CwS.js → index-Djd7uqwM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CuOx4Pbe.js → index-Dntid6oj.js} +1 -1
- package/src/assets/web-panel/assets/{index-BW3nWiu0.js → index-DqZU4_7J.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4Mqhmas.js → index-DuvweiCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRqz8TtI.js → index-DzExCCLS.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5na0B7G.js → index-LKT-Ol0A.js} +1 -1
- package/src/assets/web-panel/assets/{index-_-wGLtAg.js → index-akwXS-FF.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgMKElUh.js → index-b-rQCMkx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUlubR4l.js → index-eHbr3f5I.js} +1 -1
- package/src/assets/web-panel/assets/{index-D88-8bsB.js → index-kx6zDWew.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuAvYTmY.js → index-rRuCGdKW.js} +1 -1
- package/src/assets/web-panel/assets/index-sbwcUeRS.js +1 -0
- package/src/assets/web-panel/assets/{initDefaultProps-KV3V-PNH.js → initDefaultProps-a8411LiK.js} +1 -1
- package/src/assets/web-panel/assets/{motion-PFGQg7_X.js → motion-oVcQdpCm.js} +1 -1
- package/src/assets/web-panel/assets/{move-Dnl79wq4.js → move-B-eyzzzL.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-Bwl5JH9V.js → mtc-parser-BCYIxuG7.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DCcTtM1l.js → omit-DuFtWiYh.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Cl6BjjDf.js → pickAttrs-CVAoJhGu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-fdqhyo42.js → placementArrow-Y2iub2bO.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DYQpmcDW.js → responsiveObserve-BHenWIVD.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C4l-VfO4.js → slide-CKrMaL29.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DYDw_dyZ.js → statusUtils-w8eb_1da.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BEsubSMu.js → styleChecker-CCVi_Ww3.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Cj82ggQ_.js → useFlexGapSupport-BsmR_q0r.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-B7L1eJ0V.js → useFs-a8GMBjnc.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-DVlJVD4d.js → usePersonalDataHub-BMUYSPn-.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BBAbRN_Y.js → vnode-DL2pI31V.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-BYn48q-I.js → zoom-Cbi8fISi.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/data/changelog.json +33 -7
- package/src/lib/dependency-install-policy.js +1 -1
- package/src/lib/evolution/browser-download-action-authority.js +747 -0
- package/src/lib/evolution/browser-download-artifact-disposal-authority.js +436 -0
- package/src/lib/evolution/browser-filesystem-quarantine-custody.js +2453 -0
- package/src/lib/evolution/browser-filesystem-quarantine-disposal.js +36 -0
- package/src/lib/evolution/browser-keyboard-action-authority.js +560 -0
- package/src/lib/evolution/browser-navigation-action-authority.js +546 -0
- package/src/lib/evolution/browser-quarantine-lock-maintenance-authority.js +573 -0
- package/src/lib/evolution/browser-quarantine-operator-revocation-authority.js +442 -0
- package/src/lib/evolution/browser-quarantine-retention-authority.js +503 -0
- package/src/lib/evolution/browser-quarantine-retention-scheduler.js +207 -0
- package/src/lib/evolution/browser-quarantined-download-executor.js +525 -0
- package/src/lib/evolution/browser-tab-open-action-authority.js +491 -0
- package/src/lib/evolution/browser-vision-action-authority.js +444 -0
- package/src/lib/evolution/browser-vision-observation-authority.js +304 -0
- package/src/lib/evolution/evolution-deployment-loader.js +241 -1
- package/src/lib/evolution/evolution-eval-child-evidence-ledger-adapter.js +5 -0
- package/src/lib/evolution/evolution-eval-gate.js +2 -1
- package/src/lib/evolution/evolution-eval-process-supervisor-worker.mjs +71 -9
- package/src/lib/evolution/evolution-eval-process-supervisor.js +276 -9
- package/src/lib/evolution/evolution-workbench-metrics.js +81 -21
- package/src/lib/evolution/pm-exploration-benchmark.js +747 -0
- package/src/lib/evolution/pm-exploration-budget-executor.js +106 -9
- package/src/lib/evolution/pm-exploration-egress-authority.js +382 -0
- package/src/lib/evolution/pm-exploration-evidence-bundle.js +149 -44
- package/src/lib/evolution/pm-exploration-execution-host.js +1254 -8
- package/src/lib/evolution/pm-exploration-process-grader.js +458 -0
- package/src/lib/evolution/pm-exploration-process-reviewer.js +456 -0
- package/src/lib/evolution/pm-exploration-process-runner.js +500 -0
- package/src/lib/evolution/pm-exploration-receipts.js +211 -11
- package/src/lib/evolution/pm-exploration-recovery-snapshot-store.js +465 -46
- package/src/lib/evolution/pm-exploration-transition-committer.js +185 -34
- package/src/lib/evolution/skill-outcome-index-authority.js +7 -3
- package/src/lib/evolution/skill-writer-inventory-manifest.js +0 -39
- package/src/lib/evolution/volcengine-function-execution-authority.js +2949 -0
- package/src/lib/evolution/volcengine-function-process-executor.js +615 -0
- package/src/lib/evolution/volcengine-function-replay-store.js +836 -0
- package/src/lib/evolution/volcengine-function-revocation-status-floor-store.js +480 -0
- package/src/lib/governance-coverage.js +1 -1
- package/src/lib/headless-manifest.js +1 -1
- package/src/lib/protocol-replay.js +1 -1
- package/src/lib/skill-outcome-transcript-authority.js +45 -5
- package/src/runtime/agent-core.js +17 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Cf3LLgDc.js +0 -1
- package/src/assets/web-panel/assets/MarkdownRenderer-CTsYo9NN.js +0 -1
- package/src/assets/web-panel/assets/devWarning-D60SbiD-.js +0 -1
- package/src/assets/web-panel/assets/index-C0yR4_7h.js +0 -1
- package/src/assets/web-panel/assets/index-D4xuvo03.js +0 -1
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { types } from "node:util";
|
|
3
|
+
|
|
4
|
+
export const BROWSER_QUARANTINED_DOWNLOAD_EXECUTOR_DESCRIPTOR_SCHEMA =
|
|
5
|
+
"chainlesschain.browser-quarantined-download-executor-descriptor/v1";
|
|
6
|
+
export const BROWSER_DOWNLOAD_NETWORK_RESPONSE_SCHEMA =
|
|
7
|
+
"chainlesschain.browser-download-network-response/v1";
|
|
8
|
+
export const BROWSER_DOWNLOAD_QUARANTINE_COMMIT_ACK_SCHEMA =
|
|
9
|
+
"chainlesschain.browser-download-quarantine-commit-ack/v1";
|
|
10
|
+
export const BROWSER_DOWNLOAD_SCAN_ACK_SCHEMA =
|
|
11
|
+
"chainlesschain.browser-download-scan-ack/v1";
|
|
12
|
+
export const BROWSER_DOWNLOAD_COMPLETION_ACK_SCHEMA =
|
|
13
|
+
"chainlesschain.browser-download-completion-ack/v1";
|
|
14
|
+
|
|
15
|
+
const EXECUTION_SCHEMA = "chainlesschain.browser-download-action-execution/v1";
|
|
16
|
+
const ARTIFACT_SCHEMA = "chainlesschain.browser-download-artifact/v1";
|
|
17
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
18
|
+
const ID = /^[A-Za-z0-9._:-]{1,128}$/u;
|
|
19
|
+
const ARTIFACT_REF = /^quarantine:[A-Za-z0-9][A-Za-z0-9._-]{0,95}$/u;
|
|
20
|
+
const CONTENT_TYPE =
|
|
21
|
+
/^[a-z0-9][a-z0-9!#$&^_.+-]{0,126}\/[a-z0-9][a-z0-9!#$&^_.+-]{0,126}$/u;
|
|
22
|
+
const MAX_BYTES = 100 * 1024 * 1024;
|
|
23
|
+
const executors = new WeakSet();
|
|
24
|
+
|
|
25
|
+
function exact(value, keys, label) {
|
|
26
|
+
if (
|
|
27
|
+
!value ||
|
|
28
|
+
typeof value !== "object" ||
|
|
29
|
+
Array.isArray(value) ||
|
|
30
|
+
types.isProxy(value) ||
|
|
31
|
+
![Object.prototype, null].includes(Object.getPrototypeOf(value)) ||
|
|
32
|
+
Reflect.ownKeys(value).length !== keys.length ||
|
|
33
|
+
keys.some((key) => {
|
|
34
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
35
|
+
return !descriptor?.enumerable || !("value" in descriptor);
|
|
36
|
+
})
|
|
37
|
+
)
|
|
38
|
+
throw new TypeError(`${label} has an invalid shape`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function ownFunction(owner, name, label) {
|
|
42
|
+
const descriptor = Object.getOwnPropertyDescriptor(owner, name);
|
|
43
|
+
if (
|
|
44
|
+
!descriptor?.enumerable ||
|
|
45
|
+
!("value" in descriptor) ||
|
|
46
|
+
typeof descriptor.value !== "function" ||
|
|
47
|
+
types.isProxy(descriptor.value)
|
|
48
|
+
)
|
|
49
|
+
throw new TypeError(`${label} is invalid`);
|
|
50
|
+
return descriptor.value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function normalizeUrl(value, label) {
|
|
54
|
+
if (typeof value !== "string" || value.length < 1 || value.length > 16 * 1024)
|
|
55
|
+
throw new TypeError(`${label} is invalid`);
|
|
56
|
+
let parsed;
|
|
57
|
+
try {
|
|
58
|
+
parsed = new URL(value);
|
|
59
|
+
} catch {
|
|
60
|
+
throw new TypeError(`${label} is invalid`);
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
!["http:", "https:"].includes(parsed.protocol) ||
|
|
64
|
+
parsed.username !== "" ||
|
|
65
|
+
parsed.password !== "" ||
|
|
66
|
+
parsed.href.length > 16 * 1024
|
|
67
|
+
)
|
|
68
|
+
throw new TypeError(`${label} is invalid`);
|
|
69
|
+
return parsed.href;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function normalizeOrigins(value, destinationUrl, label) {
|
|
73
|
+
if (
|
|
74
|
+
!Array.isArray(value) ||
|
|
75
|
+
types.isProxy(value) ||
|
|
76
|
+
value.length < 1 ||
|
|
77
|
+
value.length > 16
|
|
78
|
+
)
|
|
79
|
+
throw new TypeError(`${label} is invalid`);
|
|
80
|
+
const origins = value.map((entry) => {
|
|
81
|
+
if (typeof entry !== "string" || entry.length > 2048)
|
|
82
|
+
throw new TypeError(`${label} is invalid`);
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = new URL(entry);
|
|
86
|
+
} catch {
|
|
87
|
+
throw new TypeError(`${label} is invalid`);
|
|
88
|
+
}
|
|
89
|
+
if (
|
|
90
|
+
!["http:", "https:"].includes(parsed.protocol) ||
|
|
91
|
+
parsed.username !== "" ||
|
|
92
|
+
parsed.password !== "" ||
|
|
93
|
+
entry !== parsed.origin
|
|
94
|
+
)
|
|
95
|
+
throw new TypeError(`${label} is invalid`);
|
|
96
|
+
return parsed.origin;
|
|
97
|
+
});
|
|
98
|
+
const normalized = [...new Set(origins)].sort();
|
|
99
|
+
if (
|
|
100
|
+
normalized.length !== origins.length ||
|
|
101
|
+
!normalized.includes(new URL(destinationUrl).origin)
|
|
102
|
+
)
|
|
103
|
+
throw new TypeError(`${label} is invalid`);
|
|
104
|
+
return Object.freeze(normalized);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function normalizeContentTypes(value) {
|
|
108
|
+
if (
|
|
109
|
+
!Array.isArray(value) ||
|
|
110
|
+
types.isProxy(value) ||
|
|
111
|
+
value.length < 1 ||
|
|
112
|
+
value.length > 16 ||
|
|
113
|
+
value.some(
|
|
114
|
+
(entry) => typeof entry !== "string" || !CONTENT_TYPE.test(entry),
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
throw new TypeError("download execution content types are invalid");
|
|
118
|
+
const normalized = [...new Set(value)].sort();
|
|
119
|
+
if (normalized.length !== value.length)
|
|
120
|
+
throw new TypeError("download execution content types are invalid");
|
|
121
|
+
return Object.freeze(normalized);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function normalizeDescriptor(value) {
|
|
125
|
+
exact(
|
|
126
|
+
value,
|
|
127
|
+
[
|
|
128
|
+
"schema",
|
|
129
|
+
"executorId",
|
|
130
|
+
"tenantId",
|
|
131
|
+
"handlerArtifactDigest",
|
|
132
|
+
"networkMode",
|
|
133
|
+
"custodyMode",
|
|
134
|
+
"scannerMode",
|
|
135
|
+
],
|
|
136
|
+
"quarantined download executor descriptor",
|
|
137
|
+
);
|
|
138
|
+
if (
|
|
139
|
+
value.schema !== BROWSER_QUARANTINED_DOWNLOAD_EXECUTOR_DESCRIPTOR_SCHEMA ||
|
|
140
|
+
!ID.test(value.executorId) ||
|
|
141
|
+
!ID.test(value.tenantId) ||
|
|
142
|
+
!DIGEST.test(value.handlerArtifactDigest) ||
|
|
143
|
+
value.networkMode !== "policy-egress" ||
|
|
144
|
+
value.custodyMode !== "exclusive-stream-fsync" ||
|
|
145
|
+
value.scannerMode !== "independent-malware-scan"
|
|
146
|
+
)
|
|
147
|
+
throw new TypeError("quarantined download executor descriptor is invalid");
|
|
148
|
+
return Object.freeze({ ...value });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizeExecution(value) {
|
|
152
|
+
exact(
|
|
153
|
+
value,
|
|
154
|
+
[
|
|
155
|
+
"schema",
|
|
156
|
+
"actionReceiptDigest",
|
|
157
|
+
"requestDigest",
|
|
158
|
+
"targetId",
|
|
159
|
+
"operation",
|
|
160
|
+
"destinationUrl",
|
|
161
|
+
"allowedRedirectOrigins",
|
|
162
|
+
"allowedContentTypes",
|
|
163
|
+
"maxBytes",
|
|
164
|
+
"deadlineAt",
|
|
165
|
+
],
|
|
166
|
+
"quarantined download execution request",
|
|
167
|
+
);
|
|
168
|
+
const destinationUrl = normalizeUrl(
|
|
169
|
+
value.destinationUrl,
|
|
170
|
+
"download execution destination",
|
|
171
|
+
);
|
|
172
|
+
const allowedRedirectOrigins = normalizeOrigins(
|
|
173
|
+
value.allowedRedirectOrigins,
|
|
174
|
+
destinationUrl,
|
|
175
|
+
"download execution redirect origins",
|
|
176
|
+
);
|
|
177
|
+
const allowedContentTypes = normalizeContentTypes(value.allowedContentTypes);
|
|
178
|
+
if (
|
|
179
|
+
value.schema !== EXECUTION_SCHEMA ||
|
|
180
|
+
!DIGEST.test(value.actionReceiptDigest) ||
|
|
181
|
+
!DIGEST.test(value.requestDigest) ||
|
|
182
|
+
typeof value.targetId !== "string" ||
|
|
183
|
+
value.targetId.length < 1 ||
|
|
184
|
+
value.targetId.length > 512 ||
|
|
185
|
+
value.operation !== "download-url" ||
|
|
186
|
+
!Number.isSafeInteger(value.maxBytes) ||
|
|
187
|
+
value.maxBytes < 1 ||
|
|
188
|
+
value.maxBytes > MAX_BYTES ||
|
|
189
|
+
!Number.isFinite(Date.parse(value.deadlineAt))
|
|
190
|
+
)
|
|
191
|
+
throw new TypeError("quarantined download execution request is invalid");
|
|
192
|
+
return Object.freeze({
|
|
193
|
+
...value,
|
|
194
|
+
destinationUrl,
|
|
195
|
+
allowedRedirectOrigins,
|
|
196
|
+
allowedContentTypes,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function normalizeNetworkResponse(value, execution) {
|
|
201
|
+
exact(
|
|
202
|
+
value,
|
|
203
|
+
[
|
|
204
|
+
"schema",
|
|
205
|
+
"statusCode",
|
|
206
|
+
"finalUrl",
|
|
207
|
+
"redirectOrigins",
|
|
208
|
+
"contentType",
|
|
209
|
+
"contentLength",
|
|
210
|
+
"networkReceiptDigest",
|
|
211
|
+
"body",
|
|
212
|
+
],
|
|
213
|
+
"download network response",
|
|
214
|
+
);
|
|
215
|
+
const finalUrl = normalizeUrl(value.finalUrl, "download response final URL");
|
|
216
|
+
const redirectOrigins = normalizeOrigins(
|
|
217
|
+
value.redirectOrigins,
|
|
218
|
+
execution.destinationUrl,
|
|
219
|
+
"download response redirect origins",
|
|
220
|
+
);
|
|
221
|
+
const finalOrigin = new URL(finalUrl).origin;
|
|
222
|
+
if (
|
|
223
|
+
value.schema !== BROWSER_DOWNLOAD_NETWORK_RESPONSE_SCHEMA ||
|
|
224
|
+
value.statusCode !== 200 ||
|
|
225
|
+
typeof value.contentType !== "string" ||
|
|
226
|
+
!CONTENT_TYPE.test(value.contentType) ||
|
|
227
|
+
!execution.allowedContentTypes.includes(value.contentType) ||
|
|
228
|
+
(value.contentLength !== null &&
|
|
229
|
+
(!Number.isSafeInteger(value.contentLength) ||
|
|
230
|
+
value.contentLength < 0 ||
|
|
231
|
+
value.contentLength > execution.maxBytes)) ||
|
|
232
|
+
redirectOrigins.some(
|
|
233
|
+
(origin) => !execution.allowedRedirectOrigins.includes(origin),
|
|
234
|
+
) ||
|
|
235
|
+
!redirectOrigins.includes(finalOrigin) ||
|
|
236
|
+
!DIGEST.test(value.networkReceiptDigest) ||
|
|
237
|
+
!value.body ||
|
|
238
|
+
(typeof value.body !== "object" && typeof value.body !== "function") ||
|
|
239
|
+
types.isProxy(value.body) ||
|
|
240
|
+
typeof value.body[Symbol.asyncIterator] !== "function"
|
|
241
|
+
)
|
|
242
|
+
throw new TypeError("download network response is invalid");
|
|
243
|
+
return Object.freeze({
|
|
244
|
+
...value,
|
|
245
|
+
finalUrl,
|
|
246
|
+
redirectOrigins,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function normalizeQuarantineSession(value) {
|
|
251
|
+
if (!value || typeof value !== "object" || types.isProxy(value))
|
|
252
|
+
throw new TypeError("download quarantine session is invalid");
|
|
253
|
+
exact(
|
|
254
|
+
value,
|
|
255
|
+
["writeChunk", "commitArtifact", "discardArtifact"],
|
|
256
|
+
"download quarantine session",
|
|
257
|
+
);
|
|
258
|
+
return Object.freeze({
|
|
259
|
+
writeChunk: ownFunction(
|
|
260
|
+
value,
|
|
261
|
+
"writeChunk",
|
|
262
|
+
"download quarantine write port",
|
|
263
|
+
),
|
|
264
|
+
commitArtifact: ownFunction(
|
|
265
|
+
value,
|
|
266
|
+
"commitArtifact",
|
|
267
|
+
"download quarantine commit port",
|
|
268
|
+
),
|
|
269
|
+
discardArtifact: ownFunction(
|
|
270
|
+
value,
|
|
271
|
+
"discardArtifact",
|
|
272
|
+
"download quarantine discard port",
|
|
273
|
+
),
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function normalizeQuarantineAck(value, streamed) {
|
|
278
|
+
exact(
|
|
279
|
+
value,
|
|
280
|
+
[
|
|
281
|
+
"schema",
|
|
282
|
+
"artifactRef",
|
|
283
|
+
"artifactDigest",
|
|
284
|
+
"sizeBytes",
|
|
285
|
+
"contentType",
|
|
286
|
+
"quarantineReceiptDigest",
|
|
287
|
+
"authenticated",
|
|
288
|
+
"durable",
|
|
289
|
+
"readbackVerified",
|
|
290
|
+
],
|
|
291
|
+
"download quarantine commit acknowledgement",
|
|
292
|
+
);
|
|
293
|
+
if (
|
|
294
|
+
value.schema !== BROWSER_DOWNLOAD_QUARANTINE_COMMIT_ACK_SCHEMA ||
|
|
295
|
+
!ARTIFACT_REF.test(value.artifactRef) ||
|
|
296
|
+
value.artifactDigest !== streamed.artifactDigest ||
|
|
297
|
+
value.sizeBytes !== streamed.sizeBytes ||
|
|
298
|
+
value.contentType !== streamed.contentType ||
|
|
299
|
+
!DIGEST.test(value.quarantineReceiptDigest) ||
|
|
300
|
+
value.authenticated !== true ||
|
|
301
|
+
value.durable !== true ||
|
|
302
|
+
value.readbackVerified !== true
|
|
303
|
+
)
|
|
304
|
+
throw new Error("download quarantine commit acknowledgement is invalid");
|
|
305
|
+
return Object.freeze({ ...value });
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function normalizeScanAck(value, quarantine) {
|
|
309
|
+
exact(
|
|
310
|
+
value,
|
|
311
|
+
[
|
|
312
|
+
"schema",
|
|
313
|
+
"artifactRef",
|
|
314
|
+
"artifactDigest",
|
|
315
|
+
"sizeBytes",
|
|
316
|
+
"contentType",
|
|
317
|
+
"verdict",
|
|
318
|
+
"scanEvidenceDigest",
|
|
319
|
+
"authenticated",
|
|
320
|
+
"independent",
|
|
321
|
+
],
|
|
322
|
+
"download scan acknowledgement",
|
|
323
|
+
);
|
|
324
|
+
if (
|
|
325
|
+
value.schema !== BROWSER_DOWNLOAD_SCAN_ACK_SCHEMA ||
|
|
326
|
+
value.artifactRef !== quarantine.artifactRef ||
|
|
327
|
+
value.artifactDigest !== quarantine.artifactDigest ||
|
|
328
|
+
value.sizeBytes !== quarantine.sizeBytes ||
|
|
329
|
+
value.contentType !== quarantine.contentType ||
|
|
330
|
+
!["clean", "rejected"].includes(value.verdict) ||
|
|
331
|
+
!DIGEST.test(value.scanEvidenceDigest) ||
|
|
332
|
+
value.authenticated !== true ||
|
|
333
|
+
value.independent !== true
|
|
334
|
+
)
|
|
335
|
+
throw new Error("download scan acknowledgement is invalid");
|
|
336
|
+
return Object.freeze({ ...value });
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function normalizeCompletionAck(value, quarantine, scan) {
|
|
340
|
+
exact(
|
|
341
|
+
value,
|
|
342
|
+
[
|
|
343
|
+
"schema",
|
|
344
|
+
"artifactRef",
|
|
345
|
+
"artifactDigest",
|
|
346
|
+
"scanEvidenceDigest",
|
|
347
|
+
"completionReceiptDigest",
|
|
348
|
+
"authenticated",
|
|
349
|
+
"durable",
|
|
350
|
+
"readbackVerified",
|
|
351
|
+
],
|
|
352
|
+
"download completion acknowledgement",
|
|
353
|
+
);
|
|
354
|
+
if (
|
|
355
|
+
value.schema !== BROWSER_DOWNLOAD_COMPLETION_ACK_SCHEMA ||
|
|
356
|
+
value.artifactRef !== quarantine.artifactRef ||
|
|
357
|
+
value.artifactDigest !== quarantine.artifactDigest ||
|
|
358
|
+
value.scanEvidenceDigest !== scan.scanEvidenceDigest ||
|
|
359
|
+
!DIGEST.test(value.completionReceiptDigest) ||
|
|
360
|
+
value.authenticated !== true ||
|
|
361
|
+
value.durable !== true ||
|
|
362
|
+
value.readbackVerified !== true
|
|
363
|
+
)
|
|
364
|
+
throw new Error("download completion acknowledgement is invalid");
|
|
365
|
+
return Object.freeze({ ...value });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function deadlineGuard(execution, signal, now) {
|
|
369
|
+
const current = now();
|
|
370
|
+
if (signal?.aborted === true)
|
|
371
|
+
throw new Error("download execution was cancelled");
|
|
372
|
+
if (!Number.isFinite(current) || current >= Date.parse(execution.deadlineAt))
|
|
373
|
+
throw new Error("download execution deadline exceeded");
|
|
374
|
+
return current;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function createBrowserQuarantinedDownloadExecutor({
|
|
378
|
+
descriptor,
|
|
379
|
+
openNetworkResponse,
|
|
380
|
+
openQuarantine,
|
|
381
|
+
scanArtifact,
|
|
382
|
+
completeArtifact,
|
|
383
|
+
now = Date.now,
|
|
384
|
+
} = {}) {
|
|
385
|
+
const normalizedDescriptor = normalizeDescriptor(descriptor);
|
|
386
|
+
for (const [name, port] of Object.entries({
|
|
387
|
+
openNetworkResponse,
|
|
388
|
+
openQuarantine,
|
|
389
|
+
scanArtifact,
|
|
390
|
+
completeArtifact,
|
|
391
|
+
now,
|
|
392
|
+
})) {
|
|
393
|
+
if (typeof port !== "function" || types.isProxy(port))
|
|
394
|
+
throw new TypeError(`quarantined download ${name} port is invalid`);
|
|
395
|
+
}
|
|
396
|
+
const executeDownload = async (input, { signal } = {}) => {
|
|
397
|
+
const execution = normalizeExecution(input);
|
|
398
|
+
deadlineGuard(execution, signal, now);
|
|
399
|
+
let session = null;
|
|
400
|
+
try {
|
|
401
|
+
const network = normalizeNetworkResponse(
|
|
402
|
+
await Reflect.apply(openNetworkResponse, undefined, [
|
|
403
|
+
execution,
|
|
404
|
+
Object.freeze({ signal }),
|
|
405
|
+
]),
|
|
406
|
+
execution,
|
|
407
|
+
);
|
|
408
|
+
deadlineGuard(execution, signal, now);
|
|
409
|
+
const artifactId = randomUUID();
|
|
410
|
+
session = normalizeQuarantineSession(
|
|
411
|
+
await Reflect.apply(openQuarantine, undefined, [
|
|
412
|
+
Object.freeze({
|
|
413
|
+
artifactId,
|
|
414
|
+
tenantId: normalizedDescriptor.tenantId,
|
|
415
|
+
maxBytes: execution.maxBytes,
|
|
416
|
+
contentType: network.contentType,
|
|
417
|
+
networkReceiptDigest: network.networkReceiptDigest,
|
|
418
|
+
actionReceiptDigest: execution.actionReceiptDigest,
|
|
419
|
+
}),
|
|
420
|
+
]),
|
|
421
|
+
);
|
|
422
|
+
const hash = createHash("sha256");
|
|
423
|
+
let sizeBytes = 0;
|
|
424
|
+
for await (const value of network.body) {
|
|
425
|
+
deadlineGuard(execution, signal, now);
|
|
426
|
+
if (
|
|
427
|
+
!(value instanceof Uint8Array) ||
|
|
428
|
+
types.isProxy(value) ||
|
|
429
|
+
value.byteLength < 1
|
|
430
|
+
)
|
|
431
|
+
throw new TypeError("download response chunk is invalid");
|
|
432
|
+
if (value.byteLength > execution.maxBytes - sizeBytes)
|
|
433
|
+
throw new Error("download response exceeds its byte budget");
|
|
434
|
+
const chunk = Buffer.from(value);
|
|
435
|
+
sizeBytes += chunk.byteLength;
|
|
436
|
+
hash.update(chunk);
|
|
437
|
+
await Reflect.apply(session.writeChunk, undefined, [chunk]);
|
|
438
|
+
}
|
|
439
|
+
if (
|
|
440
|
+
sizeBytes < 1 ||
|
|
441
|
+
(network.contentLength !== null && network.contentLength !== sizeBytes)
|
|
442
|
+
)
|
|
443
|
+
throw new Error("download response length is invalid");
|
|
444
|
+
deadlineGuard(execution, signal, now);
|
|
445
|
+
const streamed = Object.freeze({
|
|
446
|
+
artifactDigest: `sha256:${hash.digest("hex")}`,
|
|
447
|
+
sizeBytes,
|
|
448
|
+
contentType: network.contentType,
|
|
449
|
+
});
|
|
450
|
+
const quarantine = normalizeQuarantineAck(
|
|
451
|
+
await Reflect.apply(session.commitArtifact, undefined, [streamed]),
|
|
452
|
+
streamed,
|
|
453
|
+
);
|
|
454
|
+
deadlineGuard(execution, signal, now);
|
|
455
|
+
const scan = normalizeScanAck(
|
|
456
|
+
await Reflect.apply(scanArtifact, undefined, [
|
|
457
|
+
Object.freeze({
|
|
458
|
+
artifactRef: quarantine.artifactRef,
|
|
459
|
+
artifactDigest: quarantine.artifactDigest,
|
|
460
|
+
sizeBytes: quarantine.sizeBytes,
|
|
461
|
+
contentType: quarantine.contentType,
|
|
462
|
+
quarantineReceiptDigest: quarantine.quarantineReceiptDigest,
|
|
463
|
+
}),
|
|
464
|
+
Object.freeze({ signal }),
|
|
465
|
+
]),
|
|
466
|
+
quarantine,
|
|
467
|
+
);
|
|
468
|
+
if (scan.verdict !== "clean")
|
|
469
|
+
throw new Error("download artifact was rejected by malware scanning");
|
|
470
|
+
const completedAtMs = deadlineGuard(execution, signal, now);
|
|
471
|
+
const completedAt = new Date(completedAtMs).toISOString();
|
|
472
|
+
const completion = normalizeCompletionAck(
|
|
473
|
+
await Reflect.apply(completeArtifact, undefined, [
|
|
474
|
+
Object.freeze({
|
|
475
|
+
artifactRef: quarantine.artifactRef,
|
|
476
|
+
artifactDigest: quarantine.artifactDigest,
|
|
477
|
+
sizeBytes: quarantine.sizeBytes,
|
|
478
|
+
contentType: quarantine.contentType,
|
|
479
|
+
networkReceiptDigest: network.networkReceiptDigest,
|
|
480
|
+
quarantineReceiptDigest: quarantine.quarantineReceiptDigest,
|
|
481
|
+
scanEvidenceDigest: scan.scanEvidenceDigest,
|
|
482
|
+
completedAt,
|
|
483
|
+
}),
|
|
484
|
+
]),
|
|
485
|
+
quarantine,
|
|
486
|
+
scan,
|
|
487
|
+
);
|
|
488
|
+
deadlineGuard(execution, signal, now);
|
|
489
|
+
return Object.freeze({
|
|
490
|
+
schema: ARTIFACT_SCHEMA,
|
|
491
|
+
artifactRef: quarantine.artifactRef,
|
|
492
|
+
artifactDigest: quarantine.artifactDigest,
|
|
493
|
+
sizeBytes: quarantine.sizeBytes,
|
|
494
|
+
contentType: quarantine.contentType,
|
|
495
|
+
finalUrl: network.finalUrl,
|
|
496
|
+
redirectOrigins: network.redirectOrigins,
|
|
497
|
+
quarantined: true,
|
|
498
|
+
scanVerdict: "clean",
|
|
499
|
+
scanEvidenceDigest: scan.scanEvidenceDigest,
|
|
500
|
+
quarantineReceiptDigest: quarantine.quarantineReceiptDigest,
|
|
501
|
+
completionReceiptDigest: completion.completionReceiptDigest,
|
|
502
|
+
completedAt,
|
|
503
|
+
});
|
|
504
|
+
} catch (error) {
|
|
505
|
+
if (session) {
|
|
506
|
+
try {
|
|
507
|
+
await Reflect.apply(session.discardArtifact, undefined, [
|
|
508
|
+
Object.freeze({ reason: "download-execution-failed" }),
|
|
509
|
+
]);
|
|
510
|
+
} catch {
|
|
511
|
+
throw new Error("download quarantine cleanup failed", {
|
|
512
|
+
cause: error,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
throw error;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
executors.add(executeDownload);
|
|
520
|
+
return executeDownload;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export function isBrowserQuarantinedDownloadExecutor(value) {
|
|
524
|
+
return executors.has(value);
|
|
525
|
+
}
|