gitflow-analytics 3.3.0__py3-none-any.whl → 3.5.2__py3-none-any.whl

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.
Files changed (36) hide show
  1. gitflow_analytics/_version.py +1 -1
  2. gitflow_analytics/cli.py +517 -15
  3. gitflow_analytics/cli_wizards/__init__.py +10 -0
  4. gitflow_analytics/cli_wizards/install_wizard.py +1181 -0
  5. gitflow_analytics/cli_wizards/run_launcher.py +433 -0
  6. gitflow_analytics/config/__init__.py +3 -0
  7. gitflow_analytics/config/aliases.py +306 -0
  8. gitflow_analytics/config/loader.py +35 -1
  9. gitflow_analytics/config/schema.py +13 -0
  10. gitflow_analytics/constants.py +75 -0
  11. gitflow_analytics/core/cache.py +7 -3
  12. gitflow_analytics/core/data_fetcher.py +66 -30
  13. gitflow_analytics/core/git_timeout_wrapper.py +6 -4
  14. gitflow_analytics/core/progress.py +2 -4
  15. gitflow_analytics/core/subprocess_git.py +31 -5
  16. gitflow_analytics/identity_llm/analysis_pass.py +13 -3
  17. gitflow_analytics/identity_llm/analyzer.py +14 -2
  18. gitflow_analytics/identity_llm/models.py +7 -1
  19. gitflow_analytics/qualitative/classifiers/llm/openai_client.py +5 -3
  20. gitflow_analytics/security/config.py +6 -6
  21. gitflow_analytics/security/extractors/dependency_checker.py +14 -14
  22. gitflow_analytics/security/extractors/secret_detector.py +8 -14
  23. gitflow_analytics/security/extractors/vulnerability_scanner.py +9 -9
  24. gitflow_analytics/security/llm_analyzer.py +10 -10
  25. gitflow_analytics/security/security_analyzer.py +17 -17
  26. gitflow_analytics/tui/screens/analysis_progress_screen.py +1 -1
  27. gitflow_analytics/ui/progress_display.py +36 -29
  28. gitflow_analytics/verify_activity.py +23 -26
  29. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.5.2.dist-info}/METADATA +1 -1
  30. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.5.2.dist-info}/RECORD +34 -31
  31. gitflow_analytics/security/reports/__init__.py +0 -5
  32. gitflow_analytics/security/reports/security_report.py +0 -358
  33. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.5.2.dist-info}/WHEEL +0 -0
  34. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.5.2.dist-info}/entry_points.txt +0 -0
  35. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.5.2.dist-info}/licenses/LICENSE +0 -0
  36. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.5.2.dist-info}/top_level.txt +0 -0
@@ -1,42 +1,47 @@
1
1
  gitflow_analytics/__init__.py,sha256=yN1dyAUu4l9qX-YNAGRItEf4RFFe-5GQiOntXPIfdxo,683
2
- gitflow_analytics/_version.py,sha256=dmUQsHyop15uYXbUzQBx5gTwqV6m0-_R_rJJ-oiDk-0,137
3
- gitflow_analytics/cli.py,sha256=8V-5FmKmaOAv944-N-D9kv_a-c0cAyV1HsPoxQFe6qs,239710
2
+ gitflow_analytics/_version.py,sha256=VlHh9eAQ34bIRsu31l61B7n-gUeASgbwjJXQ9Hp_IbQ,137
3
+ gitflow_analytics/cli.py,sha256=gKfN-fbEPQqEQVgE693xU5yKSlR4TeVXjoI-vG5oIPA,258433
4
4
  gitflow_analytics/config.py,sha256=XRuxvzLWyn_ML7mDCcuZ9-YFNAEsnt33vIuWxQQ_jxg,1033
5
- gitflow_analytics/verify_activity.py,sha256=C1Ou0ZEdkg_jXZViBamZa4-AUtbY090PWVUeW02DDIE,27135
5
+ gitflow_analytics/constants.py,sha256=GXEncUJS9ijOI5KWtQCTANwdqxPfXpw-4lNjhaWTKC4,2488
6
+ gitflow_analytics/verify_activity.py,sha256=aRQnmypf5NDasXudf2iz_WdJnCWtwlbAiJ5go0DJLSU,27050
6
7
  gitflow_analytics/classification/__init__.py,sha256=p8shPUZpGaw7-ivhfAVrPDbSP2LrpvWC1WEsBJIg-PI,969
7
8
  gitflow_analytics/classification/batch_classifier.py,sha256=wR1hwYOB4JbV2h5fQrs-UHlf4XwCPZSJUjKFWyD4Qv0,37696
8
9
  gitflow_analytics/classification/classifier.py,sha256=U1vpdiMXqGdHR8iHWf_wPdrJxxNRB5By94BDpck8R9g,17750
9
10
  gitflow_analytics/classification/feature_extractor.py,sha256=W82vztPQO8-MFw9Yt17K1kXrLZ5lNtuMSwC1NBsZPLQ,23804
10
11
  gitflow_analytics/classification/linguist_analyzer.py,sha256=HjLx9mM7hGXtrvMba6osovHJLAacTx9oDmN6CS5w0bE,17687
11
12
  gitflow_analytics/classification/model.py,sha256=2KbmFh9MpyvHMcNHbqwUTAAVLHHu3MiTfFIPyZSGa-8,16356
12
- gitflow_analytics/config/__init__.py,sha256=lzFOHsJGoeDHuu_NEgcSeUFwU0bgV3lnL9w0Pyc4FI0,1037
13
+ gitflow_analytics/cli_wizards/__init__.py,sha256=D73D97cS1hZsB_fCQQaAiWtd_w2Lb8TtcGc9Pn2DIyE,343
14
+ gitflow_analytics/cli_wizards/install_wizard.py,sha256=aQepE3ThhBPyOjkDj-_p0fKZ2OEVTlq7d_6HEt4JrkQ,44871
15
+ gitflow_analytics/cli_wizards/run_launcher.py,sha256=J6G_C7IqxPg7_GhAfbV99D1dIIWwb1s_qmHC7Iv2iGI,15038
16
+ gitflow_analytics/config/__init__.py,sha256=KziRIbBJctB5LOLcKLzELWA1rXwjS6-C2_DeM_hT9rM,1133
17
+ gitflow_analytics/config/aliases.py,sha256=z9F0X6qbbF544Tw7sHlOoBj5mpRSddMkCpoKLzvVzDU,10960
13
18
  gitflow_analytics/config/errors.py,sha256=IBKhAIwJ4gscZFnLDyE3jEp03wn2stPR7JQJXNSIfok,10386
14
- gitflow_analytics/config/loader.py,sha256=aS-A9_uuoj7T58pVdKv_J2qWxbXw0JUMveBsRYpGVHE,35862
19
+ gitflow_analytics/config/loader.py,sha256=EiksTB1Uqey63FxIvuud_kMdab3sNDfuICE_RwMLYFA,37290
15
20
  gitflow_analytics/config/profiles.py,sha256=yUjFAWW6uzOUdi5qlPE-QV9681HigyrLiSJFpL8X9A0,7967
16
21
  gitflow_analytics/config/repository.py,sha256=maptMAdCKDsuMAfoTAaTrMPVfVd_tKNLRenvuPe1-t4,4350
17
- gitflow_analytics/config/schema.py,sha256=g-YQDSHS0x5jfaLo4V57qB6Mvl11Pdwl7DFvz2SwDDw,14856
22
+ gitflow_analytics/config/schema.py,sha256=lFN80-YcSqu33UwiJryFHn8F5_zX619AaJXSuJ3aht8,15271
18
23
  gitflow_analytics/config/validator.py,sha256=l7AHjXYJ8wEmyA1rn2WiItZXtAiRb9YBLjFCDl53qKM,5907
19
24
  gitflow_analytics/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
25
  gitflow_analytics/core/analyzer.py,sha256=59kGObzjziOb8geFyZMKCUvWmo3hcXE0eTgrjYEc1XA,58736
21
26
  gitflow_analytics/core/branch_mapper.py,sha256=1L1ctrhTEqMZ61eS1nZRkcyaarLipeQgotw4HdXcSmM,7407
22
- gitflow_analytics/core/cache.py,sha256=Y4LPRrNgx648dzWtbT0KJa5uQTqny97WY3Q4jtOrR9w,68949
23
- gitflow_analytics/core/data_fetcher.py,sha256=VDfwZM8aDe6c9XnC4L7QK4Mw1_gv_p36lcq3XakCFlE,100949
27
+ gitflow_analytics/core/cache.py,sha256=JLfr-7ayGGkdUX6e3_uURpWA0yiK7nrk5mvcHDfBYic,69124
28
+ gitflow_analytics/core/data_fetcher.py,sha256=YSanxAVKo44yR_T38YEpHnTZSmioP--0aQSuN29Y6LM,103294
24
29
  gitflow_analytics/core/git_auth.py,sha256=QP7U5_Mi9J-hEtoEhdjoMBl61nCukOGlL8PYXYSyN3g,6369
25
- gitflow_analytics/core/git_timeout_wrapper.py,sha256=wc08xxhzhzcyl7pOBosr_tl2LGF0tV0bXQQGE02y070,12422
30
+ gitflow_analytics/core/git_timeout_wrapper.py,sha256=14K8PHKSOonW4hJpLigB5XQNSWxmFbMFbrpu8cT1h-M,12534
26
31
  gitflow_analytics/core/identity.py,sha256=k7i-vcRJ2eiTU0_kYGY5QOhxcqnitibTTx7DVONW0kg,31237
27
32
  gitflow_analytics/core/metrics_storage.py,sha256=2u4dxVHsCTEaVIO5udWCaHzuefRL7JVS8aN7wIwyMlc,21769
28
- gitflow_analytics/core/progress.py,sha256=HjVL1yyJNSurMN_gY4VDPZc22V7L448i8nh27EcUTCE,20788
33
+ gitflow_analytics/core/progress.py,sha256=KMXwZpJGlmUU8OehNRA7_PONpXUgSIxWl5ZN7INc108,20732
29
34
  gitflow_analytics/core/schema_version.py,sha256=fhYKxerCgPHJoX4SAoJQO38sQcKDNguMEUWSj367Ilc,10660
30
- gitflow_analytics/core/subprocess_git.py,sha256=QBPuIx06N4Eghr1Dv-dkvUI_jyi8e2pxKEB-HL9r0sw,4792
35
+ gitflow_analytics/core/subprocess_git.py,sha256=a6WUQB0tiSox7_ESBgI4AjYEdni_rIOjUW17cjMXVhs,5917
31
36
  gitflow_analytics/extractors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
37
  gitflow_analytics/extractors/base.py,sha256=AKbYkFiMhNxVj7zfNzsJfh0rpyTdNr4Faea3bcZPPBo,1168
33
38
  gitflow_analytics/extractors/ml_tickets.py,sha256=js5OFmbZt9JHy5r_crehhuB1MxrkdfrPj2u_4B6K35c,43304
34
39
  gitflow_analytics/extractors/story_points.py,sha256=IggP-Ei832oV9aD08a3li08kmjF3BqyU9i8EgAZcpfs,5324
35
40
  gitflow_analytics/extractors/tickets.py,sha256=2s5Iu7eZXVi8yl9Yme5HKzrJo3mDjzsSOUr_iJGUeLM,43799
36
41
  gitflow_analytics/identity_llm/__init__.py,sha256=tpWDwapm6zIyb8LxLO8A6pHlE3wNorT_fBL-Yp9-XnU,250
37
- gitflow_analytics/identity_llm/analysis_pass.py,sha256=mJUC8Cts2m6vAd_VEXRcZOF1FqHtnma8-N5yqd1Csh0,8787
38
- gitflow_analytics/identity_llm/analyzer.py,sha256=iYx7CxgYpfiliY0qOtTpoy1GLAM-hvrJdtNuFO9llqc,17336
39
- gitflow_analytics/identity_llm/models.py,sha256=YcTh1tz0_OgBPqapMzVglBAIUOfG5uPzur72631VFUU,2375
42
+ gitflow_analytics/identity_llm/analysis_pass.py,sha256=lYfjM6f82agXstTrUBsS0R9-ipfRnviIqe8ExkjKnvc,9459
43
+ gitflow_analytics/identity_llm/analyzer.py,sha256=-a7lUJt_Dlgx9aNOH1YlFqPe7BSxtwY2RoGruIzwrzs,17932
44
+ gitflow_analytics/identity_llm/models.py,sha256=F1RN6g8og9esj-m4TPY_928Ci9TA43G9NFNHYf4zHHQ,2677
40
45
  gitflow_analytics/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
46
  gitflow_analytics/integrations/github_integration.py,sha256=52lyq5GNJIlTXIv7iwrkuxg0firpTcwYtTU9RAn8EIk,13324
42
47
  gitflow_analytics/integrations/jira_integration.py,sha256=3DV1hGNs1HxAOSGt2BfqBrWSigRN5H8BT1-G7E_8hGg,28761
@@ -69,7 +74,7 @@ gitflow_analytics/qualitative/classifiers/llm/base.py,sha256=1_GEb-Q5hxj109nvJaZ
69
74
  gitflow_analytics/qualitative/classifiers/llm/batch_processor.py,sha256=oSlJPYOAp-JXQRYVj9PFNSkoiFp8BdZViH-0cNNpo5E,13138
70
75
  gitflow_analytics/qualitative/classifiers/llm/cache.py,sha256=5UWRMgz0bOc_GRShE5gvYEzLhxB-VBDkhZKRECNmjOI,16930
71
76
  gitflow_analytics/qualitative/classifiers/llm/cost_tracker.py,sha256=2mqWPHo9SrhwZQ0Q_FNViI_M83Rl18sJVg3p1lwkcBM,14902
72
- gitflow_analytics/qualitative/classifiers/llm/openai_client.py,sha256=bCfoEj5fX6qZ6WdTG1sZJ9gKxUl66gFMd4UHwifY4uE,15204
77
+ gitflow_analytics/qualitative/classifiers/llm/openai_client.py,sha256=pkLvXpAR9tK2VLPwpUoK3GYmThMb8VcekRj2qpvVrNw,15255
73
78
  gitflow_analytics/qualitative/classifiers/llm/prompts.py,sha256=7dLvCb8bQyZGOsQF_ajZpFxaXyHIbtfUfTO2yQJPxJs,12906
74
79
  gitflow_analytics/qualitative/classifiers/llm/response_parser.py,sha256=AyGfTpmvpx4QvRKJDWGS3CbylLm2SOAD0nSZVrkGTrM,9931
75
80
  gitflow_analytics/qualitative/core/__init__.py,sha256=22tZJDPyYE0k5-9lx_84R2SsZN8PRc_1I1L6prSkoSE,315
@@ -103,15 +108,13 @@ gitflow_analytics/reports/narrative_writer.py,sha256=4s-4VQiHqckvOEy53ueFA9_UZEt
103
108
  gitflow_analytics/reports/story_point_correlation.py,sha256=V9fnqNOxJxK00w0Mx69BMpcZgdgQJyja_Pu4qD-SWw0,51210
104
109
  gitflow_analytics/reports/weekly_trends_writer.py,sha256=m_TQ6ThSaa5rkAwfQpPRms2Jwgq3RReYwVBsts67cLk,15720
105
110
  gitflow_analytics/security/__init__.py,sha256=qocWBnb3xM_H43vsj6RWWOjHHTAZM7GDma5wEhdbWDc,432
106
- gitflow_analytics/security/config.py,sha256=5FVDmCfq5TcmBbf94Y6KRixLTXwLcRODneLCHbFEShk,6300
107
- gitflow_analytics/security/llm_analyzer.py,sha256=rQ6m2vjfR5VOloGca9eS6VHO8cEePQZEcC90W9ATYLc,12998
108
- gitflow_analytics/security/security_analyzer.py,sha256=f8mgZSLoylBDLTQIDs5NrNYA_KUEH5-_pcAF0VwR8vA,15273
111
+ gitflow_analytics/security/config.py,sha256=JyydqgitGvOJFBORlBDDsv4dOtRELXqIsI0I0LNHcJE,6288
112
+ gitflow_analytics/security/llm_analyzer.py,sha256=08n260LmlKnjX2v-MH8bygH6nmodpwDasPCNlOe7Qks,12986
113
+ gitflow_analytics/security/security_analyzer.py,sha256=6Sxlq8rvR3Cif-FXy_VWuPUqVUxfuxm68rbiPBLwNQs,15261
109
114
  gitflow_analytics/security/extractors/__init__.py,sha256=K64IAhS0k47J5oUFNmm1NcLWag_dMZw99U0h38dUR6Y,280
110
- gitflow_analytics/security/extractors/dependency_checker.py,sha256=jSho9KfKug-jXeDWDF5jMnEjKPmnJsxhXaCnXEJng0A,14613
111
- gitflow_analytics/security/extractors/secret_detector.py,sha256=lBuPF6tJaE_2_Mm3LQZIXkg6zk6kUsqSeFmblRyhNp0,6984
112
- gitflow_analytics/security/extractors/vulnerability_scanner.py,sha256=JWfFr4jacQ0EugXw0-tcual4Bpr1-Urgqha9vL3oXaE,12694
113
- gitflow_analytics/security/reports/__init__.py,sha256=C6a4tHi-xCtTE5sSkQU7bjhRtEGE9l-ApUC13DVZqVQ,125
114
- gitflow_analytics/security/reports/security_report.py,sha256=W0DXpR2ddej0Di6X6YqI6U05M08oTPjW8VWWHdNrGCg,15466
115
+ gitflow_analytics/security/extractors/dependency_checker.py,sha256=Tb-Rb4G7jJkH_hsvH3dmEV2SyqjN8ycTHjgItObnCHg,14601
116
+ gitflow_analytics/security/extractors/secret_detector.py,sha256=aa2YiKeR6SurWHMHU5fr71Q83stSmIRKvwWFpp0IVFs,6857
117
+ gitflow_analytics/security/extractors/vulnerability_scanner.py,sha256=TWK1fPMN5s_EM8AwTFpkxRjXEsHIMwxYUZQqM2l5dV0,12682
115
118
  gitflow_analytics/training/__init__.py,sha256=YT5p7Wm4U8trzLnbS5FASJBWPMKhqp3rlAThjpxWnxo,143
116
119
  gitflow_analytics/training/model_loader.py,sha256=xGZLSopGxDhC--2XN6ytRgi2CyjOKY4zS4fZ-ZlO6lM,13245
117
120
  gitflow_analytics/training/pipeline.py,sha256=PQegTk_-OsPexVyRDfiy-3Df-7pcs25C4vPASr-HT9E,19951
@@ -119,7 +122,7 @@ gitflow_analytics/tui/__init__.py,sha256=1liMpth2RvUkmKfNUEnYEZkYi2RpYITFMmGKtBB
119
122
  gitflow_analytics/tui/app.py,sha256=rD0UTQqu9u3yrnJ7akcUNffZbJu-mfAFG5PTTLEcRo0,22200
120
123
  gitflow_analytics/tui/progress_adapter.py,sha256=fb6z2N87VAiQo0IyIpho23XTSHvQ-ydU6rVzHRzzQu8,11300
121
124
  gitflow_analytics/tui/screens/__init__.py,sha256=JVnPy-o4V6D2jehliXAbRET9x8zWmHR7PPk2is-l9OM,327
122
- gitflow_analytics/tui/screens/analysis_progress_screen.py,sha256=13FTjcW3NSsewy2RNQSm7JzFbmOMIGPo30wtdhR-itc,36659
125
+ gitflow_analytics/tui/screens/analysis_progress_screen.py,sha256=j1Mg5WZOKPGMtQoKxLUjy4UM9tKllcl3Ktqr0WJgOaY,36660
123
126
  gitflow_analytics/tui/screens/configuration_screen.py,sha256=QLtTz8xFAGdIxYsRmUyBr4m-1PteAk3_kxfE1UexqgA,19345
124
127
  gitflow_analytics/tui/screens/loading_screen.py,sha256=5kh0kKKCa6-NMlZyPfu2fE4ROgmjU8_jA2xCUX3z5iY,14451
125
128
  gitflow_analytics/tui/screens/main_screen.py,sha256=6aIzJrDtgXDlgGcW--wQUqncBBa_hcUytiLu76fMALw,11482
@@ -129,10 +132,10 @@ gitflow_analytics/tui/widgets/data_table.py,sha256=8fGNG4m7H41vCid3QwCHJa7bd8qu_
129
132
  gitflow_analytics/tui/widgets/export_modal.py,sha256=L-XKPOc6u-fow2TudPgDnC0kXZM1WZuGd_jahtV8lhg,10737
130
133
  gitflow_analytics/tui/widgets/progress_widget.py,sha256=Qny6Q1nU0Pr3aj4aHfXLaRjya9MH3rldR2HWYiaQyGE,6167
131
134
  gitflow_analytics/ui/__init__.py,sha256=UBhYhZMvwlSrCuGWjkIdoP2zNbiQxOHOli-I8mqIZUE,441
132
- gitflow_analytics/ui/progress_display.py,sha256=s0HNArAKofwx9m-xytTPTdhfmYJLcbriDZeF3k2z4fQ,58781
133
- gitflow_analytics-3.3.0.dist-info/licenses/LICENSE,sha256=xwvSwY1GYXpRpmbnFvvnbmMwpobnrdN9T821sGvjOY0,1066
134
- gitflow_analytics-3.3.0.dist-info/METADATA,sha256=8anwUII1XOfghEnGtCA9z5mcRCrwSn2pruj6K7OoFYQ,34122
135
- gitflow_analytics-3.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
136
- gitflow_analytics-3.3.0.dist-info/entry_points.txt,sha256=a3y8HnfLOvK1QVOgAkDY6VQXXm3o9ZSQRZrpiaS3hEM,65
137
- gitflow_analytics-3.3.0.dist-info/top_level.txt,sha256=CQyxZXjKvpSB1kgqqtuE0PCRqfRsXZJL8JrYpJKtkrk,18
138
- gitflow_analytics-3.3.0.dist-info/RECORD,,
135
+ gitflow_analytics/ui/progress_display.py,sha256=3xJnCOSs1DRVAfS-rTu37EsLfWDFW5-mbv-bPS9NMm4,59182
136
+ gitflow_analytics-3.5.2.dist-info/licenses/LICENSE,sha256=xwvSwY1GYXpRpmbnFvvnbmMwpobnrdN9T821sGvjOY0,1066
137
+ gitflow_analytics-3.5.2.dist-info/METADATA,sha256=7TWOkZxt3KP7_w30_hufQiB8JKsNC6ZGJcPHwNRyMAc,34122
138
+ gitflow_analytics-3.5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
139
+ gitflow_analytics-3.5.2.dist-info/entry_points.txt,sha256=a3y8HnfLOvK1QVOgAkDY6VQXXm3o9ZSQRZrpiaS3hEM,65
140
+ gitflow_analytics-3.5.2.dist-info/top_level.txt,sha256=CQyxZXjKvpSB1kgqqtuE0PCRqfRsXZJL8JrYpJKtkrk,18
141
+ gitflow_analytics-3.5.2.dist-info/RECORD,,
@@ -1,5 +0,0 @@
1
- """Security reporting module."""
2
-
3
- from .security_report import SecurityReportGenerator
4
-
5
- __all__ = ["SecurityReportGenerator"]
@@ -1,358 +0,0 @@
1
- """Generate security analysis reports."""
2
-
3
- import json
4
- import csv
5
- from typing import List, Dict, Any, Optional
6
- from pathlib import Path
7
- from datetime import datetime
8
- from ..security_analyzer import SecurityAnalysis
9
-
10
-
11
- class SecurityReportGenerator:
12
- """Generate various format reports for security findings."""
13
-
14
- def __init__(self, output_dir: Optional[Path] = None):
15
- """Initialize report generator.
16
-
17
- Args:
18
- output_dir: Directory for report output
19
- """
20
- self.output_dir = output_dir or Path("reports")
21
- self.output_dir.mkdir(parents=True, exist_ok=True)
22
-
23
- def generate_reports(self, analyses: List[SecurityAnalysis], summary: Dict[str, Any]) -> Dict[str, Path]:
24
- """Generate all report formats.
25
-
26
- Args:
27
- analyses: List of security analyses
28
- summary: Summary statistics
29
-
30
- Returns:
31
- Dictionary of report type to file path
32
- """
33
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
34
- reports = {}
35
-
36
- # Generate Markdown report
37
- md_path = self.output_dir / f"security_report_{timestamp}.md"
38
- self._generate_markdown_report(analyses, summary, md_path)
39
- reports["markdown"] = md_path
40
-
41
- # Generate JSON report
42
- json_path = self.output_dir / f"security_findings_{timestamp}.json"
43
- self._generate_json_report(analyses, summary, json_path)
44
- reports["json"] = json_path
45
-
46
- # Generate CSV report
47
- csv_path = self.output_dir / f"security_issues_{timestamp}.csv"
48
- self._generate_csv_report(analyses, csv_path)
49
- reports["csv"] = csv_path
50
-
51
- # Generate SARIF report if requested
52
- if any(a.total_findings > 0 for a in analyses):
53
- sarif_path = self.output_dir / f"security_sarif_{timestamp}.json"
54
- self._generate_sarif_report(analyses, sarif_path)
55
- reports["sarif"] = sarif_path
56
-
57
- return reports
58
-
59
- def _generate_markdown_report(self, analyses: List[SecurityAnalysis], summary: Dict, path: Path) -> None:
60
- """Generate comprehensive Markdown security report."""
61
- with open(path, 'w') as f:
62
- # Header
63
- f.write("# 🔒 Security Analysis Report\n\n")
64
- f.write(f"**Generated**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
65
-
66
- # Executive Summary
67
- f.write("## 📊 Executive Summary\n\n")
68
- f.write(f"- **Commits Analyzed**: {summary['total_commits']}\n")
69
- f.write(f"- **Commits with Issues**: {summary['commits_with_issues']}\n")
70
- f.write(f"- **Total Findings**: {summary['total_findings']}\n")
71
- f.write(f"- **Risk Level**: **{summary['risk_level']}** (Score: {summary['average_risk_score']})\n\n")
72
-
73
- # Risk Assessment
74
- self._write_risk_assessment(f, summary)
75
-
76
- # Severity Distribution
77
- f.write("## 🎯 Severity Distribution\n\n")
78
- severity = summary['severity_distribution']
79
- if severity['critical'] > 0:
80
- f.write(f"- 🔴 **Critical**: {severity['critical']}\n")
81
- if severity['high'] > 0:
82
- f.write(f"- 🟠 **High**: {severity['high']}\n")
83
- if severity['medium'] > 0:
84
- f.write(f"- 🟡 **Medium**: {severity['medium']}\n")
85
- if severity['low'] > 0:
86
- f.write(f"- 🟢 **Low**: {severity['low']}\n")
87
- f.write("\n")
88
-
89
- # Top Issues
90
- if summary['top_issues']:
91
- f.write("## 🔝 Top Security Issues\n\n")
92
- f.write("| Issue Type | Severity | Occurrences | Affected Files |\n")
93
- f.write("|------------|----------|-------------|----------------|\n")
94
- for issue in summary['top_issues']:
95
- f.write(f"| {issue['type']} | {issue['severity'].upper()} | "
96
- f"{issue['occurrences']} | {issue['affected_files']} |\n")
97
- f.write("\n")
98
-
99
- # Detailed Findings by Category
100
- self._write_detailed_findings(f, analyses)
101
-
102
- # LLM Insights
103
- if 'llm_insights' in summary and summary['llm_insights']:
104
- f.write("## 🤖 AI Security Insights\n\n")
105
- f.write(summary['llm_insights'])
106
- f.write("\n\n")
107
-
108
- # Recommendations
109
- f.write("## 💡 Recommendations\n\n")
110
- for rec in summary['recommendations']:
111
- f.write(f"- {rec}\n")
112
- f.write("\n")
113
-
114
- # Appendix - All Findings
115
- f.write("## 📋 Detailed Findings\n\n")
116
- self._write_all_findings(f, analyses)
117
-
118
- def _write_risk_assessment(self, f, summary: Dict) -> None:
119
- """Write risk assessment section."""
120
- risk_level = summary['risk_level']
121
- score = summary['average_risk_score']
122
-
123
- f.write("## ⚠️ Risk Assessment\n\n")
124
-
125
- if risk_level == "CRITICAL":
126
- f.write("### 🚨 CRITICAL RISK DETECTED\n\n")
127
- f.write("Immediate action required. Critical security vulnerabilities have been identified "
128
- "that could lead to severe security breaches.\n\n")
129
- elif risk_level == "HIGH":
130
- f.write("### 🔴 High Risk\n\n")
131
- f.write("Significant security issues detected that should be addressed urgently.\n\n")
132
- elif risk_level == "MEDIUM":
133
- f.write("### 🟡 Medium Risk\n\n")
134
- f.write("Moderate security concerns identified that should be addressed in the near term.\n\n")
135
- else:
136
- f.write("### 🟢 Low Risk\n\n")
137
- f.write("Minor security issues detected. Continue with regular security practices.\n\n")
138
-
139
- # Risk score visualization
140
- f.write("**Risk Score Breakdown**:\n")
141
- f.write("```\n")
142
- bar_length = 50
143
- filled = int(score / 100 * bar_length)
144
- bar = "█" * filled + "░" * (bar_length - filled)
145
- f.write(f"[{bar}] {score:.1f}/100\n")
146
- f.write("```\n\n")
147
-
148
- def _write_detailed_findings(self, f, analyses: List[SecurityAnalysis]) -> None:
149
- """Write detailed findings by category."""
150
- # Aggregate findings
151
- all_secrets = []
152
- all_vulnerabilities = []
153
- all_dependencies = []
154
- all_llm = []
155
-
156
- for analysis in analyses:
157
- all_secrets.extend(analysis.secrets)
158
- all_vulnerabilities.extend(analysis.vulnerabilities)
159
- all_dependencies.extend(analysis.dependency_issues)
160
- all_llm.extend(analysis.llm_findings)
161
-
162
- # Secrets Section
163
- if all_secrets:
164
- f.write("## 🔑 Exposed Secrets\n\n")
165
- f.write(f"**Total**: {len(all_secrets)} potential secrets detected\n\n")
166
-
167
- # Group by secret type
168
- by_type = {}
169
- for secret in all_secrets:
170
- secret_type = secret.get('secret_type', 'unknown')
171
- if secret_type not in by_type:
172
- by_type[secret_type] = []
173
- by_type[secret_type].append(secret)
174
-
175
- for secret_type, secrets in sorted(by_type.items()):
176
- f.write(f"### {secret_type.replace('_', ' ').title()}\n")
177
- for s in secrets[:5]: # Show first 5 of each type
178
- f.write(f"- **File**: `{s.get('file', 'unknown')}`\n")
179
- f.write(f" - Line: {s.get('line', 'N/A')}\n")
180
- f.write(f" - Pattern: `{s.get('match', 'N/A')}`\n")
181
- if len(secrets) > 5:
182
- f.write(f" - *... and {len(secrets) - 5} more*\n")
183
- f.write("\n")
184
-
185
- # Vulnerabilities Section
186
- if all_vulnerabilities:
187
- f.write("## 🛡️ Code Vulnerabilities\n\n")
188
- f.write(f"**Total**: {len(all_vulnerabilities)} vulnerabilities detected\n\n")
189
-
190
- # Group by vulnerability type
191
- by_type = {}
192
- for vuln in all_vulnerabilities:
193
- vuln_type = vuln.get('vulnerability_type', 'unknown')
194
- if vuln_type not in by_type:
195
- by_type[vuln_type] = []
196
- by_type[vuln_type].append(vuln)
197
-
198
- for vuln_type, vulns in sorted(by_type.items()):
199
- f.write(f"### {vuln_type.replace('_', ' ').title()}\n")
200
- for v in vulns[:5]:
201
- f.write(f"- **File**: `{v.get('file', 'unknown')}:{v.get('line', 'N/A')}`\n")
202
- f.write(f" - Tool: {v.get('tool', 'N/A')}\n")
203
- f.write(f" - Message: {v.get('message', 'N/A')}\n")
204
- if len(vulns) > 5:
205
- f.write(f" - *... and {len(vulns) - 5} more*\n")
206
- f.write("\n")
207
-
208
- # Dependencies Section
209
- if all_dependencies:
210
- f.write("## 📦 Vulnerable Dependencies\n\n")
211
- f.write(f"**Total**: {len(all_dependencies)} vulnerable dependencies\n\n")
212
-
213
- for dep in all_dependencies[:10]:
214
- f.write(f"- **{dep.get('package', 'unknown')}** @ {dep.get('version', 'unknown')}\n")
215
- f.write(f" - File: `{dep.get('file', 'unknown')}`\n")
216
- if dep.get('cve'):
217
- f.write(f" - CVE: {dep['cve']}\n")
218
- f.write(f" - Message: {dep.get('message', 'N/A')}\n")
219
- if len(all_dependencies) > 10:
220
- f.write(f"\n*... and {len(all_dependencies) - 10} more vulnerable dependencies*\n")
221
- f.write("\n")
222
-
223
- def _write_all_findings(self, f, analyses: List[SecurityAnalysis]) -> None:
224
- """Write all findings in detail."""
225
- for analysis in analyses:
226
- if analysis.total_findings == 0:
227
- continue
228
-
229
- f.write(f"### Commit: `{analysis.commit_hash[:8]}`\n")
230
- f.write(f"**Time**: {analysis.timestamp.strftime('%Y-%m-%d %H:%M:%S')}\n")
231
- f.write(f"**Files Changed**: {len(analysis.files_changed)}\n")
232
- f.write(f"**Risk Score**: {analysis.risk_score:.1f}\n\n")
233
-
234
- if analysis.secrets:
235
- f.write("**Secrets**:\n")
236
- for s in analysis.secrets:
237
- f.write(f"- {s.get('secret_type', 'unknown')}: {s.get('file', 'N/A')}\n")
238
-
239
- if analysis.vulnerabilities:
240
- f.write("**Vulnerabilities**:\n")
241
- for v in analysis.vulnerabilities:
242
- f.write(f"- {v.get('vulnerability_type', 'unknown')}: {v.get('file', 'N/A')}\n")
243
-
244
- f.write("\n---\n\n")
245
-
246
- def _generate_json_report(self, analyses: List[SecurityAnalysis], summary: Dict, path: Path) -> None:
247
- """Generate JSON report with all findings."""
248
- report = {
249
- "metadata": {
250
- "generated": datetime.now().isoformat(),
251
- "version": "1.0.0"
252
- },
253
- "summary": summary,
254
- "analyses": []
255
- }
256
-
257
- for analysis in analyses:
258
- report["analyses"].append({
259
- "commit_hash": analysis.commit_hash,
260
- "timestamp": analysis.timestamp.isoformat(),
261
- "files_changed": analysis.files_changed,
262
- "risk_score": analysis.risk_score,
263
- "findings": {
264
- "secrets": analysis.secrets,
265
- "vulnerabilities": analysis.vulnerabilities,
266
- "dependency_issues": analysis.dependency_issues,
267
- "llm_findings": analysis.llm_findings
268
- },
269
- "metrics": {
270
- "total": analysis.total_findings,
271
- "critical": analysis.critical_count,
272
- "high": analysis.high_count,
273
- "medium": analysis.medium_count,
274
- "low": analysis.low_count
275
- }
276
- })
277
-
278
- with open(path, 'w') as f:
279
- json.dump(report, f, indent=2)
280
-
281
- def _generate_csv_report(self, analyses: List[SecurityAnalysis], path: Path) -> None:
282
- """Generate CSV report of all findings."""
283
- with open(path, 'w', newline='') as f:
284
- writer = csv.DictWriter(f, fieldnames=[
285
- 'commit_hash', 'timestamp', 'type', 'severity',
286
- 'category', 'file', 'line', 'message', 'tool', 'confidence'
287
- ])
288
- writer.writeheader()
289
-
290
- for analysis in analyses:
291
- # Write all findings
292
- for finding in (analysis.secrets + analysis.vulnerabilities +
293
- analysis.dependency_issues + analysis.llm_findings):
294
- writer.writerow({
295
- 'commit_hash': analysis.commit_hash[:8],
296
- 'timestamp': analysis.timestamp.isoformat(),
297
- 'type': finding.get('type', 'unknown'),
298
- 'severity': finding.get('severity', 'medium'),
299
- 'category': finding.get('vulnerability_type',
300
- finding.get('secret_type', 'unknown')),
301
- 'file': finding.get('file', ''),
302
- 'line': finding.get('line', ''),
303
- 'message': finding.get('message', ''),
304
- 'tool': finding.get('tool', finding.get('source', '')),
305
- 'confidence': finding.get('confidence', '')
306
- })
307
-
308
- def _generate_sarif_report(self, analyses: List[SecurityAnalysis], path: Path) -> None:
309
- """Generate SARIF format report for GitHub Security tab integration."""
310
- sarif = {
311
- "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
312
- "version": "2.1.0",
313
- "runs": [{
314
- "tool": {
315
- "driver": {
316
- "name": "GitFlow Analytics Security",
317
- "version": "1.0.0",
318
- "informationUri": "https://github.com/yourusername/gitflow-analytics"
319
- }
320
- },
321
- "results": []
322
- }]
323
- }
324
-
325
- for analysis in analyses:
326
- for finding in (analysis.secrets + analysis.vulnerabilities):
327
- result = {
328
- "ruleId": finding.get('vulnerability_type',
329
- finding.get('secret_type', 'unknown')),
330
- "level": self._severity_to_sarif_level(finding.get('severity', 'medium')),
331
- "message": {
332
- "text": finding.get('message', 'Security issue detected')
333
- },
334
- "locations": [{
335
- "physicalLocation": {
336
- "artifactLocation": {
337
- "uri": finding.get('file', 'unknown')
338
- },
339
- "region": {
340
- "startLine": finding.get('line', 1)
341
- }
342
- }
343
- }]
344
- }
345
- sarif["runs"][0]["results"].append(result)
346
-
347
- with open(path, 'w') as f:
348
- json.dump(sarif, f, indent=2)
349
-
350
- def _severity_to_sarif_level(self, severity: str) -> str:
351
- """Convert severity to SARIF level."""
352
- mapping = {
353
- "critical": "error",
354
- "high": "error",
355
- "medium": "warning",
356
- "low": "note"
357
- }
358
- return mapping.get(severity.lower(), "warning")