agentic-qe 3.14.2 → 3.14.3
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/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +72 -0
- package/assets/skills/skills-manifest.json +1 -1
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-AKQYU6UH.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-SD5N32MX.js → agent-booster-wasm-TQOMLWWX.js} +2 -2
- package/dist/cli/chunks/{agent-handler-OZRSJM7F.js → agent-handler-Q3SEMMI2.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-NYTRANRN.js → agent-memory-branch-SJXAQSZI.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-MICRPL3N.js +2 -0
- package/dist/cli/chunks/{arena-EX3SC2DP.js → arena-7RC7TBGW.js} +4 -4
- package/dist/cli/chunks/{arena-FOTVSRK4.js → arena-UPB7TKUY.js} +2 -2
- package/dist/cli/chunks/{audit-Q5GHSUQY.js → audit-IXN5R4WB.js} +3 -3
- package/dist/cli/chunks/base-DZK36ITB.js +2 -0
- package/dist/cli/chunks/{better-sqlite3-S7M5R2HS.js → better-sqlite3-RP4DEGK3.js} +2 -2
- package/dist/cli/chunks/{brain-handler-5FSA46R5.js → brain-handler-H5OVZ22L.js} +3 -3
- package/dist/cli/chunks/{branch-enumerator-RWAXTGEU.js → branch-enumerator-VPRWDGFJ.js} +2 -2
- package/dist/cli/chunks/{browser-IGDESJGF.js → browser-B6E7JPPJ.js} +2 -2
- package/dist/cli/chunks/browser-workflow-ADK54DOE.js +2 -0
- package/dist/cli/chunks/{chunk-E3Q7WNJD.js → chunk-26GM5CWD.js} +3 -3
- package/dist/cli/chunks/{chunk-BWRHOIYC.js → chunk-2GNRW3OD.js} +2 -2
- package/dist/cli/chunks/{chunk-ZCDXJ27H.js → chunk-2IGT5WNQ.js} +1 -1
- package/dist/cli/chunks/{chunk-7SK6IMGZ.js → chunk-2WT7AKKK.js} +2 -2
- package/dist/cli/chunks/{chunk-DVHWLAEO.js → chunk-33VPWGCM.js} +3 -3
- package/dist/cli/chunks/{chunk-NAZVPZJS.js → chunk-37BASGWC.js} +2 -2
- package/dist/cli/chunks/{chunk-Q335R4MR.js → chunk-3ABRGG3C.js} +2 -2
- package/dist/cli/chunks/{chunk-IWFL6EFI.js → chunk-3P6NCFI2.js} +2 -2
- package/dist/cli/chunks/{chunk-4AKGBUYD.js → chunk-42VSQ2MX.js} +3 -3
- package/dist/cli/chunks/{chunk-2LKAMMMI.js → chunk-44WISFTU.js} +2 -2
- package/dist/cli/chunks/{chunk-RFNEHNJV.js → chunk-4C6UJIHB.js} +1 -1
- package/dist/cli/chunks/{chunk-CTCQOYBS.js → chunk-4JBFJTHJ.js} +1 -1
- package/dist/cli/chunks/{chunk-GJAWEUIN.js → chunk-4OUPCTBS.js} +2 -2
- package/dist/cli/chunks/{chunk-2EHFEMRU.js → chunk-4Q35TI6Z.js} +2 -2
- package/dist/cli/chunks/{chunk-N3RN4QTC.js → chunk-4RP5VUBI.js} +2 -2
- package/dist/cli/chunks/{chunk-ZYLSOBOR.js → chunk-4TXAP3YH.js} +1 -1
- package/dist/cli/chunks/{chunk-KZOBLLXW.js → chunk-4UDZLD42.js} +2 -2
- package/dist/cli/chunks/{chunk-UYTWDS2R.js → chunk-4W7QVUNO.js} +2 -2
- package/dist/cli/chunks/{chunk-4ZUZDFAA.js → chunk-4XPPMADQ.js} +2 -2
- package/dist/cli/chunks/{chunk-TENYL5TI.js → chunk-527HQAY7.js} +2 -2
- package/dist/cli/chunks/chunk-5BLFXQEB.js +96 -0
- package/dist/cli/chunks/{chunk-OX6T26CD.js → chunk-5DFHE4H5.js} +3 -3
- package/dist/cli/chunks/{chunk-T46LOLW3.js → chunk-5F6DWHQG.js} +2 -2
- package/dist/cli/chunks/{chunk-FVGTINVR.js → chunk-5FOZO7KM.js} +81 -65
- package/dist/cli/chunks/{chunk-T4C56HBW.js → chunk-5IAB5NRZ.js} +2 -2
- package/dist/cli/chunks/{chunk-4MH22236.js → chunk-5P3G6ZW4.js} +1 -1
- package/dist/cli/chunks/{chunk-7AFBNU6K.js → chunk-5Y5TC6BY.js} +2 -2
- package/dist/cli/chunks/{chunk-VSYBMNIR.js → chunk-6F2RM6X2.js} +1 -1
- package/dist/cli/chunks/{chunk-ICKLBATY.js → chunk-72UJXEKQ.js} +2 -2
- package/dist/cli/chunks/{chunk-A7N3YUDJ.js → chunk-74PK3NEW.js} +2 -2
- package/dist/cli/chunks/{chunk-PMUBG2RU.js → chunk-7BUERZYL.js} +2 -2
- package/dist/cli/chunks/{chunk-HZMLNM2T.js → chunk-7FYMDKQH.js} +174 -169
- package/dist/cli/chunks/{chunk-67A6PEZM.js → chunk-7G6UX2QN.js} +2 -2
- package/dist/cli/chunks/{chunk-G2CLZVYI.js → chunk-7JQHPANT.js} +3 -3
- package/dist/cli/chunks/{chunk-TLPL3EQY.js → chunk-7SLAZAVU.js} +2 -2
- package/dist/cli/chunks/{chunk-4UCMTYFQ.js → chunk-7Y2CEXRS.js} +4 -4
- package/dist/cli/chunks/{chunk-2H5YJ5ZK.js → chunk-AB4U6ZNI.js} +2 -2
- package/dist/cli/chunks/chunk-AWNYEAGB.js +2 -0
- package/dist/cli/chunks/chunk-B2ZONZPZ.js +5 -0
- package/dist/cli/chunks/{chunk-6F4WHN7D.js → chunk-B5XFDFBT.js} +1 -1
- package/dist/cli/chunks/{chunk-5EHLGOWW.js → chunk-B7SWW5A6.js} +2 -2
- package/dist/cli/chunks/{chunk-P64DMQ2C.js → chunk-BN7DKW5D.js} +1 -1
- package/dist/cli/chunks/{chunk-CIESXO73.js → chunk-C46DMCWF.js} +1 -1
- package/dist/cli/chunks/{chunk-HZ6NEJMO.js → chunk-CB5JPRIZ.js} +2 -2
- package/dist/cli/chunks/{chunk-SEHCX4NH.js → chunk-CBOPBYPU.js} +1 -1
- package/dist/cli/chunks/{chunk-LWUJQGCS.js → chunk-CR3QGHVN.js} +2 -2
- package/dist/cli/chunks/{chunk-NHHWLJQF.js → chunk-D4Z2N74A.js} +1 -1
- package/dist/cli/chunks/{chunk-4K27ESCA.js → chunk-DBA3N742.js} +1 -1
- package/dist/cli/chunks/{chunk-2TG24LYQ.js → chunk-DX3C5HPN.js} +1 -1
- package/dist/cli/chunks/{chunk-S4MR6RTE.js → chunk-E3FBW6G2.js} +1 -1
- package/dist/cli/chunks/{chunk-GHUPID5E.js → chunk-ELU4IC3I.js} +1 -1
- package/dist/cli/chunks/{chunk-C7AXRI7E.js → chunk-EY4OQHU3.js} +1 -1
- package/dist/cli/chunks/{chunk-6LMLN5R2.js → chunk-F67AV5P2.js} +2 -2
- package/dist/cli/chunks/chunk-FD2JFEQC.js +2 -0
- package/dist/cli/chunks/chunk-FKG5OQFT.js +2 -0
- package/dist/cli/chunks/{chunk-UIGEUEBT.js → chunk-FUE6LL72.js} +2 -2
- package/dist/cli/chunks/{chunk-4RT7I7PI.js → chunk-G5C2XVZC.js} +1 -1
- package/dist/cli/chunks/{chunk-WVY22NHZ.js → chunk-G6DCESPU.js} +2 -2
- package/dist/cli/chunks/{chunk-3DFFFXXO.js → chunk-G6ZHTF42.js} +2 -2
- package/dist/cli/chunks/{chunk-CH2TQCV5.js → chunk-GNTVFN7D.js} +1 -1
- package/dist/cli/chunks/{chunk-WMW47QMR.js → chunk-H5ZRV5K5.js} +2 -2
- package/dist/cli/chunks/{chunk-4M6A3PVJ.js → chunk-H6XBM2LY.js} +2 -2
- package/dist/cli/chunks/{chunk-4N44UJGI.js → chunk-HOQQCY2K.js} +2 -2
- package/dist/cli/chunks/{chunk-D4RCV4LY.js → chunk-HRUU7E3D.js} +2 -2
- package/dist/cli/chunks/{chunk-EAJK25IS.js → chunk-HXE5NBJG.js} +1 -1
- package/dist/cli/chunks/{chunk-IUNMW454.js → chunk-I4NDQGKP.js} +2 -2
- package/dist/cli/chunks/{chunk-CJHVOBJY.js → chunk-IFFZR6GY.js} +1 -1
- package/dist/cli/chunks/{chunk-YN6W2XIT.js → chunk-IKIITGU5.js} +3 -3
- package/dist/cli/chunks/{chunk-JIUF6NV6.js → chunk-IP7NSFQQ.js} +2 -2
- package/dist/cli/chunks/{chunk-3C2K4YMS.js → chunk-IQEJGZV6.js} +3 -3
- package/dist/cli/chunks/{chunk-RGH3WGWD.js → chunk-IU4EBTA2.js} +3 -3
- package/dist/cli/chunks/{chunk-QW4LN5NK.js → chunk-JBB7E26R.js} +2 -2
- package/dist/cli/chunks/{chunk-RYJHREYS.js → chunk-JRYZKBY2.js} +2 -2
- package/dist/cli/chunks/{chunk-6EHN7Q2Q.js → chunk-JV4FZUE7.js} +2 -2
- package/dist/cli/chunks/{chunk-PZKZY6PB.js → chunk-KF63AYXD.js} +2 -2
- package/dist/cli/chunks/{chunk-ZFGND4QK.js → chunk-KPMGTVB4.js} +1 -1
- package/dist/cli/chunks/chunk-L2GKMBDP.js +2 -0
- package/dist/cli/chunks/{chunk-7FWP7CZY.js → chunk-LHGNNZ76.js} +3 -3
- package/dist/cli/chunks/{chunk-HSJ5FQCF.js → chunk-LKN7JQTW.js} +2 -2
- package/dist/cli/chunks/{chunk-EH5OCHAJ.js → chunk-LLVGTEHU.js} +1 -1
- package/dist/cli/chunks/{chunk-TYVQNYXX.js → chunk-LS3EUMBP.js} +6 -6
- package/dist/cli/chunks/{chunk-F2KRT2II.js → chunk-LTBCAFTZ.js} +3 -3
- package/dist/cli/chunks/{chunk-PVHO6KLB.js → chunk-LVLBIKJD.js} +1 -1
- package/dist/cli/chunks/{chunk-5WJUNRTY.js → chunk-MDZ6RRLX.js} +2 -2
- package/dist/cli/chunks/{chunk-W4OQYLYO.js → chunk-MWWAMDHI.js} +1 -1
- package/dist/cli/chunks/{chunk-N5ZRUOMR.js → chunk-MXMFGSWR.js} +2 -2
- package/dist/cli/chunks/{chunk-L2F7FVIU.js → chunk-N4H2QOT5.js} +2 -2
- package/dist/cli/chunks/{chunk-R5RNOLHI.js → chunk-NBWQBQO5.js} +1 -1
- package/dist/cli/chunks/{chunk-IVGVOGIZ.js → chunk-NN76NTOX.js} +1 -1
- package/dist/cli/chunks/{chunk-OBCKP3G4.js → chunk-NTSXHZTC.js} +2 -2
- package/dist/cli/chunks/{chunk-YF5OP6CR.js → chunk-OV5EZO3U.js} +2 -2
- package/dist/cli/chunks/{chunk-JQ2ZD3WO.js → chunk-OVDKWCD7.js} +2 -2
- package/dist/cli/chunks/{chunk-WBH4C4BG.js → chunk-OVPJQWHW.js} +2 -2
- package/dist/cli/chunks/{chunk-6SZKLWZT.js → chunk-OY7MLMEX.js} +5 -5
- package/dist/cli/chunks/{chunk-NG2UVPTW.js → chunk-OYMLHX2B.js} +1 -1
- package/dist/cli/chunks/{chunk-RVCWSAXI.js → chunk-PI74GOE4.js} +2 -2
- package/dist/cli/chunks/{chunk-42WUDDUU.js → chunk-PZTVH2AS.js} +2 -2
- package/dist/cli/chunks/{chunk-4PH7O5ZH.js → chunk-QLWNXBRU.js} +2 -2
- package/dist/cli/chunks/{chunk-FPHPP5TQ.js → chunk-R2AFVFRF.js} +1 -1
- package/dist/cli/chunks/{chunk-MWQTACNA.js → chunk-RKJZWWB7.js} +2 -2
- package/dist/cli/chunks/{chunk-FREF6Y4L.js → chunk-ROX7NLS4.js} +2 -2
- package/dist/cli/chunks/{chunk-K574FLVB.js → chunk-RTJ2M6V6.js} +2 -2
- package/dist/cli/chunks/{chunk-NWFIJNEB.js → chunk-SG47AXJG.js} +1 -1
- package/dist/cli/chunks/{chunk-UHTAI4ZB.js → chunk-SGXSB7YO.js} +1 -1
- package/dist/cli/chunks/{chunk-PWEIDHT7.js → chunk-SHGKY3QB.js} +2 -2
- package/dist/cli/chunks/{chunk-MH73D5TN.js → chunk-SJPZSCZP.js} +2 -2
- package/dist/cli/chunks/{chunk-3WV4H33V.js → chunk-SQ3PPISY.js} +1 -1
- package/dist/cli/chunks/{chunk-RCI64RIJ.js → chunk-STRDSVH3.js} +2 -2
- package/dist/cli/chunks/{chunk-GHQ5PUOV.js → chunk-T35GZXTU.js} +2 -2
- package/dist/cli/chunks/{chunk-DRO5NJZA.js → chunk-T62DH4GG.js} +2 -2
- package/dist/cli/chunks/{chunk-W2FLULF4.js → chunk-TFHQE6NK.js} +2 -2
- package/dist/cli/chunks/{chunk-LML5VTFK.js → chunk-TGLAWI62.js} +1 -1
- package/dist/cli/chunks/{chunk-KFXZDQ5J.js → chunk-TISSZFHO.js} +1 -1
- package/dist/cli/chunks/{chunk-KPFG4OIM.js → chunk-TJEIOE7F.js} +1 -1
- package/dist/cli/chunks/{chunk-57TNBR2P.js → chunk-TTMHIOQ3.js} +2 -2
- package/dist/cli/chunks/{chunk-ARMZYHYL.js → chunk-UHQY3GK7.js} +2 -2
- package/dist/cli/chunks/{chunk-ADK7DF3G.js → chunk-UMENRGHK.js} +2 -2
- package/dist/cli/chunks/{chunk-ZWRQ5VAU.js → chunk-UMNYF5EZ.js} +2 -2
- package/dist/cli/chunks/{chunk-G6AV2HGY.js → chunk-UTVRQEZM.js} +3 -3
- package/dist/cli/chunks/{chunk-VOYJJ3KA.js → chunk-UYJHYMEQ.js} +2 -2
- package/dist/cli/chunks/{chunk-KEEBUVGV.js → chunk-V4AOVDCJ.js} +2 -2
- package/dist/cli/chunks/{chunk-HBTC4L53.js → chunk-VIDBBB6F.js} +2 -2
- package/dist/cli/chunks/{chunk-PFXX5Q32.js → chunk-VWNHKYK7.js} +2 -2
- package/dist/cli/chunks/{chunk-DW6ZMNMR.js → chunk-WYMGK5DX.js} +2 -2
- package/dist/cli/chunks/{chunk-CJCT452S.js → chunk-XA7CG3LH.js} +2 -2
- package/dist/cli/chunks/{chunk-QFUX7ELG.js → chunk-XX7HUSX2.js} +2 -2
- package/dist/cli/chunks/{chunk-WXMSRBQF.js → chunk-XXCYQXK3.js} +2 -2
- package/dist/cli/chunks/{chunk-6VKJAMZY.js → chunk-Y2DVFVGI.js} +1 -1
- package/dist/cli/chunks/{chunk-JDXLCNKV.js → chunk-Y5SQBI2F.js} +2 -2
- package/dist/cli/chunks/{chunk-QXTW7XCS.js → chunk-YAKS3CAB.js} +2 -2
- package/dist/cli/chunks/{chunk-Q3JHS3UD.js → chunk-YCBDE2N7.js} +2 -2
- package/dist/cli/chunks/{chunk-VNAKBHJI.js → chunk-YDNSLLBS.js} +2 -2
- package/dist/cli/chunks/{chunk-S7N4BKI3.js → chunk-YEXPFHV6.js} +2 -2
- package/dist/cli/chunks/{chunk-CG2MUD7U.js → chunk-YGB6R5DW.js} +1 -1
- package/dist/cli/chunks/{chunk-NNOMDM4N.js → chunk-YL7T3VV7.js} +2 -2
- package/dist/cli/chunks/{chunk-U54OOA24.js → chunk-ZHXWGNYM.js} +1 -1
- package/dist/cli/chunks/{chunk-NRR23SJV.js → chunk-ZK5JKHP4.js} +1 -1
- package/dist/cli/chunks/{chunk-XPLSLHRB.js → chunk-ZN3J6TZD.js} +1 -1
- package/dist/cli/chunks/{chunk-RHJVDCLV.js → chunk-ZUG5MOUB.js} +2 -2
- package/dist/cli/chunks/{ci-NTRH7PAW.js → ci-YVKDQWIX.js} +2 -2
- package/dist/cli/chunks/ci-output-LK3MBXLZ.js +2 -0
- package/dist/cli/chunks/{circuit-breaker-4WST3QSA.js → circuit-breaker-7AIXACR7.js} +2 -2
- package/dist/cli/chunks/{claude-flow-setup-KB4TKMYI.js → claude-flow-setup-UA7EO6VN.js} +2 -2
- package/dist/cli/chunks/client-M72GXFVQ.js +2 -0
- package/dist/cli/chunks/{cline-installer-W2ZHQ6WM.js → cline-installer-YDDCKKEV.js} +2 -2
- package/dist/cli/chunks/{code-TF2AVR5D.js → code-URB7UAKW.js} +2 -2
- package/dist/cli/chunks/{code-index-extractor-RMDRUVQJ.js → code-index-extractor-BL6HWH4Q.js} +2 -2
- package/dist/cli/chunks/{codex-installer-VSTRHJHV.js → codex-installer-2AVEO4EA.js} +2 -2
- package/dist/cli/chunks/{completions-3Q37QVFR.js → completions-RCKBSSSS.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-3FNFLACD.js → complexity-analyzer-LEDG62YK.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-VTEXM455.js → continuedev-installer-VOTCEBFH.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-AGY7DQUS.js → copilot-installer-NVTGLZX3.js} +2 -2
- package/dist/cli/chunks/core-handlers-C32RAMVI.js +2 -0
- package/dist/cli/chunks/{cost-tracker-LTZAQBX7.js → cost-tracker-GJIFB2BM.js} +2 -2
- package/dist/cli/chunks/{coverage-TT64IOS4.js → coverage-ZTPJWXWE.js} +3 -3
- package/dist/cli/chunks/cross-domain-router-UGFUCPKC.js +2 -0
- package/dist/cli/chunks/{cursor-installer-OVOJIOHN.js → cursor-installer-CMGJQMHX.js} +2 -2
- package/dist/cli/chunks/{daemon-DMFZMXKA.js → daemon-V456IWR3.js} +3 -3
- package/dist/cli/chunks/{daemon-XCULEHWY.js → daemon-Y2I3YVIU.js} +4 -4
- package/dist/cli/chunks/{dag-attention-scheduler-WW2RX4B5.js → dag-attention-scheduler-ZCBBOD3D.js} +2 -2
- package/dist/cli/chunks/{detect-QNWIM232.js → detect-2GHEA4A3.js} +2 -2
- package/dist/cli/chunks/{dist-node-6GQIY3CG.js → dist-node-44AGYJCH.js} +2 -2
- package/dist/cli/chunks/{domain-handler-6RJPDELX.js → domain-handler-IDLIEP6X.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-WK524LBS.js → domain-transfer-KLEXGIRF.js} +2 -2
- package/dist/cli/chunks/dream-UFBBXY5W.js +2 -0
- package/dist/cli/chunks/{embed-and-insert-pattern-SO6GWJW7.js → embed-and-insert-pattern-DUW6K35A.js} +2 -2
- package/dist/cli/chunks/{eval-GL544IPO.js → eval-PR6YWDFM.js} +2 -2
- package/dist/cli/chunks/{experience-capture-middleware-BCCJ35OX.js → experience-capture-middleware-CJJJN7CF.js} +3 -3
- package/dist/cli/chunks/{fast-paths-CATDDBEU.js → fast-paths-LGLLKX6P.js} +2 -2
- package/dist/cli/chunks/{feature-flags-BUXNAORY.js → feature-flags-33D3I4DD.js} +2 -2
- package/dist/cli/chunks/{feature-flags-KNNWWP5G.js → feature-flags-QL4NCEGW.js} +2 -2
- package/dist/cli/chunks/{file-discovery-S4HF3O7D.js → file-discovery-GKCYJPFZ.js} +2 -2
- package/dist/cli/chunks/{fleet-DQVQQONW.js → fleet-QNT6X2YI.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-G7ABZUG7.js → gnn-wrapper-ELSGT67Z.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-UFMELIWT.js → heartbeat-handler-ZUI3DOOP.js} +4 -4
- package/dist/cli/chunks/heartbeat-scheduler-VQWHSGTY.js +2 -0
- package/dist/cli/chunks/hnsw-adapter-BS3NXWGU.js +2 -0
- package/dist/cli/chunks/hnsw-index-JP26B4HW.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-YIILSRXI.js → hnsw-legacy-bridge-EZMM5JWL.js} +2 -2
- package/dist/cli/chunks/{hnswlib-node-TSVRMXDH.js → hnswlib-node-G4CE3EOS.js} +2 -2
- package/dist/cli/chunks/{hooks-OHQPNK74.js → hooks-LCVXXA55.js} +10 -10
- package/dist/cli/chunks/{hypergraph-engine-LY7PJDSL.js → hypergraph-engine-6Z7RQ3HI.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-YGDYZO6H.js → hypergraph-handler-PLI2GYPK.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-WRE4CWPJ.js +2 -0
- package/dist/cli/chunks/{init-handler-MTK65LI3.js → init-handler-7VH3QJ5Y.js} +6 -6
- package/dist/cli/chunks/init-wizard-OFOEYB75.js +2 -0
- package/dist/cli/chunks/kernel-MXNFWNUD.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-3IICU4RS.js → kilocode-installer-U3MW62DE.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-MXOVQYQA.js → kiro-installer-PPEAC44T.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-7JE2TVKJ.js +2 -0
- package/dist/cli/chunks/{learning-EV5LSOOM.js → learning-7B5NITDV.js} +3 -3
- package/dist/cli/chunks/{llm-router-7LYDJUOT.js → llm-router-BNEPT5OB.js} +4 -4
- package/dist/cli/chunks/llm-router-service-GTDVDKSU.js +2 -0
- package/dist/cli/chunks/{load-F4PVAMXQ.js → load-VO7ABNHV.js} +2 -2
- package/dist/cli/chunks/load-test-JZ4CLSBF.js +2 -0
- package/dist/cli/chunks/{mcp-DUYJUEME.js → mcp-THXEINCL.js} +2 -2
- package/dist/cli/chunks/{memory-XTTYORAS.js → memory-FZTT7ADM.js} +5 -5
- package/dist/cli/chunks/memory-backend-Q54CC5A5.js +2 -0
- package/dist/cli/chunks/memory-handlers-IIBJXKHZ.js +2 -0
- package/dist/cli/chunks/{multi-model-executor-R6XCTMCF.js → multi-model-executor-HEDVOZ6X.js} +2 -2
- package/dist/cli/chunks/{opencode-installer-33ULBU2F.js → opencode-installer-GQ7J5SBG.js} +2 -2
- package/dist/cli/chunks/{orchestrator-34ZJEKTJ.js → orchestrator-66JK2OS5.js} +5 -5
- package/dist/cli/chunks/pattern-null-store-W33O7FYO.js +2 -0
- package/dist/cli/chunks/{pattern-usage-recorder-GZ2M3F2E.js → pattern-usage-recorder-O6XHM2G5.js} +2 -2
- package/dist/cli/chunks/{pipeline-SR5OTY45.js → pipeline-UOG6UO6D.js} +2 -2
- package/dist/cli/chunks/{platform-VKJFJIAW.js → platform-Q7UH7FZQ.js} +2 -2
- package/dist/cli/chunks/{plugin-5C5UBQUQ.js → plugin-7OFLNGOS.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-KO2PQI3D.js → prime-radiant-advanced-wasm-OSRDK2FW.js} +2 -2
- package/dist/cli/chunks/protocol-executor-VGN4M7XY.js +2 -0
- package/dist/cli/chunks/{protocol-handler-ZF74NIKA.js → protocol-handler-WLYTX6FS.js} +2 -2
- package/dist/cli/chunks/{prove-MO5TQQU4.js → prove-QVNE4V3J.js} +2 -2
- package/dist/cli/chunks/qe-reasoning-bank-7VB5LJGS.js +2 -0
- package/dist/cli/chunks/{quality-EYIN6AER.js → quality-DNE2RKE5.js} +2 -2
- package/dist/cli/chunks/{quality-gate-V6OQM57O.js → quality-gate-2K35GCSI.js} +3 -3
- package/dist/cli/chunks/queen-coordinator-HIDMJMMG.js +2 -0
- package/dist/cli/chunks/{real-embeddings-AR7TZX7U.js → real-embeddings-SF42ML7G.js} +2 -2
- package/dist/cli/chunks/{roocode-installer-46E626VZ.js → roocode-installer-NCEZPF5L.js} +2 -2
- package/dist/cli/chunks/router-CTIJS4D7.js +2 -0
- package/dist/cli/chunks/routing-feedback-LFYYZS5J.js +2 -0
- package/dist/cli/chunks/{routing-handler-ZNRVW6QW.js → routing-handler-4MXXZRYP.js} +2 -2
- package/dist/cli/chunks/{ruvector-commands-4L2CQMWS.js → ruvector-commands-WDFMMCMK.js} +2 -2
- package/dist/cli/chunks/rvf-dual-writer-MNPMIWVM.js +2 -0
- package/dist/cli/chunks/rvf-migration-adapter-DXOKSSNW.js +2 -0
- package/dist/cli/chunks/{rvf-migration-coordinator-H4RVGBIA.js → rvf-migration-coordinator-JT7ZCJY7.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-PZKUKA7R.js +2 -0
- package/dist/cli/chunks/{rvf-store-integrity-OJGWWPLL.js → rvf-store-integrity-Q3FGYKFT.js} +2 -2
- package/dist/cli/chunks/safe-db-73BLFV2C.js +2 -0
- package/dist/cli/chunks/schedule-YVTGTTI4.js +2 -0
- package/dist/cli/chunks/scheduler-5DSAFIRQ.js +2 -0
- package/dist/cli/chunks/security-CJJ3WJSB.js +13 -0
- package/dist/cli/chunks/{shared-rvf-adapter-M6NNIZ5W.js → shared-rvf-adapter-6RGCTLHD.js} +2 -2
- package/dist/cli/chunks/{shared-rvf-dual-writer-LHMFCYVE.js → shared-rvf-dual-writer-KIA6Q6MT.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-LRG4IPCU.js +2 -0
- package/dist/cli/chunks/{status-handler-GMIOXQIE.js → status-handler-BADQV2KA.js} +2 -2
- package/dist/cli/chunks/{structural-health-YZXHVHM6.js → structural-health-VMYEYRZG.js} +2 -2
- package/dist/cli/chunks/{sync-GMPCPV5L.js → sync-PXLROLHB.js} +2 -2
- package/dist/cli/chunks/{sync-N56UQGCS.js → sync-ULY7T5II.js} +2 -2
- package/dist/cli/chunks/{task-handler-VTNRGAXP.js → task-handler-L7E2I267.js} +2 -2
- package/dist/cli/chunks/{task-handlers-FYSDH55N.js → task-handlers-6DPKE4AK.js} +3 -3
- package/dist/cli/chunks/{test-GQNCKRSP.js → test-WCLURQUT.js} +11 -11
- package/dist/cli/chunks/test-scheduling-4CNJA6TW.js +15 -0
- package/dist/cli/chunks/{token-bootstrap-76SRNXGG.js → token-bootstrap-GTI24JAL.js} +2 -2
- package/dist/cli/chunks/{token-usage-O7CZDP5Q.js → token-usage-DRVG6PKO.js} +2 -2
- package/dist/cli/chunks/{transformers-LTU4LSSC.js → transformers-IMDDAMYQ.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-NQSQCMBM.js → tree-sitter-wasm-parser-WBZIHVFA.js} +2 -2
- package/dist/cli/chunks/{types-OP6IWE6N.js → types-TXMY5M2K.js} +2 -2
- package/dist/cli/chunks/unified-memory-VBQ572X4.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-J3L74ZRY.js +2 -0
- package/dist/cli/chunks/unified-persistence-EM2VYRTQ.js +2 -0
- package/dist/cli/chunks/{upgrade-OQWRBZ3R.js → upgrade-6ALSDWDD.js} +2 -2
- package/dist/cli/chunks/{validate-TH5SRP3E.js → validate-XF4ZG3LC.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-UITL2JUS.js → validate-swarm-P5TAFRDA.js} +2 -2
- package/dist/cli/chunks/{vibium-O6PRAG4J.js → vibium-2XDXKNKZ.js} +2 -2
- package/dist/cli/chunks/visual-security-GX4S7K6O.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-XBFBY4OF.js → web-tree-sitter-4JVRQPX5.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-DVT5NRNY.js → windsurf-installer-WYJXVDO5.js} +2 -2
- package/dist/cli/chunks/witness-chain-QGKMHCNW.js +2 -0
- package/dist/cli/chunks/{witness-chain-VRSH2F36.js → witness-chain-RPZ2SE2V.js} +2 -2
- package/dist/cli/chunks/{witness-key-manager-GFHRDKQ4.js → witness-key-manager-LXG2VBQQ.js} +2 -2
- package/dist/cli/chunks/{workflow-HAZ6YKOG.js → workflow-2OYNO7UU.js} +4 -4
- package/dist/cli/chunks/workflow-orchestrator-KWHIJMFV.js +2 -0
- package/dist/cli/chunks/{wrappers-5LLLT7GM.js → wrappers-DIJNNFU6.js} +2 -2
- package/dist/cli/commands/security.js +167 -100
- package/dist/cli/commands/test.js +1 -0
- package/dist/cli/utils/ci-output.d.ts +17 -0
- package/dist/cli/utils/ci-output.js +34 -2
- package/dist/coordination/handlers/handler-types.d.ts +3 -0
- package/dist/coordination/handlers/quality-handlers.js +10 -0
- package/dist/coordination/handlers/security-handlers.js +168 -72
- package/dist/coordination/handlers/test-execution-handlers.js +37 -43
- package/dist/coordination/protocols/security-audit.d.ts +3 -0
- package/dist/coordination/protocols/security-audit.js +61 -39
- package/dist/coordination/result-saver.js +36 -5
- package/dist/coordination/task-executor.d.ts +3 -1
- package/dist/coordination/task-executor.js +4 -0
- package/dist/domains/security-compliance/interfaces.d.ts +5 -0
- package/dist/domains/security-compliance/interfaces.js +0 -6
- package/dist/domains/security-compliance/scan-discovery.d.ts +12 -0
- package/dist/domains/security-compliance/scan-discovery.js +105 -0
- package/dist/domains/security-compliance/scan-evidence.d.ts +57 -0
- package/dist/domains/security-compliance/scan-evidence.js +2 -0
- package/dist/domains/security-compliance/services/scanners/sast-scanner.d.ts +3 -2
- package/dist/domains/security-compliance/services/scanners/sast-scanner.js +109 -65
- package/dist/domains/security-compliance/services/semgrep-integration.d.ts +9 -2
- package/dist/domains/security-compliance/services/semgrep-integration.js +68 -20
- package/dist/domains/test-execution/services/flaky-detector.js +14 -3
- package/dist/domains/test-execution/services/retry-handler.js +18 -8
- package/dist/domains/test-execution/services/test-executor.js +46 -12
- package/dist/init/init-wizard-hooks.js +2 -2
- package/dist/integrations/agentic-flow/reasoning-bank/index.js +3 -20
- package/dist/integrations/coherence/engines/cohomology-adapter.js +100 -30
- package/dist/mcp/bundle.js +499 -478
- package/dist/mcp/handlers/domain-handler-configs.d.ts +22 -1
- package/dist/mcp/handlers/domain-handler-configs.js +50 -19
- package/dist/mcp/handlers/domain-handlers.d.ts +7 -5
- package/dist/mcp/handlers/domain-handlers.js +7 -5
- package/dist/mcp/protocol-server.js +9 -5
- package/dist/mcp/types.d.ts +5 -0
- package/dist/shared/llm/router/hybrid-router.d.ts +4 -0
- package/dist/shared/llm/router/hybrid-router.js +74 -26
- package/dist/shared/llm/router/types.d.ts +9 -2
- package/dist/shared/test-runner-verdict.d.ts +29 -0
- package/dist/shared/test-runner-verdict.js +43 -0
- package/dist/shared/vitest-json-report.d.ts +45 -0
- package/dist/shared/vitest-json-report.js +71 -0
- package/dist/test-scheduling/executors/vitest-executor.js +18 -6
- package/dist/validation/frontier-judge.d.ts +7 -4
- package/dist/validation/frontier-judge.js +11 -6
- package/package.json +6 -6
- package/dist/cli/chunks/adapter-OJF6LAJV.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-FGT474XB.js +0 -2
- package/dist/cli/chunks/base-HH765JFV.js +0 -2
- package/dist/cli/chunks/browser-workflow-NPM4FUPI.js +0 -2
- package/dist/cli/chunks/chunk-OC2CKU7I.js +0 -2
- package/dist/cli/chunks/chunk-PHHXHG2W.js +0 -2
- package/dist/cli/chunks/chunk-U2AY57XZ.js +0 -5
- package/dist/cli/chunks/chunk-WBMRADQP.js +0 -81
- package/dist/cli/chunks/ci-output-F57RSD35.js +0 -2
- package/dist/cli/chunks/client-GPZEC54E.js +0 -2
- package/dist/cli/chunks/core-handlers-YMGHYIBR.js +0 -2
- package/dist/cli/chunks/cross-domain-router-ECBNTGYJ.js +0 -2
- package/dist/cli/chunks/dream-EHGKCFWH.js +0 -2
- package/dist/cli/chunks/heartbeat-scheduler-UFEL2CBW.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-BLSD45CX.js +0 -2
- package/dist/cli/chunks/hnsw-index-A3RZODEN.js +0 -2
- package/dist/cli/chunks/impact-analyzer-OXHD3FOH.js +0 -2
- package/dist/cli/chunks/init-wizard-PKZL7QBU.js +0 -2
- package/dist/cli/chunks/kernel-XWPM72UJ.js +0 -2
- package/dist/cli/chunks/knowledge-graph-FRKR5XWI.js +0 -2
- package/dist/cli/chunks/llm-router-service-R7A5HHDM.js +0 -2
- package/dist/cli/chunks/load-test-5IKDWSYL.js +0 -2
- package/dist/cli/chunks/memory-backend-7VW5QCMO.js +0 -2
- package/dist/cli/chunks/memory-handlers-VZH3R2TQ.js +0 -2
- package/dist/cli/chunks/pattern-null-store-BU6LCB2W.js +0 -2
- package/dist/cli/chunks/protocol-executor-BSP5LWJN.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-J2RC7P25.js +0 -2
- package/dist/cli/chunks/queen-coordinator-XI5AJV2V.js +0 -2
- package/dist/cli/chunks/router-VXQYYA3D.js +0 -2
- package/dist/cli/chunks/routing-feedback-HZQTASFF.js +0 -2
- package/dist/cli/chunks/rvf-dual-writer-BDTLLJER.js +0 -2
- package/dist/cli/chunks/rvf-migration-adapter-IB343MDN.js +0 -2
- package/dist/cli/chunks/rvf-native-adapter-Y7SN7LHU.js +0 -2
- package/dist/cli/chunks/safe-db-2VTKKBJU.js +0 -2
- package/dist/cli/chunks/schedule-QJ4CFGJY.js +0 -2
- package/dist/cli/chunks/scheduler-2NHDQPC3.js +0 -2
- package/dist/cli/chunks/security-JF3BVNRO.js +0 -14
- package/dist/cli/chunks/sqlite-persistence-NVVAPZN3.js +0 -2
- package/dist/cli/chunks/test-scheduling-GPRXSIRP.js +0 -15
- package/dist/cli/chunks/unified-memory-DSTIEDSH.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-KBV3P4OS.js +0 -2
- package/dist/cli/chunks/unified-persistence-XKCOUBKY.js +0 -2
- package/dist/cli/chunks/visual-security-TTWSPZTZ.js +0 -2
- package/dist/cli/chunks/witness-chain-2KY73HPN.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-UK56AVOT.js +0 -2
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
* Agentic QE v3 - SAST (Static Application Security Testing) Scanner
|
|
3
3
|
* Performs static code analysis to detect security vulnerabilities
|
|
4
4
|
*/
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import { FilePath } from '../../../../shared/value-objects/index.js';
|
|
5
8
|
import { LoggerFactory } from '../../../../logging/index.js';
|
|
6
9
|
import { v4 as uuidv4 } from 'uuid';
|
|
7
10
|
import { ok, err } from '../../../../shared/types/index.js';
|
|
@@ -48,30 +51,61 @@ export class SASTScanner {
|
|
|
48
51
|
if (files.length === 0) {
|
|
49
52
|
return err(new Error('No files provided for scanning'));
|
|
50
53
|
}
|
|
51
|
-
this.activeScans.set(scanId, 'running');
|
|
52
54
|
const startTime = Date.now();
|
|
53
55
|
// Get applicable rule sets
|
|
54
56
|
const ruleSets = BUILT_IN_RULE_SETS.filter((rs) => ruleSetIds.includes(rs.id));
|
|
55
57
|
if (ruleSets.length === 0) {
|
|
56
58
|
return err(new Error(`No valid rule sets found: ${ruleSetIds.join(', ')}`));
|
|
57
59
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
const unknownRuleSets = ruleSetIds.filter(id => !BUILT_IN_RULE_SETS.some(ruleSet => ruleSet.id === id));
|
|
61
|
+
if (unknownRuleSets.length > 0) {
|
|
62
|
+
return err(new Error('Unknown rule sets requested; no scan executed.'));
|
|
63
|
+
}
|
|
64
|
+
this.activeScans.set(scanId, 'running');
|
|
65
|
+
// Deduplicate lexical absolute paths; do not follow symlinks to invent identity.
|
|
66
|
+
const requestedPaths = [...new Set(files.map(file => path.resolve(file.value)))];
|
|
67
|
+
const uniqueFiles = requestedPaths.map(value => FilePath.create(value));
|
|
68
|
+
// Run independent engines while retaining each engine's execution outcome.
|
|
69
|
+
const [patternResult, semgrepResult] = await Promise.all([
|
|
70
|
+
this.runPatternScanning(uniqueFiles, ruleSets),
|
|
71
|
+
this.runSemgrepScanning(uniqueFiles, ruleSetIds),
|
|
62
72
|
]);
|
|
63
73
|
// Merge pattern-based and semgrep findings, deduplicating by file+line
|
|
64
|
-
const vulnerabilities = this.mergeVulnerabilities(patternResult.vulnerabilities,
|
|
65
|
-
const
|
|
74
|
+
const vulnerabilities = this.mergeVulnerabilities(patternResult.vulnerabilities, semgrepResult.vulnerabilities);
|
|
75
|
+
const analyzedFiles = patternResult.files.filter(file => file.status === 'analyzed');
|
|
76
|
+
const linesScanned = analyzedFiles.reduce((total, file) => total + file.analyzedLines, 0);
|
|
66
77
|
const scanDurationMs = Date.now() - startTime;
|
|
67
78
|
// Calculate summary
|
|
68
|
-
const summary = this.calculateSummary(vulnerabilities,
|
|
69
|
-
|
|
70
|
-
const
|
|
79
|
+
const summary = this.calculateSummary(vulnerabilities, analyzedFiles.length, scanDurationMs);
|
|
80
|
+
const patterns = this.getApplicablePatterns(ruleSets);
|
|
81
|
+
const ruleIds = analyzedFiles.length > 0 ? [...new Set(patterns.map(pattern => pattern.id))] : [];
|
|
71
82
|
const coverage = {
|
|
72
|
-
filesScanned:
|
|
83
|
+
filesScanned: analyzedFiles.length,
|
|
73
84
|
linesScanned,
|
|
74
|
-
rulesApplied:
|
|
85
|
+
rulesApplied: ruleIds.length,
|
|
86
|
+
rulesAppliedScope: 'built-in-patterns',
|
|
87
|
+
};
|
|
88
|
+
const patternEngine = {
|
|
89
|
+
id: 'patterns', requested: true, required: true, scope: 'requested-files',
|
|
90
|
+
status: analyzedFiles.length === uniqueFiles.length ? 'completed' : analyzedFiles.length > 0 ? 'partial' : 'failed',
|
|
91
|
+
analyzedFiles: analyzedFiles.length,
|
|
92
|
+
ruleIds,
|
|
93
|
+
rulesetDigest: createHash('sha256').update(JSON.stringify(patterns.map(pattern => ({
|
|
94
|
+
id: pattern.id, category: pattern.category, expression: pattern.pattern.source, flags: pattern.pattern.flags,
|
|
95
|
+
})))).digest('hex'),
|
|
96
|
+
ruleCoverage: 'known',
|
|
97
|
+
errors: patternResult.files.filter(file => file.status === 'unreadable').map(file => file.reason || 'File unreadable'),
|
|
98
|
+
limitations: ['Built-in pattern matching covers supported JavaScript and TypeScript inputs only.'],
|
|
99
|
+
};
|
|
100
|
+
const evidence = {
|
|
101
|
+
schemaVersion: 1,
|
|
102
|
+
completeness: analyzedFiles.length === uniqueFiles.length ? 'complete' : analyzedFiles.length > 0 ? 'partial' : 'none',
|
|
103
|
+
requestedFiles: uniqueFiles.length,
|
|
104
|
+
requestedPaths,
|
|
105
|
+
duplicateInputs: files.length - uniqueFiles.length,
|
|
106
|
+
files: patternResult.files,
|
|
107
|
+
engines: [patternEngine, semgrepResult.evidence],
|
|
108
|
+
limitations: [...patternEngine.limitations, ...semgrepResult.evidence.limitations],
|
|
75
109
|
};
|
|
76
110
|
// Store scan results in memory
|
|
77
111
|
await this.storeScanResults(scanId, 'sast', vulnerabilities, summary);
|
|
@@ -81,6 +115,7 @@ export class SASTScanner {
|
|
|
81
115
|
vulnerabilities,
|
|
82
116
|
summary,
|
|
83
117
|
coverage,
|
|
118
|
+
evidence,
|
|
84
119
|
});
|
|
85
120
|
}
|
|
86
121
|
catch (error) {
|
|
@@ -93,36 +128,48 @@ export class SASTScanner {
|
|
|
93
128
|
*/
|
|
94
129
|
async runPatternScanning(files, ruleSets) {
|
|
95
130
|
const vulnerabilities = [];
|
|
96
|
-
|
|
131
|
+
const receipts = [];
|
|
97
132
|
for (const file of files) {
|
|
98
|
-
const
|
|
99
|
-
vulnerabilities.push(...
|
|
100
|
-
|
|
133
|
+
const result = await this.analyzeFile(file, ruleSets);
|
|
134
|
+
vulnerabilities.push(...result.vulnerabilities);
|
|
135
|
+
receipts.push(result.evidence);
|
|
101
136
|
}
|
|
102
|
-
return { vulnerabilities,
|
|
137
|
+
return { vulnerabilities, files: receipts };
|
|
103
138
|
}
|
|
104
139
|
/**
|
|
105
140
|
* Run semgrep scanning when enabled and available.
|
|
106
|
-
*
|
|
141
|
+
* Optional engine failure preserves completed pattern analysis and its findings.
|
|
107
142
|
*/
|
|
108
143
|
async runSemgrepScanning(files, ruleSetIds) {
|
|
144
|
+
const targetDir = this.resolveTargetDirectory(files);
|
|
145
|
+
const base = {
|
|
146
|
+
id: 'semgrep', requested: !!this.config.enableSemgrep, required: false,
|
|
147
|
+
status: 'disabled', scope: 'parent-directory', target: targetDir,
|
|
148
|
+
ruleCoverage: 'unknown', errors: [], limitations: [],
|
|
149
|
+
};
|
|
109
150
|
if (!this.config.enableSemgrep) {
|
|
110
|
-
return [];
|
|
151
|
+
return { vulnerabilities: [], evidence: base };
|
|
111
152
|
}
|
|
153
|
+
const limitations = [
|
|
154
|
+
'Optional Semgrep scans a parent directory; requested-file membership and executed-rule coverage are unverified.',
|
|
155
|
+
];
|
|
112
156
|
try {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
157
|
+
if (!await isSemgrepAvailable()) {
|
|
158
|
+
return { vulnerabilities: [], evidence: { ...base, status: 'unavailable', limitations,
|
|
159
|
+
errors: ['Semgrep is unavailable.'] } };
|
|
116
160
|
}
|
|
117
|
-
// Determine target directory from files (use common parent)
|
|
118
|
-
const targetDir = this.resolveTargetDirectory(files);
|
|
119
161
|
const semgrepResult = await runSemgrepWithRules(targetDir, ruleSetIds);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
162
|
+
const evidence = {
|
|
163
|
+
...base,
|
|
164
|
+
status: semgrepResult.status || 'unverified',
|
|
165
|
+
version: semgrepResult.version,
|
|
166
|
+
errors: semgrepResult.errors.length > 0 ? [`Semgrep reported ${semgrepResult.errors.length} execution error(s).`] : [],
|
|
167
|
+
limitations: [...limitations, ...(semgrepResult.diagnostics ?? []),
|
|
168
|
+
...(!semgrepResult.status ? ['Legacy Semgrep adapter returned no execution disposition.'] : [])],
|
|
169
|
+
};
|
|
123
170
|
// Convert semgrep findings to our Vulnerability format
|
|
124
171
|
const converted = convertSemgrepFindings(semgrepResult.findings);
|
|
125
|
-
|
|
172
|
+
const vulnerabilities = converted.map(f => ({
|
|
126
173
|
id: uuidv4(),
|
|
127
174
|
cveId: undefined,
|
|
128
175
|
title: f.title,
|
|
@@ -142,10 +189,11 @@ export class SASTScanner {
|
|
|
142
189
|
},
|
|
143
190
|
references: f.references,
|
|
144
191
|
}));
|
|
192
|
+
return { vulnerabilities, evidence };
|
|
145
193
|
}
|
|
146
194
|
catch {
|
|
147
|
-
|
|
148
|
-
|
|
195
|
+
return { vulnerabilities: [], evidence: { ...base, status: 'failed', limitations,
|
|
196
|
+
errors: ['Semgrep execution failed.'] } };
|
|
149
197
|
}
|
|
150
198
|
}
|
|
151
199
|
/**
|
|
@@ -153,25 +201,18 @@ export class SASTScanner {
|
|
|
153
201
|
*/
|
|
154
202
|
resolveTargetDirectory(files) {
|
|
155
203
|
if (files.length === 0)
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
let j = 0;
|
|
167
|
-
while (j < maxLen && first[j] === dir[j])
|
|
168
|
-
j++;
|
|
169
|
-
commonLen = j;
|
|
204
|
+
return process.cwd();
|
|
205
|
+
let common = path.dirname(path.resolve(files[0].value));
|
|
206
|
+
for (const file of files.slice(1)) {
|
|
207
|
+
const directory = path.dirname(path.resolve(file.value));
|
|
208
|
+
while (directory !== common && !directory.startsWith(common.endsWith(path.sep) ? common : common + path.sep)) {
|
|
209
|
+
const parent = path.dirname(common);
|
|
210
|
+
if (parent === common)
|
|
211
|
+
break;
|
|
212
|
+
common = parent;
|
|
213
|
+
}
|
|
170
214
|
}
|
|
171
|
-
|
|
172
|
-
// Trim to last path separator
|
|
173
|
-
const lastSep = common.lastIndexOf('/');
|
|
174
|
-
return lastSep > 0 ? common.substring(0, lastSep) : common || '.';
|
|
215
|
+
return common;
|
|
175
216
|
}
|
|
176
217
|
/**
|
|
177
218
|
* Map semgrep OWASP category string to VulnerabilityCategory
|
|
@@ -250,32 +291,35 @@ export class SASTScanner {
|
|
|
250
291
|
/**
|
|
251
292
|
* Analyze a file for security vulnerabilities using pattern-based detection
|
|
252
293
|
*/
|
|
294
|
+
getApplicablePatterns(ruleSets) {
|
|
295
|
+
const categories = new Set(ruleSets.flatMap(ruleSet => ruleSet.categories));
|
|
296
|
+
return ALL_SECURITY_PATTERNS.filter(pattern => categories.has(pattern.category));
|
|
297
|
+
}
|
|
253
298
|
async analyzeFile(file, ruleSets) {
|
|
254
299
|
const vulnerabilities = [];
|
|
255
300
|
const filePath = file.value;
|
|
256
|
-
const
|
|
257
|
-
// Read file content
|
|
301
|
+
const receipt = { path: filePath, engineId: 'patterns', readLines: 0, analyzedLines: 0 };
|
|
258
302
|
let content;
|
|
259
|
-
let
|
|
303
|
+
let sourceDigest;
|
|
260
304
|
try {
|
|
261
305
|
const fs = await import('fs/promises');
|
|
262
|
-
|
|
263
|
-
|
|
306
|
+
const bytes = await fs.readFile(filePath);
|
|
307
|
+
sourceDigest = createHash('sha256').update(bytes).digest('hex');
|
|
308
|
+
content = bytes.toString('utf8');
|
|
264
309
|
}
|
|
265
|
-
catch {
|
|
266
|
-
|
|
267
|
-
return { vulnerabilities: [],
|
|
310
|
+
catch (error) {
|
|
311
|
+
const code = error.code;
|
|
312
|
+
return { vulnerabilities: [], evidence: { ...receipt, status: 'unreadable',
|
|
313
|
+
reason: typeof code === 'string' && /^[A-Z0-9_]{1,32}$/.test(code) ? code : 'File unreadable' } };
|
|
268
314
|
}
|
|
269
|
-
const
|
|
270
|
-
|
|
315
|
+
const lines = content.split('\n');
|
|
316
|
+
const readReceipt = { ...receipt, sourceDigest, readLines: lines.length };
|
|
271
317
|
const supportedExtensions = ['ts', 'tsx', 'js', 'jsx', 'mjs', 'cjs'];
|
|
272
|
-
if (!supportedExtensions.includes(extension)) {
|
|
273
|
-
return { vulnerabilities: [],
|
|
318
|
+
if (!supportedExtensions.includes(file.extension)) {
|
|
319
|
+
return { vulnerabilities: [], evidence: { ...readReceipt, status: 'unsupported',
|
|
320
|
+
reason: 'Unsupported language for built-in SAST patterns' } };
|
|
274
321
|
}
|
|
275
|
-
|
|
276
|
-
const applicableCategories = new Set(ruleSets.flatMap((rs) => rs.categories));
|
|
277
|
-
// Filter patterns to only those matching applicable categories
|
|
278
|
-
const applicablePatterns = ALL_SECURITY_PATTERNS.filter((pattern) => applicableCategories.has(pattern.category));
|
|
322
|
+
const applicablePatterns = this.getApplicablePatterns(ruleSets);
|
|
279
323
|
// Scan content for each pattern
|
|
280
324
|
for (const securityPattern of applicablePatterns) {
|
|
281
325
|
const matches = this.findPatternMatches(content, lines, securityPattern);
|
|
@@ -291,7 +335,7 @@ export class SASTScanner {
|
|
|
291
335
|
vulnerabilities.push(this.createVulnerabilityFromPattern(securityPattern, filePath, match));
|
|
292
336
|
}
|
|
293
337
|
}
|
|
294
|
-
return { vulnerabilities,
|
|
338
|
+
return { vulnerabilities, evidence: { ...readReceipt, status: 'analyzed', analyzedLines: lines.length } };
|
|
295
339
|
}
|
|
296
340
|
/**
|
|
297
341
|
* Find all matches of a security pattern in the file content
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module security-compliance/semgrep-integration
|
|
11
11
|
*/
|
|
12
|
+
declare const SEMGREP_SEVERITIES: readonly ["ERROR", "WARNING", "INFO", "CRITICAL", "HIGH", "MEDIUM", "LOW", "EXPERIMENT", "INVENTORY"];
|
|
13
|
+
export type SemgrepSeverity = typeof SEMGREP_SEVERITIES[number];
|
|
12
14
|
export interface SemgrepFinding {
|
|
13
15
|
check_id: string;
|
|
14
16
|
path: string;
|
|
@@ -22,7 +24,7 @@ export interface SemgrepFinding {
|
|
|
22
24
|
};
|
|
23
25
|
extra: {
|
|
24
26
|
message: string;
|
|
25
|
-
severity:
|
|
27
|
+
severity: SemgrepSeverity;
|
|
26
28
|
lines: string;
|
|
27
29
|
metadata?: {
|
|
28
30
|
cwe?: string[];
|
|
@@ -37,8 +39,12 @@ export interface SemgrepFinding {
|
|
|
37
39
|
}
|
|
38
40
|
export interface SemgrepResult {
|
|
39
41
|
success: boolean;
|
|
42
|
+
/** Optional only for compatibility with legacy adapters. */
|
|
43
|
+
status?: 'completed' | 'partial' | 'failed' | 'unavailable';
|
|
40
44
|
findings: SemgrepFinding[];
|
|
41
45
|
errors: string[];
|
|
46
|
+
/** Sanitized non-fatal diagnostics, separate from analysis failures. */
|
|
47
|
+
diagnostics?: string[];
|
|
42
48
|
version?: string;
|
|
43
49
|
}
|
|
44
50
|
export interface SemgrepConfig {
|
|
@@ -74,7 +80,7 @@ export declare function runSemgrepWithRules(target: string, rules: string[], opt
|
|
|
74
80
|
/**
|
|
75
81
|
* Map semgrep severity to standard severity
|
|
76
82
|
*/
|
|
77
|
-
export declare function mapSemgrepSeverity(severity:
|
|
83
|
+
export declare function mapSemgrepSeverity(severity: SemgrepSeverity): 'critical' | 'high' | 'medium' | 'low';
|
|
78
84
|
/**
|
|
79
85
|
* Convert semgrep findings to a generic format
|
|
80
86
|
*/
|
|
@@ -92,4 +98,5 @@ export declare function convertSemgrepFindings(findings: SemgrepFinding[]): Arra
|
|
|
92
98
|
remediation: string;
|
|
93
99
|
references: string[];
|
|
94
100
|
}>;
|
|
101
|
+
export {};
|
|
95
102
|
//# sourceMappingURL=semgrep-integration.d.ts.map
|
|
@@ -12,10 +12,15 @@
|
|
|
12
12
|
import { execFile } from 'child_process';
|
|
13
13
|
import { promisify } from 'util';
|
|
14
14
|
import * as path from 'path';
|
|
15
|
-
import { toErrorMessage } from '../../../shared/error-utils.js';
|
|
16
15
|
import { safeJsonParse } from '../../../shared/safe-json.js';
|
|
17
16
|
const execFileAsync = promisify(execFile);
|
|
18
17
|
// ============================================================================
|
|
18
|
+
// Types
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Semgrep's match_severity schema retains legacy/deprecated values as well as
|
|
21
|
+
// CRITICAL/HIGH/MEDIUM/LOW introduced in 1.72.0.
|
|
22
|
+
const SEMGREP_SEVERITIES = ['ERROR', 'WARNING', 'INFO', 'CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'EXPERIMENT', 'INVENTORY'];
|
|
23
|
+
// ============================================================================
|
|
19
24
|
// Semgrep Integration
|
|
20
25
|
// ============================================================================
|
|
21
26
|
/**
|
|
@@ -59,8 +64,9 @@ export async function runSemgrep(config) {
|
|
|
59
64
|
if (!available) {
|
|
60
65
|
return {
|
|
61
66
|
success: false,
|
|
67
|
+
status: 'unavailable',
|
|
62
68
|
findings: [],
|
|
63
|
-
errors: ['Semgrep is
|
|
69
|
+
errors: ['Semgrep is unavailable.'],
|
|
64
70
|
};
|
|
65
71
|
}
|
|
66
72
|
try {
|
|
@@ -87,28 +93,26 @@ export async function runSemgrep(config) {
|
|
|
87
93
|
});
|
|
88
94
|
const result = parseSemgrepOutput(stdout);
|
|
89
95
|
if (stderr && fullConfig.verbose) {
|
|
90
|
-
result.
|
|
96
|
+
result.diagnostics = ['Semgrep emitted diagnostic output.'];
|
|
91
97
|
}
|
|
92
98
|
result.version = await getSemgrepVersion() || undefined;
|
|
93
99
|
return result;
|
|
94
100
|
}
|
|
95
101
|
catch (error) {
|
|
96
|
-
// Semgrep exits with non-zero if findings exist
|
|
97
102
|
const execError = error;
|
|
98
|
-
if (execError.stdout) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
103
|
+
if (typeof execError.stdout === 'string' && execError.stdout.length > 0) {
|
|
104
|
+
const result = parseSemgrepOutput(execError.stdout);
|
|
105
|
+
// Exit 1 can represent findings with --error. Other process failures cannot
|
|
106
|
+
// be converted into success just because stdout contains valid JSON.
|
|
107
|
+
if (execError.code === 1 && !execError.killed && !execError.signal && result.success && result.findings.length > 0) {
|
|
102
108
|
return result;
|
|
103
109
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
110
|
+
return { ...result, success: false, status: 'failed',
|
|
111
|
+
errors: [...result.errors, 'Semgrep process did not complete successfully.'] };
|
|
107
112
|
}
|
|
108
113
|
return {
|
|
109
|
-
success: false,
|
|
110
|
-
|
|
111
|
-
errors: [execError.message ?? String(error)],
|
|
114
|
+
success: false, status: 'failed', findings: [],
|
|
115
|
+
errors: ['Semgrep execution failed.'],
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
}
|
|
@@ -118,11 +122,47 @@ export async function runSemgrep(config) {
|
|
|
118
122
|
function parseSemgrepOutput(stdout) {
|
|
119
123
|
try {
|
|
120
124
|
const parsed = safeJsonParse(stdout);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
126
|
+
throw new Error('Expected a JSON object');
|
|
127
|
+
}
|
|
128
|
+
const rawResults = parsed.results ?? parsed.findings;
|
|
129
|
+
if (!Array.isArray(rawResults) || (parsed.errors !== undefined && !Array.isArray(parsed.errors))) {
|
|
130
|
+
throw new Error('Missing or invalid result collection');
|
|
131
|
+
}
|
|
132
|
+
const isRecord = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
133
|
+
const optionalText = (value) => value === undefined || typeof value === 'string';
|
|
134
|
+
const validSeverity = (value) => value === undefined
|
|
135
|
+
|| (typeof value === 'string' && SEMGREP_SEVERITIES.includes(value));
|
|
136
|
+
const validPosition = (value) => value === undefined || (isRecord(value)
|
|
137
|
+
&& ['line', 'col'].every(key => value[key] === undefined
|
|
138
|
+
|| (typeof value[key] === 'number' && Number.isSafeInteger(value[key]) && value[key] > 0)));
|
|
139
|
+
const validMetadata = (value) => value === undefined || (isRecord(value)
|
|
140
|
+
&& ['cwe', 'owasp', 'references'].every(key => value[key] === undefined
|
|
141
|
+
|| (Array.isArray(value[key]) && value[key].every(item => typeof item === 'string')))
|
|
142
|
+
&& ['category', 'description', 'fix', 'confidence'].every(key => optionalText(value[key])));
|
|
143
|
+
const validFinding = (value) => {
|
|
144
|
+
if (!isRecord(value))
|
|
145
|
+
return false;
|
|
146
|
+
const id = value.check_id ?? value.rule_id;
|
|
147
|
+
if (typeof value.path !== 'string' || value.path.length === 0 || typeof id !== 'string' || id.length === 0
|
|
148
|
+
|| !validPosition(value.start) || !validPosition(value.end)
|
|
149
|
+
|| !optionalText(value.message) || !validSeverity(value.severity) || !validMetadata(value.metadata))
|
|
150
|
+
return false;
|
|
151
|
+
const extra = value.extra;
|
|
152
|
+
return extra === undefined || (isRecord(extra)
|
|
153
|
+
&& ['message', 'lines', 'fix'].every(key => optionalText(extra[key]))
|
|
154
|
+
&& validSeverity(extra.severity) && validMetadata(extra.metadata));
|
|
155
|
+
};
|
|
156
|
+
const results = rawResults.filter(validFinding);
|
|
157
|
+
const malformed = rawResults.length - results.length;
|
|
158
|
+
const errors = [];
|
|
159
|
+
if (parsed.errors?.length)
|
|
160
|
+
errors.push(`Semgrep reported ${parsed.errors.length} analysis error(s).`);
|
|
161
|
+
if (malformed)
|
|
162
|
+
errors.push(`Semgrep returned ${malformed} malformed finding(s).`);
|
|
124
163
|
return {
|
|
125
|
-
success:
|
|
164
|
+
success: errors.length === 0,
|
|
165
|
+
status: errors.length === 0 ? 'completed' : 'partial',
|
|
126
166
|
findings: results.map(r => ({
|
|
127
167
|
check_id: r.check_id || r.rule_id || 'unknown',
|
|
128
168
|
path: r.path,
|
|
@@ -146,11 +186,12 @@ function parseSemgrepOutput(stdout) {
|
|
|
146
186
|
errors,
|
|
147
187
|
};
|
|
148
188
|
}
|
|
149
|
-
catch
|
|
189
|
+
catch {
|
|
150
190
|
return {
|
|
151
191
|
success: false,
|
|
192
|
+
status: 'failed',
|
|
152
193
|
findings: [],
|
|
153
|
-
errors: [
|
|
194
|
+
errors: ['Invalid Semgrep output.'],
|
|
154
195
|
};
|
|
155
196
|
}
|
|
156
197
|
}
|
|
@@ -191,6 +232,13 @@ export function mapSemgrepSeverity(severity) {
|
|
|
191
232
|
ERROR: 'high',
|
|
192
233
|
WARNING: 'medium',
|
|
193
234
|
INFO: 'low',
|
|
235
|
+
CRITICAL: 'critical',
|
|
236
|
+
HIGH: 'high',
|
|
237
|
+
MEDIUM: 'medium',
|
|
238
|
+
LOW: 'low',
|
|
239
|
+
// Retain the existing non-risky INFO mapping for deprecated categories.
|
|
240
|
+
EXPERIMENT: 'low',
|
|
241
|
+
INVENTORY: 'low',
|
|
194
242
|
};
|
|
195
243
|
return mapping[severity] || 'medium';
|
|
196
244
|
}
|
|
@@ -11,6 +11,7 @@ import { ok, err } from '../../../shared/types/index.js';
|
|
|
11
11
|
import { TEST_EXECUTION_CONSTANTS, RETRY_CONSTANTS } from '../../constants.js';
|
|
12
12
|
import { toError } from '../../../shared/error-utils.js';
|
|
13
13
|
import { safeJsonParse } from '../../../shared/safe-json.js';
|
|
14
|
+
import { createVitestJsonReport, needsVitestJsonReportFile } from '../../../shared/vitest-json-report.js';
|
|
14
15
|
import { secureRandom } from '../../../shared/utils/crypto-random.js';
|
|
15
16
|
const DEFAULT_FLAKY_CONFIG = {
|
|
16
17
|
simulateForTesting: false,
|
|
@@ -254,7 +255,13 @@ export class FlakyDetectorService {
|
|
|
254
255
|
const runId = uuidv4();
|
|
255
256
|
const startTime = Date.now();
|
|
256
257
|
return new Promise((resolve, reject) => {
|
|
257
|
-
const
|
|
258
|
+
const baseArgs = [...this.config.testRunnerArgs, file];
|
|
259
|
+
// Vitest 5 writes --reporter=json output to a file, not stdout. Detect
|
|
260
|
+
// Vitest from the runner command too (testRunner: 'vitest', args: ['run', ...]).
|
|
261
|
+
const report = needsVitestJsonReportFile([this.config.testRunner, ...baseArgs])
|
|
262
|
+
? createVitestJsonReport()
|
|
263
|
+
: undefined;
|
|
264
|
+
const args = report ? [...baseArgs, ...report.args] : baseArgs;
|
|
258
265
|
const cwd = this.config.cwd ?? process.cwd();
|
|
259
266
|
const child = spawn(this.config.testRunner, args, {
|
|
260
267
|
cwd,
|
|
@@ -272,19 +279,23 @@ export class FlakyDetectorService {
|
|
|
272
279
|
// Set timeout
|
|
273
280
|
const timeout = setTimeout(() => {
|
|
274
281
|
child.kill('SIGTERM');
|
|
282
|
+
report?.cleanup();
|
|
275
283
|
reject(new Error(`Test execution timed out after ${this.config.runTimeout}ms for ${file}`));
|
|
276
284
|
}, this.config.runTimeout);
|
|
277
285
|
child.on('error', (error) => {
|
|
278
286
|
clearTimeout(timeout);
|
|
287
|
+
report?.cleanup();
|
|
279
288
|
reject(new Error(`Failed to execute test runner: ${error.message}. ` +
|
|
280
289
|
`Ensure ${this.config.testRunner} is installed and accessible.`));
|
|
281
290
|
});
|
|
282
291
|
child.on('close', (code) => {
|
|
283
292
|
clearTimeout(timeout);
|
|
284
293
|
const duration = Date.now() - startTime;
|
|
294
|
+
const reportText = report ? report.read(stdout) : stdout;
|
|
295
|
+
report?.cleanup();
|
|
285
296
|
try {
|
|
286
|
-
// Parse the test results from stdout
|
|
287
|
-
const parsedResults = this.parseTestOutput(
|
|
297
|
+
// Parse the test results from the JSON report (or stdout for other runners)
|
|
298
|
+
const parsedResults = this.parseTestOutput(reportText, stderr, code ?? 0, file, runId, runIndex, duration);
|
|
288
299
|
// If parsing fails but we have an exit code, create a single result for the file
|
|
289
300
|
if (parsedResults.size === 0) {
|
|
290
301
|
const testId = this.generateTestId(file, 'main');
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { LoggerFactory } from '../../../logging/index.js';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
|
+
import { createVitestJsonReport } from '../../../shared/vitest-json-report.js';
|
|
7
8
|
import { existsSync, readFileSync } from 'node:fs';
|
|
8
9
|
import { ok, err } from '../../../shared/types/index.js';
|
|
9
10
|
import { TEST_EXECUTION_CONSTANTS, RETRY_CONSTANTS } from '../../constants.js';
|
|
@@ -262,9 +263,9 @@ export class RetryHandlerService {
|
|
|
262
263
|
`or configure testRunner in RetryHandlerConfig. Cannot execute retry for: ${testFile}`);
|
|
263
264
|
}
|
|
264
265
|
// Build command based on runner
|
|
265
|
-
const { command, args } = this.buildTestCommand(runner, testFile, testName);
|
|
266
|
+
const { command, args, report } = this.buildTestCommand(runner, testFile, testName);
|
|
266
267
|
// Execute the test
|
|
267
|
-
return this.spawnTestProcess(command, args, cwd);
|
|
268
|
+
return this.spawnTestProcess(command, args, cwd, report);
|
|
268
269
|
}
|
|
269
270
|
/**
|
|
270
271
|
* Detect which test runner is available in the project
|
|
@@ -337,12 +338,15 @@ export class RetryHandlerService {
|
|
|
337
338
|
switch (runner) {
|
|
338
339
|
case 'vitest':
|
|
339
340
|
// vitest run --reporter=json testFile -t "testName"
|
|
341
|
+
// --outputFile: Vitest 5 no longer prints the JSON report to stdout.
|
|
340
342
|
{
|
|
343
|
+
const report = createVitestJsonReport();
|
|
341
344
|
const vitestArgs = ['vitest', 'run', '--reporter=json', testFile];
|
|
342
345
|
if (testName && testName !== testFile) {
|
|
343
346
|
vitestArgs.push('-t', testName);
|
|
344
347
|
}
|
|
345
|
-
|
|
348
|
+
vitestArgs.push(...report.args);
|
|
349
|
+
return { command: npx, args: vitestArgs, report };
|
|
346
350
|
}
|
|
347
351
|
case 'jest':
|
|
348
352
|
// jest --json testFile -t "testName"
|
|
@@ -369,12 +373,16 @@ export class RetryHandlerService {
|
|
|
369
373
|
/**
|
|
370
374
|
* Spawn a test process and parse the result
|
|
371
375
|
*/
|
|
372
|
-
spawnTestProcess(command, args, cwd) {
|
|
376
|
+
spawnTestProcess(command, args, cwd, report) {
|
|
373
377
|
return new Promise((resolve, reject) => {
|
|
374
378
|
const timeout = this.config.testTimeout;
|
|
375
379
|
let stdout = '';
|
|
376
380
|
let stderr = '';
|
|
377
381
|
let timedOut = false;
|
|
382
|
+
const settle = (fn, value) => {
|
|
383
|
+
report?.cleanup();
|
|
384
|
+
fn(value);
|
|
385
|
+
};
|
|
378
386
|
// Note: shell: false (default) to prevent command injection (CWE-78)
|
|
379
387
|
// Arguments are passed as array to avoid shell interpretation
|
|
380
388
|
const proc = spawn(command, args, {
|
|
@@ -396,15 +404,17 @@ export class RetryHandlerService {
|
|
|
396
404
|
proc.on('close', (code) => {
|
|
397
405
|
clearTimeout(timeoutHandle);
|
|
398
406
|
if (timedOut) {
|
|
399
|
-
reject
|
|
407
|
+
settle(reject, new Error(`Test execution timed out after ${timeout}ms for command: ${command} ${args.join(' ')}`));
|
|
400
408
|
return;
|
|
401
409
|
}
|
|
402
|
-
// Parse result based on exit code and output
|
|
403
|
-
|
|
404
|
-
|
|
410
|
+
// Parse result based on exit code and output (Vitest writes the JSON
|
|
411
|
+
// report to the --outputFile; read it back rather than trusting stdout).
|
|
412
|
+
const result = this.parseTestResult(code, report ? report.read(stdout) : stdout, stderr);
|
|
413
|
+
settle(resolve, result);
|
|
405
414
|
});
|
|
406
415
|
proc.on('error', (err) => {
|
|
407
416
|
clearTimeout(timeoutHandle);
|
|
417
|
+
report?.cleanup();
|
|
408
418
|
reject(new Error(`Failed to spawn test process: ${err.message}. ` +
|
|
409
419
|
`Command: ${command} ${args.join(' ')}. ` +
|
|
410
420
|
`Ensure the test runner is installed and accessible.`));
|