0sec-cli 0.23.0 → 0.24.1
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/0.js +66 -67
- package/README.md +37 -11
- package/chunks/{adapt-loop-V3JFWOKC.js → adapt-loop-L77BWMNE.js} +4 -4
- package/chunks/{artifact-scraper-NYKUF2M5.js → artifact-scraper-BV67RAP5.js} +2 -2
- package/chunks/{assumption-mining-ZX3YVWEH.js → assumption-mining-RPVR77A3.js} +18 -18
- package/chunks/{backend-connections-H52NIVH2.js → backend-connections-ACIXNYZN.js} +3 -3
- package/chunks/{chunk-MQJVU2GZ.js → chunk-2NBZZFRG.js} +79 -9
- package/chunks/{chunk-A2ADXAC5.js → chunk-2XGBEH53.js} +1288 -2313
- package/chunks/{chunk-3E54JYS4.js → chunk-3LPBJMHB.js} +1 -1
- package/chunks/{chunk-XORZUR2H.js → chunk-3WUPCZGF.js} +1 -1
- package/chunks/{chunk-65H3MRBF.js → chunk-4ROZFLSR.js} +1 -1
- package/chunks/chunk-56UTQKXO.js +56 -0
- package/chunks/{chunk-YYS2H33A.js → chunk-5ETI27Q3.js} +2 -2
- package/chunks/{chunk-3ZNI7F42.js → chunk-5NFIY2SJ.js} +3 -3
- package/chunks/{chunk-A5QO7N3S.js → chunk-5PFRE522.js} +1 -1
- package/chunks/{chunk-EH2WTNY7.js → chunk-5WAHHZBF.js} +1424 -859
- package/chunks/{chunk-WIYNYNL2.js → chunk-6XMFCVJS.js} +1 -1
- package/chunks/chunk-7UHAXEOV.js +289 -0
- package/chunks/{chunk-CP6HANBE.js → chunk-BD7WULFS.js} +4 -3
- package/chunks/{chunk-T7ZEVY5G.js → chunk-BMDG6T7D.js} +815 -41
- package/chunks/{chunk-QMICC5O2.js → chunk-BOBIAHKA.js} +2 -2
- package/chunks/{chunk-VDOWARMJ.js → chunk-BPU6M42R.js} +15 -1
- package/chunks/{chunk-UELXHK2Y.js → chunk-CGBDEST2.js} +16 -5
- package/chunks/{remote-workflow-runtime-RGQ5ECMN.js → chunk-CJRZSWII.js} +39 -8
- package/chunks/chunk-F2P25DJY.js +5421 -0
- package/chunks/{chunk-TO35Y326.js → chunk-GTED3FSZ.js} +5 -2
- package/chunks/{chunk-LT7BFGXA.js → chunk-GV5WXDHY.js} +10 -10
- package/chunks/{chunk-LTEE2JWK.js → chunk-GXXX7AGH.js} +251 -8
- package/chunks/chunk-GZA7RGK7.js +5198 -0
- package/chunks/{chunk-KHPOSLCF.js → chunk-H5QURBBK.js} +174 -710
- package/chunks/chunk-I43YYHDU.js +566 -0
- package/chunks/{chunk-62ENS6XO.js → chunk-IIBNQTOK.js} +3 -2
- package/chunks/{chunk-BF5FIK7H.js → chunk-IJ6UPW3H.js} +108 -24
- package/chunks/{chunk-TPUJZ74W.js → chunk-ILRMLA6J.js} +2 -2
- package/chunks/{chunk-I3OCJ2OA.js → chunk-JWSOUKFF.js} +5 -3
- package/chunks/{chunk-UZUELPFW.js → chunk-K74DRDC5.js} +1 -1
- package/chunks/chunk-LMP2T4G2.js +700 -0
- package/chunks/{chunk-K55B4OH4.js → chunk-NBV4I237.js} +1 -1
- package/chunks/chunk-OOMBOLAF.js +295 -0
- package/chunks/{chunk-YAEJAZYW.js → chunk-PJU24AKR.js} +2 -2
- package/chunks/{chunk-NI6QYQSK.js → chunk-PVJUPIQG.js} +6 -6
- package/chunks/{chunk-WYNZFNMF.js → chunk-Q7JKIKKF.js} +289 -10
- package/chunks/{chunk-ITES7BCY.js → chunk-QNRYOJEB.js} +3 -111
- package/chunks/{chunk-PIR6BNE7.js → chunk-R2XBH476.js} +2 -2
- package/chunks/{chunk-O6Z4XPOM.js → chunk-RURJYCWY.js} +1 -1
- package/chunks/chunk-RVJVM5L4.js +164 -0
- package/chunks/{chunk-NX4H6L4M.js → chunk-S2GITRD5.js} +2 -2
- package/chunks/{chunk-W5VNCB57.js → chunk-SU4IHQQI.js} +1 -1
- package/chunks/{chunk-V2BWXPZP.js → chunk-SZDD77KX.js} +6 -6
- package/chunks/{chunk-EJH2TNVH.js → chunk-TVS6WVSK.js} +3 -3
- package/chunks/{chunk-LVGZGG63.js → chunk-UJE4WOQS.js} +1 -1
- package/chunks/chunk-VU7V2SDW.js +284 -0
- package/chunks/chunk-W624J7NF.js +117 -0
- package/chunks/{chunk-OLSKXTQZ.js → chunk-WUPN3D37.js} +22 -248
- package/chunks/{chunk-HFDSIHTE.js → chunk-X2IPO5JO.js} +202 -30
- package/chunks/{chunk-JEYIWMGL.js → chunk-X2ZMSRTP.js} +1 -1
- package/chunks/{chunk-F344NYUN.js → chunk-XA2IBHPB.js} +1 -1
- package/chunks/{chunk-Y4HJRUFE.js → chunk-YERUDEXT.js} +876 -817
- package/chunks/{chunk-AAA4PRWK.js → chunk-YKWARO3T.js} +2 -2
- package/chunks/{chunk-P7KDN5PK.js → chunk-YSQWZ67M.js} +14 -14
- package/chunks/{chunk-4HGGNBKK.js → chunk-ZM6ANQZJ.js} +2593 -1563
- package/chunks/{codex-models-Y457ND6Q.js → codex-models-6XVNFRXZ.js} +7 -7
- package/chunks/{commands-PBDAOZKA.js → commands-FRPOFIO4.js} +1603 -6945
- package/chunks/dashboard-5HDSNBAU.js +76 -0
- package/chunks/{db-TPSI552V.js → db-BXJNWUYJ.js} +3 -3
- package/chunks/{deep-review-DKED6EN5.js → deep-review-GSJEBLCG.js} +34 -33
- package/chunks/{disclose-LDNKAVRH.js → disclose-OU6Z6GVT.js} +4 -2
- package/chunks/{dist-RLPNLRZD.js → dist-7KSYDDSG.js} +33 -1
- package/chunks/{dist-7Q5A6TQM.js → dist-RKLQIXDT.js} +18 -2
- package/chunks/{dist-4LGRIBOC.js → dist-TQMHMSBZ.js} +99 -64
- package/chunks/eval-runner-A2DIGVP7.js +31 -0
- package/chunks/{exploit-agent-RMFYNG7H.js → exploit-agent-Y2P4GFIY.js} +1 -1
- package/chunks/{exploit-autoclimb-2A2PQI4E.js → exploit-autoclimb-YRZ3TMS2.js} +1 -1
- package/chunks/{exploit-climb-DEJ232QN.js → exploit-climb-4LEY4SDW.js} +2 -2
- package/chunks/{fix-JGBW7XUN.js → fix-RQSIG75B.js} +7 -7
- package/chunks/{harness-CBOLMUHB.js → harness-3NVC3DDY.js} +3 -3
- package/chunks/{hunt-scan-MIRMABY4.js → hunt-scan-W246VWJC.js} +16 -16
- package/chunks/{kernel-primitive-NAPDMJC7.js → kernel-primitive-T47V3RD3.js} +2 -2
- package/chunks/{kernel-vm-runner-PR5OSDQ2.js → kernel-vm-runner-AEL5E3SV.js} +2 -2
- package/chunks/local-engine-ZCW4YC6E.js +21 -0
- package/chunks/managed-operation-6R2QPPW5.js +60 -0
- package/chunks/{memsafety-scan-RPS446CD.js → memsafety-scan-L2Z7E7TE.js} +2 -2
- package/chunks/{native-loop-CGOJMNHV.js → native-loop-26X2CHBV.js} +14 -13
- package/chunks/orchestrate-OBS37FWQ.js +63 -0
- package/chunks/{prepare-YVNENPNL.js → prepare-C3KGMBXT.js} +5 -5
- package/chunks/provider-model-discovery-QGUGL3XW.js +277 -0
- package/chunks/remote-workflow-runtime-DB52SINR.js +12 -0
- package/chunks/{run-VH4OIDKF.js → run-MU3MFL2Z.js} +902 -322
- package/chunks/{runtime-4TAJFVF7.js → runtime-AUBLQUOH.js} +2 -2
- package/chunks/{runtime-HWL6HG7A.js → runtime-U66JNSAY.js} +7 -7
- package/chunks/{scan-stream-HJAZJONE.js → scan-stream-34F3LTPV.js} +3 -2
- package/chunks/{scope-Q5PIX2IS.js → scope-HSTG63TQ.js} +2 -2
- package/chunks/service-plugins-5B23PWXC.js +64 -0
- package/chunks/{session-store-2FEIZGSV.js → session-store-MKAAP3WL.js} +2 -2
- package/chunks/{variant-candidates-TX5V6XOT.js → variant-candidates-LG767HWH.js} +7 -7
- package/chunks/{web-recon-prepass-6HOUGF7Q.js → web-recon-prepass-NJP6J3E4.js} +5 -5
- package/chunks/workflow-runtime-AR627I26.js +178 -0
- package/dashboard/assets/{abnfDiagram-VCTEODGH-BuvumFYR.js → abnfDiagram-VCTEODGH-Cq149Jpk.js} +1 -1
- package/dashboard/assets/{arc-DZzWDuCG.js → arc-CpP-3TRW.js} +1 -1
- package/dashboard/assets/architecture-7GRP2DOG-z5EHYkyH.js +1 -0
- package/dashboard/assets/{architectureDiagram-5GKGNRK7-Bw5el_EL.js → architectureDiagram-5GKGNRK7-ewtXpjR_.js} +1 -1
- package/dashboard/assets/{blockDiagram-I7D4REHJ-BjVLEApp.js → blockDiagram-I7D4REHJ-CZYkRRDi.js} +1 -1
- package/dashboard/assets/{c4Diagram-7LVT6UL2-B9nNzx2J.js → c4Diagram-7LVT6UL2-CV7Iu3_Z.js} +1 -1
- package/dashboard/assets/channel-CuYjfiC5.js +1 -0
- package/dashboard/assets/{chunk-4HAMMTFA-y72Q95nW.js → chunk-4HAMMTFA-PKCyW4m-.js} +1 -1
- package/dashboard/assets/{chunk-75Z2AOVW-BOhrOGMO.js → chunk-75Z2AOVW-C5MlFTc_.js} +1 -1
- package/dashboard/assets/{chunk-DU6HZSFF-B0EhAaNb.js → chunk-DU6HZSFF-3M37ulcO.js} +2 -2
- package/dashboard/assets/{chunk-F27PBJKO-BR-hiDvM.js → chunk-F27PBJKO-DTAok0qM.js} +1 -1
- package/dashboard/assets/{chunk-GMAD6QVW-H15vJYXg.js → chunk-GMAD6QVW-CPsr2F33.js} +1 -1
- package/dashboard/assets/{chunk-GVQU2GXP-BRndps0o.js → chunk-GVQU2GXP-C2AXGf9A.js} +1 -1
- package/dashboard/assets/{chunk-IMKFNOWR-CIzC_OWx.js → chunk-IMKFNOWR-DMG6yD_O.js} +1 -1
- package/dashboard/assets/{chunk-L3NEJ4N5-NR4P1Vko.js → chunk-L3NEJ4N5-BSvwdbXx.js} +1 -1
- package/dashboard/assets/{chunk-OSK3NFVY-DtjTDf8S.js → chunk-OSK3NFVY-CfB8V7FU.js} +1 -1
- package/dashboard/assets/{chunk-P2QGCYS3-CYT11Qjx.js → chunk-P2QGCYS3-CvmZvq6n.js} +1 -1
- package/dashboard/assets/{chunk-POPQ4Y6H-Duj2Ygwu.js → chunk-POPQ4Y6H-D_TvwDxL.js} +1 -1
- package/dashboard/assets/{chunk-PWAF6VOD-z4p5Tf_y.js → chunk-PWAF6VOD-Da5GnuOY.js} +1 -1
- package/dashboard/assets/{chunk-SHT3W25Y-DYeLYLS3.js → chunk-SHT3W25Y-CcsMJpR-.js} +1 -1
- package/dashboard/assets/{chunk-SVP7TREG-BKowxCVI.js → chunk-SVP7TREG-COojjieJ.js} +1 -1
- package/dashboard/assets/{chunk-TICWLB2K-GDAwEFlH.js → chunk-TICWLB2K-nBAmJwx6.js} +1 -1
- package/dashboard/assets/{chunk-XXDRQBXY-DRD79Mjx.js → chunk-XXDRQBXY-r5HyhWDv.js} +1 -1
- package/dashboard/assets/classDiagram-ZZMXUADV-DCuOrWUV.js +1 -0
- package/dashboard/assets/classDiagram-v2-VYDZK3BY-DCuOrWUV.js +1 -0
- package/dashboard/assets/{common-vnzd7UOF.js → common-C6snVPsk.js} +1 -1
- package/dashboard/assets/{cose-bilkent-JH36ORCC-GqCLsRyf.js → cose-bilkent-JH36ORCC-BLGcaZHk.js} +1 -1
- package/dashboard/assets/{cynefin-OW5HDTMX-CJN9YwWN.js → cynefin-OW5HDTMX-DbZMK00I.js} +1 -1
- package/dashboard/assets/{cynefinDiagram-5FMLGOSQ-DVJHCYQY.js → cynefinDiagram-5FMLGOSQ-xodkuQtW.js} +1 -1
- package/dashboard/assets/{dagre-GXQ25YYZ-BMCrrJJR.js → dagre-GXQ25YYZ-CRk9qfSG.js} +1 -1
- package/dashboard/assets/{diagram-S7CK7UJ4-CLYDdOEm.js → diagram-S7CK7UJ4-DNoTMlCO.js} +1 -1
- package/dashboard/assets/{diagram-UQ7AKVKN-CnPuAZK7.js → diagram-UQ7AKVKN-COccuyew.js} +1 -1
- package/dashboard/assets/{diagram-VSXAHHWV-Cr2I2DnG.js → diagram-VSXAHHWV-g6D1tWRb.js} +1 -1
- package/dashboard/assets/{diagram-VX7I27RA-_mGW1wpp.js → diagram-VX7I27RA-DhO4eVIC.js} +1 -1
- package/dashboard/assets/{diagram-Z3DM3KII-igybB0l9.js → diagram-Z3DM3KII-DeOQFexd.js} +1 -1
- package/dashboard/assets/{dist-Bb7_-x5y.js → dist-BQYeOQg3.js} +1 -1
- package/dashboard/assets/{ebnfDiagram-PWID7BFC-CVCICf2R.js → ebnfDiagram-PWID7BFC-MtkX2feM.js} +1 -1
- package/dashboard/assets/{erDiagram-RLTQ6QDP-CRjnfgxj.js → erDiagram-RLTQ6QDP-DIWSsoYZ.js} +1 -1
- package/dashboard/assets/eventmodeling-NTZA5JFV-D_3ez4Gv.js +1 -0
- package/dashboard/assets/flowDiagram-HODETNUW-D2boyYEd.js +1 -0
- package/dashboard/assets/{ganttDiagram-EL5Y4UJY-DppD7sD7.js → ganttDiagram-EL5Y4UJY-DKggkJe0.js} +1 -1
- package/dashboard/assets/{gitGraph-4MIJSDKK-Tw0vNnQc.js → gitGraph-4MIJSDKK-DFOQYwXc.js} +1 -1
- package/dashboard/assets/{gitGraphDiagram-WWUBYQGX-Cpf96-z3.js → gitGraphDiagram-WWUBYQGX-r_iR8Qzo.js} +1 -1
- package/dashboard/assets/{info-A6RAGUB7-DLyt9arx.js → info-A6RAGUB7-DeednLoB.js} +1 -1
- package/dashboard/assets/{infoDiagram-27XIBGKW-B6_piSNS.js → infoDiagram-27XIBGKW-ClQk7UyQ.js} +1 -1
- package/dashboard/assets/{ishikawaDiagram-5VMMS53U-DvMO0B0X.js → ishikawaDiagram-5VMMS53U-tR7Vziog.js} +1 -1
- package/dashboard/assets/{journeyDiagram-3NMN7TZE-f7ZK98b6.js → journeyDiagram-3NMN7TZE-Cpb9IqKL.js} +1 -1
- package/dashboard/assets/{kanban-definition-UXKFOSKX-Dso0nAxL.js → kanban-definition-UXKFOSKX-BzUuKbA9.js} +1 -1
- package/dashboard/assets/katex-BdBmVSus.js +257 -0
- package/dashboard/assets/{line-VcKaFG0G.js → line-BHY_tSHu.js} +1 -1
- package/dashboard/assets/{linear-D7YnpiWh.js → linear-naZZ1pHk.js} +1 -1
- package/dashboard/assets/{mermaid-parser.core-DriL2hrV.js → mermaid-parser.core-CPvd4cEx.js} +3 -3
- package/dashboard/assets/{mermaid.core-DJWYDh_P.js → mermaid.core-D_QsUPU8.js} +4 -4
- package/dashboard/assets/{mindmap-definition-YA3MSWOX-DZsoIeac.js → mindmap-definition-YA3MSWOX-CtfLTPPJ.js} +1 -1
- package/dashboard/assets/operations-BwCzhVnT.js +130 -0
- package/dashboard/assets/operations-QjKT0Hwi.css +1 -0
- package/dashboard/assets/{packet-AYTQ26CC-BQBIXKxk.js → packet-AYTQ26CC-DDQbkT9I.js} +1 -1
- package/dashboard/assets/{pegDiagram-XKGWAZYB-B0-lslgW.js → pegDiagram-XKGWAZYB-C1Dclbzj.js} +1 -1
- package/dashboard/assets/{pie-WAS4IAKB-DoY6dwQs.js → pie-WAS4IAKB-LpU4ULAs.js} +1 -1
- package/dashboard/assets/{pieDiagram-E7YTZNPT-ElmdRjp5.js → pieDiagram-E7YTZNPT-Cd7XwYo5.js} +1 -1
- package/dashboard/assets/{quadrantDiagram-AXDQQJYC-Ci1h98Q2.js → quadrantDiagram-AXDQQJYC-CaS-GQ4X.js} +1 -1
- package/dashboard/assets/{radar-RG4KPBEZ-DHod2WgB.js → radar-RG4KPBEZ-CcxW3NJm.js} +1 -1
- package/dashboard/assets/{railroad-74A4TZTK-90JxB4xV.js → railroad-74A4TZTK-9P5Rl8fP.js} +1 -1
- package/dashboard/assets/railroad-abnf-HS5TGJTU-CSfpwjna.js +1 -0
- package/dashboard/assets/railroad-ebnf-LZEXJU2U-D6R5qzr-.js +1 -0
- package/dashboard/assets/railroad-peg-WCYAUIDC-DKEX7ITG.js +1 -0
- package/dashboard/assets/{railroadDiagram-O6MQD6OU-k3190A6L.js → railroadDiagram-O6MQD6OU-BeesTrqn.js} +1 -1
- package/dashboard/assets/{requirementDiagram-BXWQKSXE-C3-eR7I5.js → requirementDiagram-BXWQKSXE-IjyK0DhW.js} +1 -1
- package/dashboard/assets/{sankeyDiagram-P5KCCOFB-Bc86pi8w.js → sankeyDiagram-P5KCCOFB-DmIluQqo.js} +1 -1
- package/dashboard/assets/{sequenceDiagram-WJ2MYXX4-Bv_MvEmz.js → sequenceDiagram-WJ2MYXX4-CUp8ZjTc.js} +1 -1
- package/dashboard/assets/{src-D7Dqq5yP.js → src-r61wDjcA.js} +1 -1
- package/dashboard/assets/{stateDiagram-D77RDMKH-Bq_GVmeW.js → stateDiagram-D77RDMKH-Dy9TQb8P.js} +1 -1
- package/dashboard/assets/stateDiagram-v2-MP3YSRHH-CQb0ZtPr.js +1 -0
- package/dashboard/assets/{swimlanes-42K2YHIH-q0iK-e0P.js → swimlanes-42K2YHIH-CZuDV7V5.js} +1 -1
- package/dashboard/assets/swimlanesDiagram-VR7AAH4N-BxgWMq8X.js +8 -0
- package/dashboard/assets/{timeline-definition-24CTP7MA-CNfF07hM.js → timeline-definition-24CTP7MA-BGdXW-iY.js} +1 -1
- package/dashboard/assets/{treeView-Q6P3EWNA-m629ePnS.js → treeView-Q6P3EWNA-DlRrayjM.js} +1 -1
- package/dashboard/assets/{treemap-WGGIJYW6-D9Cb2ETv.js → treemap-WGGIJYW6-C6UqLKfI.js} +1 -1
- package/dashboard/assets/{vennDiagram-4TSXK5OY-v4dKF-FE.js → vennDiagram-4TSXK5OY-DzKWHJsl.js} +1 -1
- package/dashboard/assets/{wardley-WFR3VGLG-DkwH_nRe.js → wardley-WFR3VGLG-thmyHKb0.js} +1 -1
- package/dashboard/assets/{wardleyDiagram-VM6X3IG4-3ImohVu4.js → wardleyDiagram-VM6X3IG4-DGsjYmNJ.js} +1 -1
- package/dashboard/assets/{xychartDiagram-S5SC5T6Z-QJu3dm7K.js → xychartDiagram-S5SC5T6Z-C7_T_tWt.js} +1 -1
- package/dashboard/index.html +3 -3
- package/package.json +3 -2
- package/worker-assets/demo-target/README.md +15 -0
- package/worker-assets/demo-target/include/decoder.h +27 -0
- package/worker-assets/demo-target/patched-decoder.c +40 -0
- package/worker-assets/demo-target/src/decoder.c +50 -0
- package/worker-assets/demo-target/witness-65537x1.bin +0 -0
- package/chunks/chunk-3P3XXSIJ.js +0 -463
- package/chunks/chunk-EWXOG6D3.js +0 -1695
- package/chunks/chunk-FXR3BT2G.js +0 -226
- package/chunks/chunk-R35KKJA4.js +0 -90
- package/chunks/eval-runner-RZKDOQ2G.js +0 -30
- package/chunks/managed-operation-YNQQEA5V.js +0 -59
- package/chunks/orchestrate-5NK34H3W.js +0 -62
- package/chunks/service-plugins-QYFITODK.js +0 -63
- package/chunks/workflow-runtime-ZEEBJFQG.js +0 -64
- package/dashboard/assets/architecture-7GRP2DOG-DK9bv1Md.js +0 -1
- package/dashboard/assets/channel-Ck17eKdX.js +0 -1
- package/dashboard/assets/classDiagram-ZZMXUADV-BaeZA6LE.js +0 -1
- package/dashboard/assets/classDiagram-v2-VYDZK3BY-BaeZA6LE.js +0 -1
- package/dashboard/assets/eventmodeling-NTZA5JFV-po5oDydv.js +0 -1
- package/dashboard/assets/flowDiagram-HODETNUW-TSV9s7q4.js +0 -1
- package/dashboard/assets/katex-CXMH3UgJ.js +0 -257
- package/dashboard/assets/operations-BIaUSnYv.js +0 -121
- package/dashboard/assets/operations-BeCdkT4S.css +0 -1
- package/dashboard/assets/railroad-abnf-HS5TGJTU-BkXGl114.js +0 -1
- package/dashboard/assets/railroad-ebnf-LZEXJU2U-QVAERn2C.js +0 -1
- package/dashboard/assets/railroad-peg-WCYAUIDC-B1-PM01z.js +0 -1
- package/dashboard/assets/stateDiagram-v2-MP3YSRHH-z1XRaftz.js +0 -1
- package/dashboard/assets/swimlanesDiagram-VR7AAH4N-C9L6XBQj.js +0 -8
- package/dashboard/assets/zero-connect-BGDY0PMZ.webp +0 -0
- package/dashboard/assets/zero-curious-DovTLOs8.webp +0 -0
- package/dashboard/assets/zero-peek-Dzm3MT3X.png +0 -0
- package/dashboard/assets/zero-planning-CBeHDrG_.webp +0 -0
|
@@ -1,195 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __0CreateRequire } from "node:module";
|
|
3
3
|
const require = __0CreateRequire(import.meta.url);
|
|
4
|
-
import {
|
|
5
|
-
source_default
|
|
6
|
-
} from "./chunk-HLFS6WV2.js";
|
|
7
4
|
import {
|
|
8
5
|
SEVERITY_RANK,
|
|
9
6
|
VERSION,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
severityRank
|
|
14
|
-
} from "./chunk-LTEE2JWK.js";
|
|
15
|
-
|
|
16
|
-
// packages/cli/src/utils.ts
|
|
17
|
-
import { gzipSync } from "zlib";
|
|
18
|
-
async function getRuntimeAvailability() {
|
|
19
|
-
const { detectAvailableRuntimes, LlmApiRuntime } = await import("./dist-4LGRIBOC.js");
|
|
20
|
-
const apiRuntimeDiagnostics = new LlmApiRuntime({ type: "api", timeout: 5e3 }).getConfigurationDiagnostics();
|
|
21
|
-
const hasApiKey = apiRuntimeDiagnostics.valid;
|
|
22
|
-
const availableRuntimes = [...await detectAvailableRuntimes()];
|
|
23
|
-
return {
|
|
24
|
-
hasApiKey,
|
|
25
|
-
availableRuntimes,
|
|
26
|
-
apiRuntime: {
|
|
27
|
-
configured: apiRuntimeDiagnostics.reason !== "missing_key",
|
|
28
|
-
valid: apiRuntimeDiagnostics.valid,
|
|
29
|
-
providerLabel: apiRuntimeDiagnostics.providerLabel,
|
|
30
|
-
error: apiRuntimeDiagnostics.fatalError
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
async function checkRuntimeAvailability(runtime) {
|
|
35
|
-
const availability = await getRuntimeAvailability();
|
|
36
|
-
const { hasApiKey, availableRuntimes, apiRuntime } = availability;
|
|
37
|
-
if (hasApiKey) return;
|
|
38
|
-
console.log("");
|
|
39
|
-
if ((runtime === "api" || runtime === "auto") && apiRuntime.configured && apiRuntime.error) {
|
|
40
|
-
console.log(source_default.red(` ${apiRuntime.providerLabel} config error: ${apiRuntime.error.split("\n")[0]}`));
|
|
41
|
-
}
|
|
42
|
-
if (runtime === "api") {
|
|
43
|
-
console.log(source_default.yellow(
|
|
44
|
-
apiRuntime.configured ? " Warning: `--runtime api` needs a valid provider configuration. AI analysis will fail until this is fixed." : " Warning: `--runtime api` needs provider credentials. AI analysis may be skipped."
|
|
45
|
-
));
|
|
46
|
-
} else if (availableRuntimes.length > 0) {
|
|
47
|
-
console.log(source_default.cyan(` Using local runtime(s): ${availableRuntimes.join(", ")}`));
|
|
48
|
-
} else {
|
|
49
|
-
console.log(source_default.yellow(" Warning: No API key or local agent runtime detected. AI analysis will be skipped."));
|
|
50
|
-
}
|
|
51
|
-
console.log(source_default.gray(" Provider credentials: ZERO_CHATGPT_ACCESS_TOKEN | ZERO_CHATGPT_OAUTH_REFRESH_TOKEN, OPENROUTER_API_KEY, ANTHROPIC_API_KEY, AZURE_OPENAI_API_KEY, OPENAI_API_KEY"));
|
|
52
|
-
console.log("");
|
|
53
|
-
}
|
|
54
|
-
function buildShareUrl(report) {
|
|
55
|
-
const json = JSON.stringify(report);
|
|
56
|
-
const compressed = gzipSync(Buffer.from(json, "utf-8"));
|
|
57
|
-
const b64 = compressed.toString("base64url");
|
|
58
|
-
return `https://0.security/r#${b64}`;
|
|
59
|
-
}
|
|
7
|
+
compareFindingsByBusinessPriority,
|
|
8
|
+
getFindingPriority
|
|
9
|
+
} from "./chunk-GXXX7AGH.js";
|
|
60
10
|
|
|
61
|
-
// packages/
|
|
62
|
-
var SEVERITY_STYLE = {
|
|
63
|
-
critical: {
|
|
64
|
-
badge: (s) => source_default.bgRed.white.bold(` ${s} `),
|
|
65
|
-
label: "Critical",
|
|
66
|
-
icon: "\u25CF"
|
|
67
|
-
},
|
|
68
|
-
high: {
|
|
69
|
-
badge: (s) => source_default.bgRedBright.white.bold(` ${s} `),
|
|
70
|
-
label: "High",
|
|
71
|
-
icon: "\u25CF"
|
|
72
|
-
},
|
|
73
|
-
medium: {
|
|
74
|
-
badge: (s) => source_default.bgYellow.black.bold(` ${s} `),
|
|
75
|
-
label: "Medium",
|
|
76
|
-
icon: "\u25CF"
|
|
77
|
-
},
|
|
78
|
-
low: {
|
|
79
|
-
badge: (s) => source_default.bgBlue.white(` ${s} `),
|
|
80
|
-
label: "Low",
|
|
81
|
-
icon: "\u25CB"
|
|
82
|
-
},
|
|
83
|
-
info: {
|
|
84
|
-
badge: (s) => source_default.bgGray.white(` ${s} `),
|
|
85
|
-
label: "Info",
|
|
86
|
-
icon: "\xB7"
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
var SEVERITY_COUNT_COLOR = {
|
|
90
|
-
critical: source_default.red.bold,
|
|
91
|
-
high: source_default.redBright.bold,
|
|
92
|
-
medium: source_default.yellow.bold,
|
|
93
|
-
low: source_default.blue,
|
|
94
|
-
info: source_default.gray
|
|
95
|
-
};
|
|
96
|
-
function formatTerminal(report) {
|
|
97
|
-
const W = 60;
|
|
98
|
-
const lines = [];
|
|
99
|
-
lines.push("");
|
|
100
|
-
if (report.warnings.length > 0) {
|
|
101
|
-
lines.push(` ${source_default.yellow.bold("WARNINGS")}`);
|
|
102
|
-
lines.push("");
|
|
103
|
-
for (const warning of report.warnings) {
|
|
104
|
-
lines.push(
|
|
105
|
-
` ${source_default.yellow.bold("!")} ${source_default.yellow(`[${warning.stage}] ${warning.message}`)}`
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
lines.push("");
|
|
109
|
-
}
|
|
110
|
-
if (report.findings.length === 0) {
|
|
111
|
-
lines.push("");
|
|
112
|
-
if (report.warnings.length > 0 || report.executionSuccessful === false) {
|
|
113
|
-
lines.push(
|
|
114
|
-
` ${source_default.yellow.bold("!")} ${source_default.yellow("No vulnerabilities confirmed. Scan finished with warnings.")}`
|
|
115
|
-
);
|
|
116
|
-
} else if (report.reviewChecks?.some((check) => check.status !== "pass")) {
|
|
117
|
-
lines.push(` ${source_default.yellow.bold("!")} ${source_default.yellow("No security vulnerabilities found. Advisory review checks need attention.")}`);
|
|
118
|
-
} else {
|
|
119
|
-
lines.push(` ${source_default.green.bold("\u2713")} ${source_default.green("No vulnerabilities found.")}`);
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
lines.push(` ${source_default.bold.white("FINDINGS")}`);
|
|
123
|
-
lines.push("");
|
|
124
|
-
const sorted = [...report.findings].sort(
|
|
125
|
-
(a, b) => severityRank(b.severity) - severityRank(a.severity)
|
|
126
|
-
);
|
|
127
|
-
for (const finding of sorted) {
|
|
128
|
-
lines.push(formatFinding(finding));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
if (report.reviewChecks?.length) {
|
|
132
|
-
lines.push("", ` ${source_default.bold.white("REVIEW CHECKS (advisory)")}`, "");
|
|
133
|
-
for (const check of report.reviewChecks) {
|
|
134
|
-
const color = check.status === "pass" ? source_default.green : source_default.yellow;
|
|
135
|
-
lines.push(` ${color(check.status.toUpperCase())} ${check.name} (${check.id})`);
|
|
136
|
-
lines.push(` Reason: ${check.reason}`);
|
|
137
|
-
if (check.fix) lines.push(` Suggested fix: ${check.fix}`);
|
|
138
|
-
lines.push("");
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
lines.push("");
|
|
142
|
-
lines.push(` ${source_default.gray("\u2500".repeat(50))}`);
|
|
143
|
-
lines.push("");
|
|
144
|
-
const { summary } = report;
|
|
145
|
-
const sev = [
|
|
146
|
-
summary.critical > 0 ? source_default.red.bold(`${summary.critical} critical`) : source_default.gray(`${summary.critical} critical`),
|
|
147
|
-
summary.high > 0 ? source_default.hex("#f97316").bold(`${summary.high} high`) : source_default.gray(`${summary.high} high`),
|
|
148
|
-
summary.medium > 0 ? source_default.yellow.bold(`${summary.medium} medium`) : source_default.gray(`${summary.medium} medium`),
|
|
149
|
-
source_default.gray(`${summary.low} low`),
|
|
150
|
-
source_default.gray(`${summary.info} info`)
|
|
151
|
-
].join(source_default.gray(" "));
|
|
152
|
-
lines.push(` ${sev}`);
|
|
153
|
-
lines.push(` ${source_default.white.bold(String(summary.totalFindings))} findings ${source_default.gray("in")} ${source_default.white(formatDuration(report.durationMs))}`);
|
|
154
|
-
lines.push(` ${source_default.gray("Share:")} ${source_default.cyan(buildShareUrl(report))}`);
|
|
155
|
-
lines.push("");
|
|
156
|
-
return lines.join("\n");
|
|
157
|
-
}
|
|
158
|
-
function formatFinding(finding) {
|
|
159
|
-
const style = SEVERITY_STYLE[finding.severity];
|
|
160
|
-
const lines = [];
|
|
161
|
-
lines.push(` ${style.badge(style.label.toUpperCase().padEnd(8))} ${source_default.bold.white(finding.title)}`);
|
|
162
|
-
const meta = [];
|
|
163
|
-
meta.push(`${source_default.gray("Category:")} ${formatCategory(finding.category)}`);
|
|
164
|
-
if (finding.status === "confirmed") {
|
|
165
|
-
meta.push(source_default.green("\u2713 Confirmed"));
|
|
166
|
-
}
|
|
167
|
-
lines.push(` ${" ".repeat(11)}${meta.join(source_default.gray(" \xB7 "))}`);
|
|
168
|
-
if (finding.description) {
|
|
169
|
-
const desc = finding.description.length > 100 ? finding.description.slice(0, 97) + "..." : finding.description;
|
|
170
|
-
lines.push(` ${" ".repeat(11)}${source_default.gray(desc)}`);
|
|
171
|
-
}
|
|
172
|
-
if (finding.evidence.analysis) {
|
|
173
|
-
const ev = finding.evidence.analysis.length > 100 ? finding.evidence.analysis.slice(0, 97) + "..." : finding.evidence.analysis;
|
|
174
|
-
lines.push(` ${" ".repeat(11)}${source_default.gray.italic("\u21B3 " + ev)}`);
|
|
175
|
-
}
|
|
176
|
-
lines.push("");
|
|
177
|
-
return lines.join("\n");
|
|
178
|
-
}
|
|
179
|
-
function formatCategory(cat) {
|
|
180
|
-
return cat.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
181
|
-
}
|
|
182
|
-
function formatDuration(ms) {
|
|
183
|
-
if (ms < 1e3) return `${ms}ms`;
|
|
184
|
-
return `${(ms / 1e3).toFixed(1)}s`;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// packages/cli/src/formatters/json.ts
|
|
188
|
-
function formatJson(report) {
|
|
189
|
-
return JSON.stringify(report, null, 2);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// packages/cli/src/formatters/markdown.ts
|
|
11
|
+
// packages/core/dist/report-formatters/markdown.js
|
|
193
12
|
var EVIDENCE_BLOCK_LIMIT = 4e3;
|
|
194
13
|
var POC_STEP_LIMIT = 20;
|
|
195
14
|
function formatMarkdown(report) {
|
|
@@ -207,11 +26,20 @@ function formatMarkdown(report) {
|
|
|
207
26
|
lines.push("");
|
|
208
27
|
lines.push(`- **Attacks:** ${report.summary.totalAttacks}`);
|
|
209
28
|
lines.push(`- **Findings:** ${report.summary.totalFindings}`);
|
|
29
|
+
for (const label of ["Urgent", "High", "Moderate", "Low", "Not assessed"]) {
|
|
30
|
+
const count = report.findings.filter((finding) => getFindingPriority(finding).label === label).length;
|
|
31
|
+
if (count)
|
|
32
|
+
lines.push(`- **Business priority \u2014 ${label}:** ${count}`);
|
|
33
|
+
}
|
|
34
|
+
lines.push("", "### Technical severity", "");
|
|
210
35
|
if (report.summary.critical > 0)
|
|
211
36
|
lines.push(`- **Critical:** ${report.summary.critical}`);
|
|
212
|
-
if (report.summary.high > 0)
|
|
213
|
-
|
|
214
|
-
if (report.summary.
|
|
37
|
+
if (report.summary.high > 0)
|
|
38
|
+
lines.push(`- **High:** ${report.summary.high}`);
|
|
39
|
+
if (report.summary.medium > 0)
|
|
40
|
+
lines.push(`- **Medium:** ${report.summary.medium}`);
|
|
41
|
+
if (report.summary.low > 0)
|
|
42
|
+
lines.push(`- **Low:** ${report.summary.low}`);
|
|
215
43
|
lines.push("");
|
|
216
44
|
if (report.warnings.length > 0) {
|
|
217
45
|
lines.push("## Warnings");
|
|
@@ -228,22 +56,21 @@ function formatMarkdown(report) {
|
|
|
228
56
|
lines.push(`### ${check.status.toUpperCase()}: ${literal(check.name)}`, "");
|
|
229
57
|
lines.push(`- **ID:** ${literal(check.id)}`);
|
|
230
58
|
lines.push(`- **Reason:** ${literal(check.reason)}`);
|
|
231
|
-
if (check.fix)
|
|
59
|
+
if (check.fix)
|
|
60
|
+
lines.push(`- **Suggested fix:** ${literal(check.fix)}`);
|
|
232
61
|
lines.push("");
|
|
233
62
|
}
|
|
234
63
|
}
|
|
235
64
|
if (report.findings.length > 0) {
|
|
236
65
|
lines.push("## Findings");
|
|
237
66
|
lines.push("");
|
|
238
|
-
for (const finding of report.findings) {
|
|
239
|
-
lines.push(
|
|
67
|
+
for (const finding of [...report.findings].sort(compareFindingsByBusinessPriority)) {
|
|
68
|
+
lines.push(formatFinding(finding));
|
|
240
69
|
}
|
|
241
70
|
} else {
|
|
242
71
|
lines.push(report.warnings.length > 0 ? "## No Confirmed Vulnerabilities" : "## No Vulnerabilities Found");
|
|
243
72
|
lines.push("");
|
|
244
|
-
lines.push(
|
|
245
|
-
report.warnings.length > 0 ? "The scanner did not confirm vulnerabilities, but target validation or probe execution produced warnings." : report.reviewChecks?.length ? "No security vulnerabilities were found in the inspected scope. Advisory review-check results are reported separately above." : "The target passed all tests."
|
|
246
|
-
);
|
|
73
|
+
lines.push(report.warnings.length > 0 ? "The scanner did not confirm vulnerabilities, but target validation or probe execution produced warnings." : report.reviewChecks?.length ? "No security vulnerabilities were found in the inspected scope. Advisory review-check results are reported separately above." : "The target passed all tests.");
|
|
247
74
|
}
|
|
248
75
|
return lines.join("\n");
|
|
249
76
|
}
|
|
@@ -261,9 +88,7 @@ function evidenceBlock(label, raw) {
|
|
|
261
88
|
lines.push(body);
|
|
262
89
|
lines.push("```");
|
|
263
90
|
if (truncated) {
|
|
264
|
-
lines.push(
|
|
265
|
-
`_Truncated for readability: ${raw.length - EVIDENCE_BLOCK_LIMIT} of ${raw.length} characters not shown. Use \`--format json\` or \`--format sarif\` for the complete evidence._`
|
|
266
|
-
);
|
|
91
|
+
lines.push(`_Truncated for readability: ${raw.length - EVIDENCE_BLOCK_LIMIT} of ${raw.length} characters not shown. Use \`--format json\` or \`--format sarif\` for the complete evidence._`);
|
|
267
92
|
}
|
|
268
93
|
return lines;
|
|
269
94
|
}
|
|
@@ -274,7 +99,8 @@ function remediationSection(remediation) {
|
|
|
274
99
|
lines.push(remediation.summary);
|
|
275
100
|
if (remediation.steps.length > 0) {
|
|
276
101
|
lines.push("");
|
|
277
|
-
for (const step of remediation.steps)
|
|
102
|
+
for (const step of remediation.steps)
|
|
103
|
+
lines.push(`1. ${step}`);
|
|
278
104
|
}
|
|
279
105
|
if (remediation.codeExample) {
|
|
280
106
|
const { before, after, language } = remediation.codeExample;
|
|
@@ -298,7 +124,8 @@ function remediationSection(remediation) {
|
|
|
298
124
|
if (remediation.references.length > 0) {
|
|
299
125
|
lines.push("");
|
|
300
126
|
lines.push("References:");
|
|
301
|
-
for (const ref of remediation.references)
|
|
127
|
+
for (const ref of remediation.references)
|
|
128
|
+
lines.push(`- ${ref}`);
|
|
302
129
|
}
|
|
303
130
|
lines.push("");
|
|
304
131
|
return lines;
|
|
@@ -313,18 +140,19 @@ function reproductionSection(steps) {
|
|
|
313
140
|
});
|
|
314
141
|
if (steps.length > shown.length) {
|
|
315
142
|
lines.push("");
|
|
316
|
-
lines.push(
|
|
317
|
-
`_${steps.length - shown.length} further step(s) omitted; the full graph is in \`--format json\`._`
|
|
318
|
-
);
|
|
143
|
+
lines.push(`_${steps.length - shown.length} further step(s) omitted; the full graph is in \`--format json\`._`);
|
|
319
144
|
}
|
|
320
145
|
lines.push("");
|
|
321
146
|
return lines;
|
|
322
147
|
}
|
|
323
|
-
function
|
|
148
|
+
function formatFinding(finding) {
|
|
324
149
|
const lines = [];
|
|
325
|
-
const
|
|
326
|
-
lines.push(`### ${
|
|
150
|
+
const priority = getFindingPriority(finding);
|
|
151
|
+
lines.push(`### [${priority.label}] ${finding.title}`);
|
|
327
152
|
lines.push("");
|
|
153
|
+
lines.push(`- **Business priority:** ${priority.label}`);
|
|
154
|
+
lines.push(`- **Business impact rationale:** ${priority.rationale}`);
|
|
155
|
+
lines.push(`- **Technical severity:** ${finding.severity}`);
|
|
328
156
|
lines.push(`- **Category:** ${finding.category}`);
|
|
329
157
|
lines.push(`- **Status:** ${finding.status}`);
|
|
330
158
|
if (finding.cvssScore !== void 0 || finding.cvssVector) {
|
|
@@ -360,18 +188,8 @@ function formatFinding2(finding) {
|
|
|
360
188
|
lines.push("");
|
|
361
189
|
return lines.join("\n");
|
|
362
190
|
}
|
|
363
|
-
function severityBadge(severity) {
|
|
364
|
-
const badges = {
|
|
365
|
-
critical: "[CRITICAL]",
|
|
366
|
-
high: "[HIGH]",
|
|
367
|
-
medium: "[MEDIUM]",
|
|
368
|
-
low: "[LOW]",
|
|
369
|
-
info: "[INFO]"
|
|
370
|
-
};
|
|
371
|
-
return badges[severity] ?? `[${severity.toUpperCase()}]`;
|
|
372
|
-
}
|
|
373
191
|
|
|
374
|
-
// packages/
|
|
192
|
+
// packages/core/dist/report-formatters/html.js
|
|
375
193
|
function escapeHtml(str) {
|
|
376
194
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
377
195
|
}
|
|
@@ -382,25 +200,29 @@ var SEVERITY_COLORS = {
|
|
|
382
200
|
low: { bg: "#1d4ed8", text: "#ffffff", accent: "#60a5fa" },
|
|
383
201
|
info: { bg: "#4b5563", text: "#ffffff", accent: "#9ca3af" }
|
|
384
202
|
};
|
|
385
|
-
var SEVERITY_ORDER = Object.keys(SEVERITY_RANK).sort(
|
|
386
|
-
|
|
387
|
-
);
|
|
388
|
-
function formatCategory2(cat) {
|
|
203
|
+
var SEVERITY_ORDER = Object.keys(SEVERITY_RANK).sort((a, b) => SEVERITY_RANK[b] - SEVERITY_RANK[a]);
|
|
204
|
+
function formatCategory(cat) {
|
|
389
205
|
return cat.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
390
206
|
}
|
|
391
|
-
function
|
|
392
|
-
if (ms < 1e3)
|
|
207
|
+
function formatDuration(ms) {
|
|
208
|
+
if (ms < 1e3)
|
|
209
|
+
return `${ms}ms`;
|
|
393
210
|
return `${(ms / 1e3).toFixed(1)}s`;
|
|
394
211
|
}
|
|
395
212
|
function riskLevel(summary) {
|
|
396
|
-
if (summary.critical > 0)
|
|
397
|
-
|
|
398
|
-
if (summary.
|
|
399
|
-
|
|
213
|
+
if (summary.critical > 0)
|
|
214
|
+
return { label: "CRITICAL", color: "#ef4444" };
|
|
215
|
+
if (summary.high > 0)
|
|
216
|
+
return { label: "HIGH", color: "#f97316" };
|
|
217
|
+
if (summary.medium > 0)
|
|
218
|
+
return { label: "MEDIUM", color: "#eab308" };
|
|
219
|
+
if (summary.low > 0)
|
|
220
|
+
return { label: "LOW", color: "#3b82f6" };
|
|
400
221
|
return { label: "CLEAN", color: "#22c55e" };
|
|
401
222
|
}
|
|
402
223
|
function renderFinding(finding, index) {
|
|
403
224
|
const sev = SEVERITY_COLORS[finding.severity];
|
|
225
|
+
const priority = getFindingPriority(finding);
|
|
404
226
|
const confidence = finding.confidence != null ? `${Math.round(finding.confidence * 100)}%` : null;
|
|
405
227
|
const cvss = finding.cvssScore != null ? finding.cvssScore.toFixed(1) : null;
|
|
406
228
|
const rank = finding.findingRank != null ? `Rank: ${finding.findingRank}` : null;
|
|
@@ -408,14 +230,17 @@ function renderFinding(finding, index) {
|
|
|
408
230
|
return `
|
|
409
231
|
<div class="finding-card">
|
|
410
232
|
<div class="finding-header">
|
|
411
|
-
<span class="severity-badge" style="background
|
|
233
|
+
<span class="severity-badge" style="background:#1a1a2e;color:#fff">Business priority: ${priority.label}</span>
|
|
412
234
|
<span class="finding-title">${escapeHtml(finding.title)}</span>
|
|
413
235
|
</div>
|
|
236
|
+
<p class="finding-desc"><strong>Business impact rationale:</strong> ${escapeHtml(priority.rationale)}</p>
|
|
414
237
|
<div class="finding-meta">
|
|
415
|
-
<span class="meta-tag">
|
|
238
|
+
<span class="meta-tag" style="background:${sev.bg};color:${sev.text}">Technical severity: ${finding.severity}</span>
|
|
239
|
+
<span class="meta-tag">Category: ${formatCategory(finding.category)}</span>
|
|
416
240
|
${finding.status === "confirmed" ? '<span class="meta-tag confirmed">Confirmed</span>' : `<span class="meta-tag">${escapeHtml(finding.status)}</span>`}
|
|
417
241
|
${confidence ? `<span class="meta-tag">Confidence: ${confidence}</span>` : ""}
|
|
418
242
|
${cvss ? `<span class="meta-tag">CVSS: ${cvss}</span>` : ""}
|
|
243
|
+
${finding.cvssVector ? `<span class="meta-tag">CVSS vector: ${escapeHtml(finding.cvssVector)}</span>` : ""}
|
|
419
244
|
${rank ? `<span class="meta-tag">${rank}</span>` : ""}
|
|
420
245
|
${dedupe ? `<span class="meta-tag">${dedupe}</span>` : ""}
|
|
421
246
|
</div>
|
|
@@ -436,7 +261,8 @@ function renderFinding(finding, index) {
|
|
|
436
261
|
}
|
|
437
262
|
function renderSeverityBar(summary) {
|
|
438
263
|
const total = summary.critical + summary.high + summary.medium + summary.low + summary.info;
|
|
439
|
-
if (total === 0)
|
|
264
|
+
if (total === 0)
|
|
265
|
+
return "";
|
|
440
266
|
const segments = SEVERITY_ORDER.filter((s) => summary[s] > 0).map((s) => {
|
|
441
267
|
const pct = summary[s] / total * 100;
|
|
442
268
|
return `<div class="bar-segment" style="width:${pct}%;background:${SEVERITY_COLORS[s].accent}" title="${summary[s]} ${s}"></div>`;
|
|
@@ -445,9 +271,8 @@ function renderSeverityBar(summary) {
|
|
|
445
271
|
}
|
|
446
272
|
function formatHtml(report) {
|
|
447
273
|
const risk = riskLevel(report.summary);
|
|
448
|
-
const sorted = [...report.findings].sort(
|
|
449
|
-
|
|
450
|
-
);
|
|
274
|
+
const sorted = [...report.findings].sort(compareFindingsByBusinessPriority);
|
|
275
|
+
const businessPriority = sorted[0] ? getFindingPriority(sorted[0]).label : "No findings";
|
|
451
276
|
const findingsHtml = sorted.length > 0 ? sorted.map((f, i) => renderFinding(f, i)).join("\n") : `<div class="no-findings">${report.warnings.length > 0 || report.executionSuccessful === false ? "No vulnerabilities confirmed. Scan finished with warnings." : report.reviewChecks?.length ? "No security vulnerabilities found. See advisory review-check results below." : "No vulnerabilities found. Target passed all tests."}</div>`;
|
|
452
277
|
const warningsHtml = report.warnings.length > 0 ? `<div class="section">
|
|
453
278
|
<h2 class="section-title warning-title">Warnings</h2>
|
|
@@ -736,12 +561,13 @@ function formatHtml(report) {
|
|
|
736
561
|
<div class="brand">ZERO</div>
|
|
737
562
|
<div class="brand-sub">Security Scan Report</div>
|
|
738
563
|
</div>
|
|
739
|
-
<div class="risk-badge" style="background
|
|
564
|
+
<div class="risk-badge" style="background:#1a1a2e;color:#fff">Business priority: ${businessPriority}</div>
|
|
740
565
|
</div>
|
|
741
566
|
<div class="meta-grid">
|
|
742
567
|
<span><strong>Target:</strong> ${escapeHtml(report.target)}</span>
|
|
568
|
+
<span><strong>Technical risk:</strong> ${risk.label}</span>
|
|
743
569
|
<span><strong>Depth:</strong> ${report.scanDepth}</span>
|
|
744
|
-
<span><strong>Duration:</strong> ${
|
|
570
|
+
<span><strong>Duration:</strong> ${formatDuration(report.durationMs)}</span>
|
|
745
571
|
<span><strong>Started:</strong> ${escapeHtml(report.startedAt)}</span>
|
|
746
572
|
</div>
|
|
747
573
|
</div>
|
|
@@ -750,6 +576,11 @@ function formatHtml(report) {
|
|
|
750
576
|
<div class="stat-box">
|
|
751
577
|
<h3>Findings</h3>
|
|
752
578
|
<div class="big-num">${report.summary.totalFindings}</div>
|
|
579
|
+
<div class="sev-counts">${["Urgent", "High", "Moderate", "Low", "Not assessed"].map((label) => {
|
|
580
|
+
const count = report.findings.filter((finding) => getFindingPriority(finding).label === label).length;
|
|
581
|
+
return count ? `<span>Business priority ${label}: ${count}</span>` : "";
|
|
582
|
+
}).join(" ")}</div>
|
|
583
|
+
<h3>Technical severity</h3>
|
|
753
584
|
<div class="sev-counts">
|
|
754
585
|
${sevCountsHtml}
|
|
755
586
|
</div>
|
|
@@ -761,7 +592,7 @@ function formatHtml(report) {
|
|
|
761
592
|
<div class="timeline">
|
|
762
593
|
<div class="timeline-row"><span class="timeline-label">Started</span><span class="timeline-value">${escapeHtml(report.startedAt)}</span></div>
|
|
763
594
|
<div class="timeline-row"><span class="timeline-label">Completed</span><span class="timeline-value">${escapeHtml(report.completedAt)}</span></div>
|
|
764
|
-
<div class="timeline-row"><span class="timeline-label">Duration</span><span class="timeline-value">${
|
|
595
|
+
<div class="timeline-row"><span class="timeline-label">Duration</span><span class="timeline-value">${formatDuration(report.durationMs)}</span></div>
|
|
765
596
|
</div>
|
|
766
597
|
</div>
|
|
767
598
|
</div>
|
|
@@ -782,7 +613,7 @@ function formatHtml(report) {
|
|
|
782
613
|
</html>`;
|
|
783
614
|
}
|
|
784
615
|
|
|
785
|
-
// packages/
|
|
616
|
+
// packages/core/dist/report-formatters/sarif.js
|
|
786
617
|
function severityToLevel(severity) {
|
|
787
618
|
switch (severity) {
|
|
788
619
|
case "critical":
|
|
@@ -822,7 +653,8 @@ function actionSummary(step) {
|
|
|
822
653
|
}
|
|
823
654
|
}
|
|
824
655
|
function pocStepsToCodeFlows(pocSteps, target) {
|
|
825
|
-
if (!pocSteps || pocSteps.length === 0)
|
|
656
|
+
if (!pocSteps || pocSteps.length === 0)
|
|
657
|
+
return void 0;
|
|
826
658
|
return [
|
|
827
659
|
{
|
|
828
660
|
threadFlows: [
|
|
@@ -863,27 +695,48 @@ function findingToResult(finding, target) {
|
|
|
863
695
|
}
|
|
864
696
|
};
|
|
865
697
|
const codeFlows = pocStepsToCodeFlows(finding.pocSteps, target);
|
|
866
|
-
if (codeFlows)
|
|
698
|
+
if (codeFlows)
|
|
699
|
+
result.codeFlows = codeFlows;
|
|
700
|
+
const priority = getFindingPriority(finding);
|
|
867
701
|
result.properties = {
|
|
702
|
+
businessPriority: priority.label,
|
|
703
|
+
businessPriorityRank: priority.rank,
|
|
704
|
+
businessPriorityAssessed: priority.assessed,
|
|
705
|
+
businessPriorityRationale: priority.rationale,
|
|
868
706
|
findingId: finding.id,
|
|
869
707
|
category: finding.category,
|
|
870
708
|
severity: finding.severity,
|
|
871
709
|
status: finding.status,
|
|
872
710
|
evidence: finding.evidence
|
|
873
711
|
};
|
|
874
|
-
if (finding.
|
|
875
|
-
|
|
876
|
-
if (finding.
|
|
877
|
-
|
|
878
|
-
if (finding.
|
|
879
|
-
|
|
880
|
-
if (finding.
|
|
881
|
-
|
|
882
|
-
if (finding.
|
|
883
|
-
|
|
884
|
-
if (finding.
|
|
885
|
-
|
|
886
|
-
if (finding.
|
|
712
|
+
if (finding.impactAssessment)
|
|
713
|
+
result.properties["impactAssessment"] = finding.impactAssessment;
|
|
714
|
+
if (finding.cvssScore !== void 0)
|
|
715
|
+
result.properties["cvssScore"] = finding.cvssScore;
|
|
716
|
+
if (finding.cvssVector)
|
|
717
|
+
result.properties["cvssVector"] = finding.cvssVector;
|
|
718
|
+
if (finding.confidence !== void 0)
|
|
719
|
+
result.properties["confidence"] = finding.confidence;
|
|
720
|
+
if (finding.publishability)
|
|
721
|
+
result.properties["publishability"] = finding.publishability;
|
|
722
|
+
if (finding.noveltyVerdict)
|
|
723
|
+
result.properties["noveltyVerdict"] = finding.noveltyVerdict;
|
|
724
|
+
if (finding.dedupRefs)
|
|
725
|
+
result.properties["dedupRefs"] = finding.dedupRefs;
|
|
726
|
+
if (finding.advisoryMatches)
|
|
727
|
+
result.properties["advisoryMatches"] = finding.advisoryMatches;
|
|
728
|
+
if (finding.verification_result)
|
|
729
|
+
result.properties["verificationResult"] = finding.verification_result;
|
|
730
|
+
if (finding.inlineValidation)
|
|
731
|
+
result.properties["inlineValidation"] = finding.inlineValidation;
|
|
732
|
+
if (finding.supplyChain)
|
|
733
|
+
result.properties["supplyChain"] = finding.supplyChain;
|
|
734
|
+
if (finding.kernelExploit)
|
|
735
|
+
result.properties["kernelExploit"] = finding.kernelExploit;
|
|
736
|
+
if (finding.semanticDedupe)
|
|
737
|
+
result.properties["semanticDedupe"] = finding.semanticDedupe;
|
|
738
|
+
if (finding.findingRank !== void 0)
|
|
739
|
+
result.properties["findingRank"] = finding.findingRank;
|
|
887
740
|
return result;
|
|
888
741
|
}
|
|
889
742
|
function findingToRule(finding) {
|
|
@@ -928,7 +781,7 @@ function formatSarif(report) {
|
|
|
928
781
|
}
|
|
929
782
|
},
|
|
930
783
|
results: [
|
|
931
|
-
...report.findings.map((f) => findingToResult(f, report.target)),
|
|
784
|
+
...[...report.findings].sort(compareFindingsByBusinessPriority).map((f) => findingToResult(f, report.target)),
|
|
932
785
|
...(report.reviewChecks ?? []).map((check) => ({
|
|
933
786
|
ruleId: `review-check/${check.id}`,
|
|
934
787
|
kind: check.status === "pass" ? "pass" : check.status === "issue" ? "fail" : "open",
|
|
@@ -951,7 +804,7 @@ Suggested fix: ${check.fix}` : ""}` },
|
|
|
951
804
|
return JSON.stringify(sarif, null, 2);
|
|
952
805
|
}
|
|
953
806
|
|
|
954
|
-
// packages/
|
|
807
|
+
// packages/core/dist/report-formatters/pdf.js
|
|
955
808
|
import { createRequire } from "node:module";
|
|
956
809
|
import { createWriteStream } from "node:fs";
|
|
957
810
|
var COLORS = {
|
|
@@ -983,10 +836,12 @@ function formatDate(iso) {
|
|
|
983
836
|
minute: "2-digit"
|
|
984
837
|
});
|
|
985
838
|
}
|
|
986
|
-
function
|
|
987
|
-
if (ms < 1e3)
|
|
839
|
+
function formatDuration2(ms) {
|
|
840
|
+
if (ms < 1e3)
|
|
841
|
+
return `${ms}ms`;
|
|
988
842
|
const seconds = Math.floor(ms / 1e3);
|
|
989
|
-
if (seconds < 60)
|
|
843
|
+
if (seconds < 60)
|
|
844
|
+
return `${seconds}s`;
|
|
990
845
|
const minutes = Math.floor(seconds / 60);
|
|
991
846
|
const remainingSec = seconds % 60;
|
|
992
847
|
return `${minutes}m ${remainingSec}s`;
|
|
@@ -996,14 +851,19 @@ function riskScore(summary) {
|
|
|
996
851
|
return Math.min(100, score);
|
|
997
852
|
}
|
|
998
853
|
function riskRating(score) {
|
|
999
|
-
if (score >= 80)
|
|
1000
|
-
|
|
1001
|
-
if (score >=
|
|
1002
|
-
|
|
854
|
+
if (score >= 80)
|
|
855
|
+
return "Critical";
|
|
856
|
+
if (score >= 60)
|
|
857
|
+
return "High";
|
|
858
|
+
if (score >= 30)
|
|
859
|
+
return "Medium";
|
|
860
|
+
if (score >= 10)
|
|
861
|
+
return "Low";
|
|
1003
862
|
return "Informational";
|
|
1004
863
|
}
|
|
1005
864
|
function truncate(text, maxLen) {
|
|
1006
|
-
if (text.length <= maxLen)
|
|
865
|
+
if (text.length <= maxLen)
|
|
866
|
+
return text;
|
|
1007
867
|
return text.slice(0, maxLen - 3) + "...";
|
|
1008
868
|
}
|
|
1009
869
|
async function generatePdfReport(report, outputPath) {
|
|
@@ -1041,10 +901,13 @@ function renderCoverPage(doc, report) {
|
|
|
1041
901
|
doc.fontSize(14).font("Helvetica").fillColor(COLORS.white).text("Automated Penetration Test Report", PAGE_MARGIN, 260, { align: "center" });
|
|
1042
902
|
doc.moveTo(200, 300).lineTo(412, 300).strokeColor(COLORS.accent).lineWidth(2).stroke();
|
|
1043
903
|
doc.fontSize(18).font("Helvetica-Bold").fillColor(COLORS.white).text(report.target, PAGE_MARGIN, 330, { align: "center" });
|
|
1044
|
-
doc.fontSize(12).font("Helvetica").fillColor(COLORS.textLight).text(`Scan Depth: ${report.scanDepth}`, PAGE_MARGIN, 370, { align: "center" }).text(`Date: ${formatDate(report.startedAt)}`, PAGE_MARGIN, 390, { align: "center" }).text(`Duration: ${
|
|
904
|
+
doc.fontSize(12).font("Helvetica").fillColor(COLORS.textLight).text(`Scan Depth: ${report.scanDepth}`, PAGE_MARGIN, 370, { align: "center" }).text(`Date: ${formatDate(report.startedAt)}`, PAGE_MARGIN, 390, { align: "center" }).text(`Duration: ${formatDuration2(report.durationMs)}`, PAGE_MARGIN, 410, { align: "center" });
|
|
905
|
+
const firstFinding = [...report.findings].sort(compareFindingsByBusinessPriority)[0];
|
|
906
|
+
if (firstFinding)
|
|
907
|
+
doc.fontSize(14).font("Helvetica-Bold").fillColor(COLORS.white).text(`Business priority: ${getFindingPriority(firstFinding).label}`, PAGE_MARGIN, 440, { align: "center" });
|
|
1045
908
|
const score = riskScore(report.summary);
|
|
1046
909
|
const rating = riskRating(score);
|
|
1047
|
-
doc.fontSize(14).font("Helvetica-Bold").fillColor(COLORS.accent).text(`Risk Score: ${score}/100 (${rating})`, PAGE_MARGIN, 460, { align: "center" });
|
|
910
|
+
doc.fontSize(14).font("Helvetica-Bold").fillColor(COLORS.accent).text(`Technical Risk Score: ${score}/100 (${rating})`, PAGE_MARGIN, 460, { align: "center" });
|
|
1048
911
|
doc.fontSize(9).font("Helvetica").fillColor(COLORS.textLight).text("CONFIDENTIAL - For authorized recipients only", PAGE_MARGIN, 700, { align: "center" });
|
|
1049
912
|
}
|
|
1050
913
|
function renderExecutiveSummary(doc, report) {
|
|
@@ -1053,19 +916,18 @@ function renderExecutiveSummary(doc, report) {
|
|
|
1053
916
|
const score = riskScore(report.summary);
|
|
1054
917
|
const rating = riskRating(score);
|
|
1055
918
|
const { summary } = report;
|
|
1056
|
-
doc.fontSize(11).font("Helvetica").fillColor(COLORS.text).text(
|
|
1057
|
-
`An automated penetration test was conducted against ${report.target} on ${formatDate(report.startedAt)}. The scan ran for ${formatDuration3(report.durationMs)} at "${report.scanDepth}" depth.`,
|
|
1058
|
-
PAGE_MARGIN,
|
|
1059
|
-
doc.y + 10,
|
|
1060
|
-
{ width: CONTENT_WIDTH }
|
|
1061
|
-
);
|
|
919
|
+
doc.fontSize(11).font("Helvetica").fillColor(COLORS.text).text(`An automated penetration test was conducted against ${report.target} on ${formatDate(report.startedAt)}. The scan ran for ${formatDuration2(report.durationMs)} at "${report.scanDepth}" depth.`, PAGE_MARGIN, doc.y + 10, { width: CONTENT_WIDTH });
|
|
1062
920
|
doc.moveDown(0.5);
|
|
1063
|
-
doc.text(
|
|
1064
|
-
`The overall risk score is ${score}/100 (${rating}). A total of ${summary.totalFindings} finding(s) were identified across ${summary.totalAttacks} attack(s).`,
|
|
1065
|
-
{ width: CONTENT_WIDTH }
|
|
1066
|
-
);
|
|
921
|
+
doc.text(`The technical risk score is ${score}/100 (${rating}). A total of ${summary.totalFindings} finding(s) were identified across ${summary.totalAttacks} attack(s).`, { width: CONTENT_WIDTH });
|
|
1067
922
|
doc.moveDown(1);
|
|
1068
|
-
subSectionTitle(doc, "
|
|
923
|
+
subSectionTitle(doc, "Business priority");
|
|
924
|
+
for (const label of ["Urgent", "High", "Moderate", "Low", "Not assessed"]) {
|
|
925
|
+
const count = report.findings.filter((finding) => getFindingPriority(finding).label === label).length;
|
|
926
|
+
if (count)
|
|
927
|
+
doc.fontSize(10).font("Helvetica").fillColor(COLORS.text).text(`${label}: ${count}`);
|
|
928
|
+
}
|
|
929
|
+
doc.moveDown(0.5);
|
|
930
|
+
subSectionTitle(doc, "Findings by Technical Severity");
|
|
1069
931
|
const severities = ["critical", "high", "medium", "low", "info"];
|
|
1070
932
|
const sevCounts = {
|
|
1071
933
|
critical: summary.critical,
|
|
@@ -1076,7 +938,8 @@ function renderExecutiveSummary(doc, report) {
|
|
|
1076
938
|
};
|
|
1077
939
|
for (const sev of severities) {
|
|
1078
940
|
const count = sevCounts[sev];
|
|
1079
|
-
if (count === 0)
|
|
941
|
+
if (count === 0)
|
|
942
|
+
continue;
|
|
1080
943
|
const barWidth = Math.max(4, count / Math.max(summary.totalFindings, 1) * (CONTENT_WIDTH - 140));
|
|
1081
944
|
const y = doc.y;
|
|
1082
945
|
doc.fontSize(10).font("Helvetica-Bold").fillColor(COLORS.severity[sev]).text(severityLabel(sev), PAGE_MARGIN, y, { width: 70 });
|
|
@@ -1094,22 +957,21 @@ function renderExecutiveSummary(doc, report) {
|
|
|
1094
957
|
}
|
|
1095
958
|
}
|
|
1096
959
|
function renderFindingsTable(doc, report) {
|
|
1097
|
-
if (report.findings.length === 0)
|
|
960
|
+
if (report.findings.length === 0)
|
|
961
|
+
return;
|
|
1098
962
|
doc.addPage();
|
|
1099
963
|
sectionTitle(doc, "Findings Overview");
|
|
1100
|
-
const colWidths = { severity:
|
|
964
|
+
const colWidths = { severity: 90, title: 210, category: 105, status: 80 };
|
|
1101
965
|
const tableX = PAGE_MARGIN;
|
|
1102
966
|
const headerY = doc.y + 5;
|
|
1103
967
|
doc.rect(tableX, headerY, CONTENT_WIDTH, 20).fill(COLORS.primary);
|
|
1104
968
|
doc.fontSize(9).font("Helvetica-Bold").fillColor(COLORS.white);
|
|
1105
|
-
doc.text("
|
|
969
|
+
doc.text("Business priority", tableX + 5, headerY + 5, { width: colWidths.severity });
|
|
1106
970
|
doc.text("Title", tableX + colWidths.severity + 5, headerY + 5, { width: colWidths.title });
|
|
1107
971
|
doc.text("Category", tableX + colWidths.severity + colWidths.title + 5, headerY + 5, { width: colWidths.category });
|
|
1108
972
|
doc.text("Status", tableX + colWidths.severity + colWidths.title + colWidths.category + 5, headerY + 5, { width: colWidths.status });
|
|
1109
973
|
doc.y = headerY + 22;
|
|
1110
|
-
const sorted = [...report.findings].sort(
|
|
1111
|
-
(a, b) => severityRank(b.severity) - severityRank(a.severity)
|
|
1112
|
-
);
|
|
974
|
+
const sorted = [...report.findings].sort(compareFindingsByBusinessPriority);
|
|
1113
975
|
for (let i = 0; i < sorted.length; i++) {
|
|
1114
976
|
const finding = sorted[i];
|
|
1115
977
|
const rowY = doc.y;
|
|
@@ -1121,7 +983,7 @@ function renderFindingsTable(doc, report) {
|
|
|
1121
983
|
doc.rect(tableX, rowY, CONTENT_WIDTH, 18).fill(COLORS.background);
|
|
1122
984
|
}
|
|
1123
985
|
doc.fontSize(8).font("Helvetica");
|
|
1124
|
-
doc.fillColor(COLORS.
|
|
986
|
+
doc.fillColor(COLORS.text).font("Helvetica-Bold").text(getFindingPriority(finding).label, tableX + 5, rowY + 5, { width: colWidths.severity });
|
|
1125
987
|
doc.fillColor(COLORS.text).font("Helvetica").text(truncate(finding.title, 45), tableX + colWidths.severity + 5, rowY + 5, { width: colWidths.title });
|
|
1126
988
|
doc.text(finding.category, tableX + colWidths.severity + colWidths.title + 5, rowY + 5, { width: colWidths.category });
|
|
1127
989
|
doc.text(finding.status, tableX + colWidths.severity + colWidths.title + colWidths.category + 5, rowY + 5, { width: colWidths.status });
|
|
@@ -1129,16 +991,19 @@ function renderFindingsTable(doc, report) {
|
|
|
1129
991
|
}
|
|
1130
992
|
}
|
|
1131
993
|
function renderFindingDetails(doc, report) {
|
|
1132
|
-
if (report.findings.length === 0)
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
);
|
|
994
|
+
if (report.findings.length === 0)
|
|
995
|
+
return;
|
|
996
|
+
const sorted = [...report.findings].sort(compareFindingsByBusinessPriority);
|
|
1136
997
|
for (const finding of sorted) {
|
|
1137
998
|
doc.addPage();
|
|
1138
999
|
sectionTitle(doc, finding.title);
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1000
|
+
const priority = getFindingPriority(finding);
|
|
1001
|
+
doc.fontSize(11).font("Helvetica-Bold").fillColor(COLORS.text).text(`Business priority: ${priority.label}`);
|
|
1002
|
+
doc.fontSize(10).font("Helvetica").text(`Business impact rationale: ${priority.rationale}`, { width: CONTENT_WIDTH });
|
|
1003
|
+
doc.moveDown(0.5);
|
|
1004
|
+
doc.fontSize(10).font("Helvetica-Bold").fillColor(COLORS.severity[finding.severity]).text(`Technical severity: ${severityLabel(finding.severity)}`, PAGE_MARGIN, doc.y, { continued: true }).fillColor(COLORS.textLight).font("Helvetica").text(` | ${finding.category} | ${finding.status} | ID: ${finding.id}`);
|
|
1005
|
+
if (finding.cvssScore != null || finding.cvssVector) {
|
|
1006
|
+
doc.fontSize(9).fillColor(COLORS.text).text(`CVSS Score: ${finding.cvssScore ?? "Not provided"}${finding.cvssVector ? ` (${finding.cvssVector})` : ""}`, PAGE_MARGIN, doc.y + 2);
|
|
1142
1007
|
}
|
|
1143
1008
|
doc.moveDown(1);
|
|
1144
1009
|
subSectionTitle(doc, "Description");
|
|
@@ -1161,26 +1026,24 @@ function renderFindingDetails(doc, report) {
|
|
|
1161
1026
|
}
|
|
1162
1027
|
}
|
|
1163
1028
|
function renderReviewChecks(doc, report) {
|
|
1164
|
-
if (!report.reviewChecks?.length)
|
|
1029
|
+
if (!report.reviewChecks?.length)
|
|
1030
|
+
return;
|
|
1165
1031
|
doc.addPage();
|
|
1166
1032
|
sectionTitle(doc, "Review Checks (Advisory)");
|
|
1167
1033
|
for (const check of report.reviewChecks) {
|
|
1168
|
-
if (doc.y > doc.page.height - PAGE_MARGIN - 80)
|
|
1034
|
+
if (doc.y > doc.page.height - PAGE_MARGIN - 80)
|
|
1035
|
+
doc.addPage();
|
|
1169
1036
|
subSectionTitle(doc, `${check.status.toUpperCase()}: ${check.name}`);
|
|
1170
1037
|
doc.fontSize(10).font("Helvetica").fillColor(COLORS.text).text(`ID: ${check.id}`, PAGE_MARGIN, doc.y, { width: CONTENT_WIDTH }).text(`Reason: ${check.reason}`, { width: CONTENT_WIDTH });
|
|
1171
|
-
if (check.fix)
|
|
1038
|
+
if (check.fix)
|
|
1039
|
+
doc.text(`Suggested fix: ${check.fix}`, { width: CONTENT_WIDTH });
|
|
1172
1040
|
doc.moveDown(1);
|
|
1173
1041
|
}
|
|
1174
1042
|
}
|
|
1175
1043
|
function renderMethodology(doc, report) {
|
|
1176
1044
|
doc.addPage();
|
|
1177
1045
|
sectionTitle(doc, "Methodology");
|
|
1178
|
-
doc.fontSize(10).font("Helvetica").fillColor(COLORS.text).text(
|
|
1179
|
-
"This report was generated by 0, a fully autonomous agentic pentesting framework. The tool uses AI-driven attack generation and multi-agent consensus verification to identify security vulnerabilities.",
|
|
1180
|
-
PAGE_MARGIN,
|
|
1181
|
-
doc.y + 10,
|
|
1182
|
-
{ width: CONTENT_WIDTH }
|
|
1183
|
-
);
|
|
1046
|
+
doc.fontSize(10).font("Helvetica").fillColor(COLORS.text).text("This report was generated by 0, a fully autonomous agentic pentesting framework. The tool uses AI-driven attack generation and multi-agent consensus verification to identify security vulnerabilities.", PAGE_MARGIN, doc.y + 10, { width: CONTENT_WIDTH });
|
|
1184
1047
|
doc.moveDown(1);
|
|
1185
1048
|
subSectionTitle(doc, "Scan Parameters");
|
|
1186
1049
|
const params = [
|
|
@@ -1188,7 +1051,7 @@ function renderMethodology(doc, report) {
|
|
|
1188
1051
|
["Scan Depth", report.scanDepth],
|
|
1189
1052
|
["Started", formatDate(report.startedAt)],
|
|
1190
1053
|
["Completed", formatDate(report.completedAt)],
|
|
1191
|
-
["Duration",
|
|
1054
|
+
["Duration", formatDuration2(report.durationMs)],
|
|
1192
1055
|
["Total Attacks", String(report.summary.totalAttacks)],
|
|
1193
1056
|
["Total Findings", String(report.summary.totalFindings)]
|
|
1194
1057
|
];
|
|
@@ -1211,12 +1074,7 @@ function renderMethodology(doc, report) {
|
|
|
1211
1074
|
}
|
|
1212
1075
|
doc.moveDown(1);
|
|
1213
1076
|
subSectionTitle(doc, "Disclaimer");
|
|
1214
|
-
doc.fontSize(9).font("Helvetica").fillColor(COLORS.textLight).text(
|
|
1215
|
-
"This automated scan provides a point-in-time assessment. It may not identify all vulnerabilities. Results should be validated by security professionals. The findings do not guarantee the absence of other security issues. This report is confidential and intended for authorized recipients only.",
|
|
1216
|
-
PAGE_MARGIN,
|
|
1217
|
-
doc.y,
|
|
1218
|
-
{ width: CONTENT_WIDTH }
|
|
1219
|
-
);
|
|
1077
|
+
doc.fontSize(9).font("Helvetica").fillColor(COLORS.textLight).text("This automated scan provides a point-in-time assessment. It may not identify all vulnerabilities. Results should be validated by security professionals. The findings do not guarantee the absence of other security issues. This report is confidential and intended for authorized recipients only.", PAGE_MARGIN, doc.y, { width: CONTENT_WIDTH });
|
|
1220
1078
|
}
|
|
1221
1079
|
function sectionTitle(doc, title) {
|
|
1222
1080
|
doc.fontSize(20).font("Helvetica-Bold").fillColor(COLORS.primary).text(title, PAGE_MARGIN, PAGE_MARGIN);
|
|
@@ -1235,425 +1093,31 @@ function codeBlock(doc, text) {
|
|
|
1235
1093
|
width: CONTENT_WIDTH - 20
|
|
1236
1094
|
});
|
|
1237
1095
|
const blockHeight = Math.min(textHeight + 16, 300);
|
|
1238
|
-
|
|
1239
|
-
if (y + blockHeight > 740) {
|
|
1096
|
+
if (doc.y + blockHeight > 740) {
|
|
1240
1097
|
doc.addPage();
|
|
1241
1098
|
}
|
|
1242
|
-
|
|
1243
|
-
doc.
|
|
1099
|
+
const blockY = doc.y;
|
|
1100
|
+
doc.rect(PAGE_MARGIN, blockY, CONTENT_WIDTH, blockHeight).fill("#f1f3f5");
|
|
1101
|
+
doc.fontSize(8).font("Courier").fillColor(COLORS.text).text(displayText, PAGE_MARGIN + 10, blockY + 8, {
|
|
1244
1102
|
width: CONTENT_WIDTH - 20,
|
|
1245
1103
|
height: blockHeight - 16,
|
|
1246
1104
|
ellipsis: true
|
|
1247
1105
|
});
|
|
1248
|
-
doc.y =
|
|
1106
|
+
doc.y = blockY + blockHeight + 4;
|
|
1249
1107
|
}
|
|
1250
1108
|
function renderFooters(doc) {
|
|
1251
1109
|
const pageCount = doc.bufferedPageRange().count;
|
|
1252
1110
|
for (let i = 0; i < pageCount; i++) {
|
|
1253
1111
|
doc.switchToPage(i);
|
|
1254
|
-
if (i === 0)
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
PAGE_MARGIN,
|
|
1258
|
-
760,
|
|
1259
|
-
{ width: CONTENT_WIDTH, align: "center" }
|
|
1260
|
-
);
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
// packages/cli/src/formatters/replay.ts
|
|
1265
|
-
function sleep(ms) {
|
|
1266
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1267
|
-
}
|
|
1268
|
-
function stripAnsi(s) {
|
|
1269
|
-
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
1270
|
-
}
|
|
1271
|
-
var BOX_WIDTH = 55;
|
|
1272
|
-
function boxTop(label) {
|
|
1273
|
-
const inner = ` ${label} `;
|
|
1274
|
-
const remaining = BOX_WIDTH - inner.length - 1;
|
|
1275
|
-
return source_default.dim("\u250C\u2500 ") + source_default.bold.white(label) + source_default.dim(" " + "\u2500".repeat(Math.max(0, remaining)) + "\u2510");
|
|
1276
|
-
}
|
|
1277
|
-
function boxRow(content) {
|
|
1278
|
-
const visible = stripAnsi(content);
|
|
1279
|
-
const pad = Math.max(0, BOX_WIDTH - visible.length - 2);
|
|
1280
|
-
return source_default.dim("\u2502") + " " + content + " ".repeat(pad) + source_default.dim("\u2502");
|
|
1281
|
-
}
|
|
1282
|
-
function boxBottom(withArrow) {
|
|
1283
|
-
if (withArrow) {
|
|
1284
|
-
const half = Math.floor((BOX_WIDTH - 1) / 2);
|
|
1285
|
-
const rest = BOX_WIDTH - half - 1;
|
|
1286
|
-
return source_default.dim("\u2514" + "\u2500".repeat(half) + "\u2534" + "\u2500".repeat(rest) + "\u2518");
|
|
1287
|
-
}
|
|
1288
|
-
return source_default.dim("\u2514" + "\u2500".repeat(BOX_WIDTH) + "\u2518");
|
|
1289
|
-
}
|
|
1290
|
-
function connector() {
|
|
1291
|
-
const half = Math.floor((BOX_WIDTH + 1) / 2);
|
|
1292
|
-
return " ".repeat(half) + source_default.dim("\u25BC");
|
|
1293
|
-
}
|
|
1294
|
-
function outcomeLabel(outcome) {
|
|
1295
|
-
switch (outcome) {
|
|
1296
|
-
case "vulnerable":
|
|
1297
|
-
return source_default.red.bold("VULNERABLE");
|
|
1298
|
-
case "leaked":
|
|
1299
|
-
return source_default.red.bold("LEAKED");
|
|
1300
|
-
case "bypassed":
|
|
1301
|
-
return source_default.red.bold("BYPASSED");
|
|
1302
|
-
case "safe":
|
|
1303
|
-
return source_default.green("SAFE");
|
|
1304
|
-
case "error":
|
|
1305
|
-
return source_default.yellow("ERROR");
|
|
1306
|
-
default:
|
|
1307
|
-
return source_default.gray(outcome.toUpperCase());
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
function buildReplayLines(data) {
|
|
1311
|
-
const lines = [];
|
|
1312
|
-
const FAST = 50;
|
|
1313
|
-
const MED = 75;
|
|
1314
|
-
const SLOW = 100;
|
|
1315
|
-
const PAUSE = 200;
|
|
1316
|
-
lines.push({ text: boxTop("DISCOVER"), delay: PAUSE });
|
|
1317
|
-
if (data.targetInfo) {
|
|
1318
|
-
const t = data.targetInfo;
|
|
1319
|
-
const truncUrl = t.url.length > 30 ? t.url.slice(0, 27) + "..." : t.url;
|
|
1320
|
-
lines.push({
|
|
1321
|
-
text: boxRow(
|
|
1322
|
-
source_default.dim("\u25B8") + " " + source_default.white(`GET ${truncUrl}`) + source_default.dim(" \u2192 ") + source_default.white(`200`) + source_default.gray(` (${t.type} endpoint detected)`)
|
|
1323
|
-
),
|
|
1324
|
-
delay: MED
|
|
1325
|
-
});
|
|
1326
|
-
if (t.systemPrompt) {
|
|
1327
|
-
lines.push({
|
|
1328
|
-
text: boxRow(
|
|
1329
|
-
source_default.dim("\u25B8") + " " + source_default.white(`System prompt extracted`) + source_default.gray(` (${t.systemPrompt.length} chars)`)
|
|
1330
|
-
),
|
|
1331
|
-
delay: MED
|
|
1332
|
-
});
|
|
1333
|
-
}
|
|
1334
|
-
if (t.detectedFeatures && t.detectedFeatures.length > 0) {
|
|
1335
|
-
lines.push({
|
|
1336
|
-
text: boxRow(
|
|
1337
|
-
source_default.dim("\u25B8") + " " + source_default.white(`${t.detectedFeatures.length} features detected: `) + source_default.gray(t.detectedFeatures.slice(0, 3).join(", "))
|
|
1338
|
-
),
|
|
1339
|
-
delay: MED
|
|
1340
|
-
});
|
|
1341
|
-
}
|
|
1342
|
-
if (t.endpoints && t.endpoints.length > 0) {
|
|
1343
|
-
lines.push({
|
|
1344
|
-
text: boxRow(
|
|
1345
|
-
source_default.dim("\u25B8") + " " + source_default.white(`${t.endpoints.length} endpoints found`)
|
|
1346
|
-
),
|
|
1347
|
-
delay: MED
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
} else {
|
|
1351
|
-
const truncTarget = data.target.length > 30 ? data.target.slice(0, 27) + "..." : data.target;
|
|
1352
|
-
lines.push({
|
|
1353
|
-
text: boxRow(
|
|
1354
|
-
source_default.dim("\u25B8") + " " + source_default.white(`GET ${truncTarget}`) + source_default.dim(" \u2192 ") + source_default.white("200") + source_default.gray(" (LLM API detected)")
|
|
1355
|
-
),
|
|
1356
|
-
delay: MED
|
|
1357
|
-
});
|
|
1358
|
-
}
|
|
1359
|
-
lines.push({ text: boxBottom(true), delay: FAST });
|
|
1360
|
-
lines.push({ text: connector(), delay: PAUSE });
|
|
1361
|
-
lines.push({ text: boxTop("ATTACK"), delay: PAUSE });
|
|
1362
|
-
const vulnerableFindings = data.findings.filter(
|
|
1363
|
-
(f) => f.status !== "false-positive"
|
|
1364
|
-
);
|
|
1365
|
-
if (vulnerableFindings.length > 0) {
|
|
1366
|
-
for (const finding of vulnerableFindings.slice(0, 8)) {
|
|
1367
|
-
const truncTitle = finding.title.length > 30 ? finding.title.slice(0, 27) + "..." : finding.title;
|
|
1368
|
-
let outcome = "VULNERABLE";
|
|
1369
|
-
const cat = finding.category;
|
|
1370
|
-
if (cat === "system-prompt-extraction") outcome = "LEAKED";
|
|
1371
|
-
if (cat === "jailbreak") outcome = "BYPASSED";
|
|
1372
|
-
lines.push({
|
|
1373
|
-
text: boxRow(
|
|
1374
|
-
source_default.dim("\u25B8") + " " + source_default.white(truncTitle) + source_default.dim(" \u2192 ") + " " + outcomeLabel(outcome.toLowerCase())
|
|
1375
|
-
),
|
|
1376
|
-
delay: MED
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
if (vulnerableFindings.length > 8) {
|
|
1380
|
-
lines.push({
|
|
1381
|
-
text: boxRow(
|
|
1382
|
-
source_default.gray(
|
|
1383
|
-
` ... and ${vulnerableFindings.length - 8} more attacks`
|
|
1384
|
-
)
|
|
1385
|
-
),
|
|
1386
|
-
delay: FAST
|
|
1387
|
-
});
|
|
1388
|
-
}
|
|
1389
|
-
} else {
|
|
1390
|
-
lines.push({
|
|
1391
|
-
text: boxRow(
|
|
1392
|
-
source_default.dim("\u25B8") + " " + source_default.white(`${data.summary.totalAttacks} probes executed`) + source_default.dim(" \u2192 ") + source_default.green("ALL SAFE")
|
|
1393
|
-
),
|
|
1394
|
-
delay: MED
|
|
1395
|
-
});
|
|
1396
|
-
}
|
|
1397
|
-
lines.push({ text: boxBottom(true), delay: FAST });
|
|
1398
|
-
lines.push({ text: connector(), delay: PAUSE });
|
|
1399
|
-
lines.push({ text: boxTop("VERIFY"), delay: PAUSE });
|
|
1400
|
-
const confirmed = data.findings.filter(
|
|
1401
|
-
(f) => f.status === "confirmed" || f.status === "verified" || f.status === "scored" || f.status === "reported"
|
|
1402
|
-
);
|
|
1403
|
-
const falsePositives = data.findings.filter(
|
|
1404
|
-
(f) => f.status === "false-positive"
|
|
1405
|
-
);
|
|
1406
|
-
if (confirmed.length > 0 || data.findings.length > 0) {
|
|
1407
|
-
const reproduced = confirmed.length > 0 ? confirmed.length : data.findings.length;
|
|
1408
|
-
lines.push({
|
|
1409
|
-
text: boxRow(
|
|
1410
|
-
source_default.green("\u2713") + " " + source_default.white(`Reproduced ${reproduced}/${reproduced} findings`)
|
|
1411
|
-
),
|
|
1412
|
-
delay: MED
|
|
1413
|
-
});
|
|
1112
|
+
if (i === 0)
|
|
1113
|
+
continue;
|
|
1114
|
+
doc.fontSize(8).font("Helvetica").fillColor(COLORS.textLight).text(`0 Pentest Report | Page ${i + 1} of ${pageCount} | CONFIDENTIAL`, PAGE_MARGIN, 760, { width: CONTENT_WIDTH, align: "center" });
|
|
1414
1115
|
}
|
|
1415
|
-
if (falsePositives.length > 0) {
|
|
1416
|
-
lines.push({
|
|
1417
|
-
text: boxRow(
|
|
1418
|
-
source_default.red("\u2717") + " " + source_default.white(`Eliminated ${falsePositives.length} false positives`)
|
|
1419
|
-
),
|
|
1420
|
-
delay: MED
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
if (confirmed.length === 0 && falsePositives.length === 0 && data.findings.length === 0) {
|
|
1424
|
-
lines.push({
|
|
1425
|
-
text: boxRow(source_default.green("\u2713") + " " + source_default.white("No findings to verify")),
|
|
1426
|
-
delay: MED
|
|
1427
|
-
});
|
|
1428
|
-
}
|
|
1429
|
-
lines.push({ text: boxBottom(true), delay: FAST });
|
|
1430
|
-
lines.push({ text: connector(), delay: PAUSE });
|
|
1431
|
-
lines.push({ text: boxTop("REPORT"), delay: PAUSE });
|
|
1432
|
-
const totalFindings = data.summary.totalFindings;
|
|
1433
|
-
if (totalFindings > 0) {
|
|
1434
|
-
const parts = [];
|
|
1435
|
-
if (data.summary.critical > 0) parts.push(source_default.red.bold(`${data.summary.critical} CRITICAL`));
|
|
1436
|
-
if (data.summary.high > 0) parts.push(source_default.redBright(`${data.summary.high} HIGH`));
|
|
1437
|
-
if (data.summary.medium > 0) parts.push(source_default.yellow(`${data.summary.medium} MEDIUM`));
|
|
1438
|
-
if (data.summary.low > 0) parts.push(source_default.blue(`${data.summary.low} LOW`));
|
|
1439
|
-
if (data.summary.info > 0) parts.push(source_default.gray(`${data.summary.info} INFO`));
|
|
1440
|
-
lines.push({
|
|
1441
|
-
text: boxRow(
|
|
1442
|
-
source_default.white(`${totalFindings} verified findings: `) + parts.join(source_default.dim(", "))
|
|
1443
|
-
),
|
|
1444
|
-
delay: MED
|
|
1445
|
-
});
|
|
1446
|
-
} else {
|
|
1447
|
-
lines.push({
|
|
1448
|
-
text: boxRow(source_default.green.bold("0 findings") + source_default.white(" - target appears secure")),
|
|
1449
|
-
delay: MED
|
|
1450
|
-
});
|
|
1451
|
-
}
|
|
1452
|
-
const duration = data.durationMs < 1e3 ? `${data.durationMs}ms` : `${(data.durationMs / 1e3).toFixed(1)}s`;
|
|
1453
|
-
lines.push({
|
|
1454
|
-
text: boxRow(
|
|
1455
|
-
source_default.white(`Completed in ${duration}`) + source_default.dim(" \u2192 ") + source_default.gray("./0-report.json")
|
|
1456
|
-
),
|
|
1457
|
-
delay: MED
|
|
1458
|
-
});
|
|
1459
|
-
lines.push({ text: boxBottom(false), delay: FAST });
|
|
1460
|
-
return lines;
|
|
1461
|
-
}
|
|
1462
|
-
async function renderReplay(data) {
|
|
1463
|
-
const lines = buildReplayLines(data);
|
|
1464
|
-
process.stdout.write("\n");
|
|
1465
|
-
process.stdout.write(
|
|
1466
|
-
source_default.red.bold(" \u25C6 0") + source_default.gray(" attack replay") + "\n"
|
|
1467
|
-
);
|
|
1468
|
-
process.stdout.write(
|
|
1469
|
-
source_default.dim(" " + "\u2500".repeat(BOX_WIDTH + 1)) + "\n"
|
|
1470
|
-
);
|
|
1471
|
-
process.stdout.write("\n");
|
|
1472
|
-
await sleep(200);
|
|
1473
|
-
for (const line of lines) {
|
|
1474
|
-
await sleep(line.delay);
|
|
1475
|
-
process.stdout.write(" " + line.text + "\n");
|
|
1476
|
-
}
|
|
1477
|
-
process.stdout.write("\n");
|
|
1478
|
-
}
|
|
1479
|
-
|
|
1480
|
-
// packages/cli/src/formatters/timeline.ts
|
|
1481
|
-
var TIMELINE_FORMATS = ["json", "csv", "markdown"];
|
|
1482
|
-
function isTimelineFormat(value) {
|
|
1483
|
-
return TIMELINE_FORMATS.includes(value);
|
|
1484
|
-
}
|
|
1485
|
-
function formatTimeline(record, format) {
|
|
1486
|
-
switch (format) {
|
|
1487
|
-
case "json":
|
|
1488
|
-
return formatTimelineJson(record);
|
|
1489
|
-
case "csv":
|
|
1490
|
-
return formatTimelineCsv(record);
|
|
1491
|
-
case "markdown":
|
|
1492
|
-
return formatTimelineMarkdown(record);
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
function formatTimelineJson(record) {
|
|
1496
|
-
return JSON.stringify(record, null, 2);
|
|
1497
|
-
}
|
|
1498
|
-
var CSV_COLUMNS = [
|
|
1499
|
-
"timestamp",
|
|
1500
|
-
"stage",
|
|
1501
|
-
"eventType",
|
|
1502
|
-
"agentRole",
|
|
1503
|
-
"findingId",
|
|
1504
|
-
"action",
|
|
1505
|
-
"attackTechniqueIds",
|
|
1506
|
-
"attackTactics",
|
|
1507
|
-
// Appended, never interleaved with the ATT&CK columns — an importer that
|
|
1508
|
-
// only knows the ATT&CK pair keeps working, and the two taxonomies stay
|
|
1509
|
-
// visibly distinct in a spreadsheet.
|
|
1510
|
-
"atlasTechniqueIds",
|
|
1511
|
-
"atlasTactics"
|
|
1512
|
-
];
|
|
1513
|
-
function formatTimelineCsv(record) {
|
|
1514
|
-
const lines = [CSV_COLUMNS.join(",")];
|
|
1515
|
-
for (const entry of record.entries) {
|
|
1516
|
-
lines.push(
|
|
1517
|
-
[
|
|
1518
|
-
entry.timestamp,
|
|
1519
|
-
entry.stage,
|
|
1520
|
-
entry.eventType,
|
|
1521
|
-
entry.agentRole ?? "",
|
|
1522
|
-
entry.findingId ?? "",
|
|
1523
|
-
entry.action,
|
|
1524
|
-
entry.techniques.map((t) => t.id).join(" "),
|
|
1525
|
-
uniq(entry.techniques.map((t) => t.tactic)).join(" "),
|
|
1526
|
-
entry.atlasTechniques.map((t) => t.id).join(" "),
|
|
1527
|
-
uniq(entry.atlasTechniques.map((t) => t.tactic)).join(" ")
|
|
1528
|
-
].map(csvCell).join(",")
|
|
1529
|
-
);
|
|
1530
|
-
}
|
|
1531
|
-
return lines.join("\n") + "\n";
|
|
1532
|
-
}
|
|
1533
|
-
function csvCell(value) {
|
|
1534
|
-
if (!/[",\r\n]/.test(value)) return value;
|
|
1535
|
-
return `"${value.replace(/"/g, '""')}"`;
|
|
1536
|
-
}
|
|
1537
|
-
function formatTimelineMarkdown(record) {
|
|
1538
|
-
const out = [];
|
|
1539
|
-
out.push(`# Forensic timeline \u2014 scan \`${record.scanId}\``);
|
|
1540
|
-
out.push("");
|
|
1541
|
-
if (record.target) out.push(`- **Target:** ${record.target}`);
|
|
1542
|
-
out.push(`- **Generated:** ${record.generatedAt}`);
|
|
1543
|
-
out.push(`- **Events:** ${record.eventCount} of ${record.totalEventCount} recorded`);
|
|
1544
|
-
if (record.filters.since) out.push(`- **Since:** ${record.filters.since}`);
|
|
1545
|
-
if (record.filters.until) out.push(`- **Until:** ${record.filters.until}`);
|
|
1546
|
-
if (record.filters.attackOnly) {
|
|
1547
|
-
out.push(`- **Filter:** technique-mapped events only (pipeline lifecycle events omitted)`);
|
|
1548
|
-
}
|
|
1549
|
-
out.push("");
|
|
1550
|
-
out.push("All timestamps are UTC (ISO-8601).");
|
|
1551
|
-
out.push("");
|
|
1552
|
-
out.push(
|
|
1553
|
-
"ATT&CK (Enterprise) and ATLAS (AI systems) are separate matrices; a row may carry either, both, or neither."
|
|
1554
|
-
);
|
|
1555
|
-
out.push("");
|
|
1556
|
-
if (record.entries.length === 0) {
|
|
1557
|
-
out.push("_No pipeline events recorded for this scan in the selected window._");
|
|
1558
|
-
out.push("");
|
|
1559
|
-
return out.join("\n");
|
|
1560
|
-
}
|
|
1561
|
-
out.push("| # | Timestamp (UTC) | Stage | Event | Agent | Finding | Action | ATT&CK | ATLAS |");
|
|
1562
|
-
out.push("| --- | --- | --- | --- | --- | --- | --- | --- | --- |");
|
|
1563
|
-
record.entries.forEach((entry, i) => {
|
|
1564
|
-
out.push(
|
|
1565
|
-
"| " + [
|
|
1566
|
-
String(i + 1),
|
|
1567
|
-
entry.timestamp,
|
|
1568
|
-
mdCell(entry.stage),
|
|
1569
|
-
mdCell(entry.eventType),
|
|
1570
|
-
mdCell(entry.agentRole ?? "\u2014"),
|
|
1571
|
-
mdCell(entry.findingId ?? "\u2014"),
|
|
1572
|
-
mdCell(entry.action),
|
|
1573
|
-
mdTechniques(entry.techniques),
|
|
1574
|
-
mdTechniques(entry.atlasTechniques)
|
|
1575
|
-
].join(" | ") + " |"
|
|
1576
|
-
);
|
|
1577
|
-
});
|
|
1578
|
-
out.push("");
|
|
1579
|
-
return out.join("\n");
|
|
1580
|
-
}
|
|
1581
|
-
function mdTechniques(techniques) {
|
|
1582
|
-
return techniques.length > 0 ? techniques.map(mdTechnique).join(", ") : "\u2014";
|
|
1583
|
-
}
|
|
1584
|
-
function mdTechnique(technique) {
|
|
1585
|
-
const label = mdCell(`${technique.id} ${technique.name}`);
|
|
1586
|
-
return technique.url ? `[${label}](${technique.url})` : label;
|
|
1587
|
-
}
|
|
1588
|
-
function mdCell(value) {
|
|
1589
|
-
return value.replace(/\s+/g, " ").replace(/\|/g, "\\|").trim();
|
|
1590
|
-
}
|
|
1591
|
-
function uniq(values) {
|
|
1592
|
-
return [...new Set(values.filter((v) => v.length > 0))];
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
// packages/cli/src/formatters/index.ts
|
|
1596
|
-
function formatPresentationDocument(document, format) {
|
|
1597
|
-
if (format === "json" && document.documentType !== "scan-report") {
|
|
1598
|
-
return JSON.stringify(document.report, null, 2);
|
|
1599
|
-
}
|
|
1600
|
-
const report = document.documentType === "scan-report" ? document.report : document.documentType === "audit-report" ? {
|
|
1601
|
-
target: `${document.report.package}@${document.report.version}`,
|
|
1602
|
-
scanDepth: "deep",
|
|
1603
|
-
startedAt: document.report.startedAt,
|
|
1604
|
-
completedAt: document.report.completedAt,
|
|
1605
|
-
durationMs: document.report.durationMs,
|
|
1606
|
-
summary: document.report.summary,
|
|
1607
|
-
findings: document.report.findings,
|
|
1608
|
-
warnings: []
|
|
1609
|
-
} : {
|
|
1610
|
-
target: document.report.repo,
|
|
1611
|
-
scanDepth: "deep",
|
|
1612
|
-
startedAt: document.report.startedAt,
|
|
1613
|
-
completedAt: document.report.completedAt,
|
|
1614
|
-
durationMs: document.report.durationMs,
|
|
1615
|
-
summary: document.report.summary,
|
|
1616
|
-
findings: document.report.findings,
|
|
1617
|
-
warnings: document.report.warnings?.map((warning) => ({ stage: "report", message: `[${warning.stage}] ${warning.message}` })) ?? [],
|
|
1618
|
-
executionSuccessful: document.report.researchFailed ? false : void 0,
|
|
1619
|
-
reviewChecks: document.report.reviewChecks
|
|
1620
|
-
};
|
|
1621
|
-
switch (format) {
|
|
1622
|
-
case "terminal":
|
|
1623
|
-
return formatTerminal(report);
|
|
1624
|
-
case "json":
|
|
1625
|
-
return formatJson(report);
|
|
1626
|
-
case "markdown":
|
|
1627
|
-
return formatMarkdown(report);
|
|
1628
|
-
case "html":
|
|
1629
|
-
return formatHtml(report);
|
|
1630
|
-
case "sarif":
|
|
1631
|
-
return formatSarif(report);
|
|
1632
|
-
case "pdf":
|
|
1633
|
-
return "[PDF output requires generatePdfReport()]";
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
function formatReport(report, format) {
|
|
1637
|
-
return formatPresentationDocument(createScanReportDocument(report), format);
|
|
1638
|
-
}
|
|
1639
|
-
function formatAuditReport(report, format) {
|
|
1640
|
-
return formatPresentationDocument(createAuditReportDocument(report), format);
|
|
1641
|
-
}
|
|
1642
|
-
function formatReviewReport(report, format) {
|
|
1643
|
-
return formatPresentationDocument(createReviewReportDocument(report), format);
|
|
1644
1116
|
}
|
|
1645
1117
|
|
|
1646
1118
|
export {
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
checkRuntimeAvailability,
|
|
1650
|
-
buildShareUrl,
|
|
1119
|
+
formatMarkdown,
|
|
1120
|
+
formatHtml,
|
|
1651
1121
|
formatSarif,
|
|
1652
|
-
generatePdfReport
|
|
1653
|
-
TIMELINE_FORMATS,
|
|
1654
|
-
isTimelineFormat,
|
|
1655
|
-
formatTimeline,
|
|
1656
|
-
formatReport,
|
|
1657
|
-
formatAuditReport,
|
|
1658
|
-
formatReviewReport
|
|
1122
|
+
generatePdfReport
|
|
1659
1123
|
};
|