gitflow-analytics 3.3.0__py3-none-any.whl → 3.4.7__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 (33) hide show
  1. gitflow_analytics/_version.py +1 -1
  2. gitflow_analytics/cli.py +164 -15
  3. gitflow_analytics/cli_wizards/__init__.py +10 -0
  4. gitflow_analytics/cli_wizards/install_wizard.py +936 -0
  5. gitflow_analytics/cli_wizards/run_launcher.py +343 -0
  6. gitflow_analytics/config/schema.py +12 -0
  7. gitflow_analytics/constants.py +75 -0
  8. gitflow_analytics/core/cache.py +7 -3
  9. gitflow_analytics/core/data_fetcher.py +66 -30
  10. gitflow_analytics/core/git_timeout_wrapper.py +6 -4
  11. gitflow_analytics/core/progress.py +2 -4
  12. gitflow_analytics/core/subprocess_git.py +31 -5
  13. gitflow_analytics/identity_llm/analysis_pass.py +13 -3
  14. gitflow_analytics/identity_llm/analyzer.py +14 -2
  15. gitflow_analytics/identity_llm/models.py +7 -1
  16. gitflow_analytics/qualitative/classifiers/llm/openai_client.py +5 -3
  17. gitflow_analytics/security/config.py +6 -6
  18. gitflow_analytics/security/extractors/dependency_checker.py +14 -14
  19. gitflow_analytics/security/extractors/secret_detector.py +8 -14
  20. gitflow_analytics/security/extractors/vulnerability_scanner.py +9 -9
  21. gitflow_analytics/security/llm_analyzer.py +10 -10
  22. gitflow_analytics/security/security_analyzer.py +17 -17
  23. gitflow_analytics/tui/screens/analysis_progress_screen.py +1 -1
  24. gitflow_analytics/ui/progress_display.py +36 -29
  25. gitflow_analytics/verify_activity.py +23 -26
  26. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.4.7.dist-info}/METADATA +1 -1
  27. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.4.7.dist-info}/RECORD +31 -29
  28. gitflow_analytics/security/reports/__init__.py +0 -5
  29. gitflow_analytics/security/reports/security_report.py +0 -358
  30. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.4.7.dist-info}/WHEEL +0 -0
  31. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.4.7.dist-info}/entry_points.txt +0 -0
  32. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.4.7.dist-info}/licenses/LICENSE +0 -0
  33. {gitflow_analytics-3.3.0.dist-info → gitflow_analytics-3.4.7.dist-info}/top_level.txt +0 -0
@@ -1,42 +1,46 @@
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=dupKhjqgdnT_nCus1JhoKEyp5K9agx_0vf-cvvs_Nzc,137
3
+ gitflow_analytics/cli.py,sha256=A0aXFPk4GSw1_s5Oiol4nnoGwZ8VCJHUq-wDreKAvzQ,244683
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
13
+ gitflow_analytics/cli_wizards/__init__.py,sha256=D73D97cS1hZsB_fCQQaAiWtd_w2Lb8TtcGc9Pn2DIyE,343
14
+ gitflow_analytics/cli_wizards/install_wizard.py,sha256=SAsPiBWqFfkD3UWOg8aR1Kuj6GBpHHEa6oss3iiuM9A,35644
15
+ gitflow_analytics/cli_wizards/run_launcher.py,sha256=jWtRyw5WSfTMElQ-HQm9gPHMx7AI1Lhp3jlNnaBFpzQ,11919
12
16
  gitflow_analytics/config/__init__.py,sha256=lzFOHsJGoeDHuu_NEgcSeUFwU0bgV3lnL9w0Pyc4FI0,1037
13
17
  gitflow_analytics/config/errors.py,sha256=IBKhAIwJ4gscZFnLDyE3jEp03wn2stPR7JQJXNSIfok,10386
14
18
  gitflow_analytics/config/loader.py,sha256=aS-A9_uuoj7T58pVdKv_J2qWxbXw0JUMveBsRYpGVHE,35862
15
19
  gitflow_analytics/config/profiles.py,sha256=yUjFAWW6uzOUdi5qlPE-QV9681HigyrLiSJFpL8X9A0,7967
16
20
  gitflow_analytics/config/repository.py,sha256=maptMAdCKDsuMAfoTAaTrMPVfVd_tKNLRenvuPe1-t4,4350
17
- gitflow_analytics/config/schema.py,sha256=g-YQDSHS0x5jfaLo4V57qB6Mvl11Pdwl7DFvz2SwDDw,14856
21
+ gitflow_analytics/config/schema.py,sha256=jIim7dD9jKrBZT4PSigI5Xzj8t6gbLVpyakLF8j25bY,15195
18
22
  gitflow_analytics/config/validator.py,sha256=l7AHjXYJ8wEmyA1rn2WiItZXtAiRb9YBLjFCDl53qKM,5907
19
23
  gitflow_analytics/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
24
  gitflow_analytics/core/analyzer.py,sha256=59kGObzjziOb8geFyZMKCUvWmo3hcXE0eTgrjYEc1XA,58736
21
25
  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
26
+ gitflow_analytics/core/cache.py,sha256=JLfr-7ayGGkdUX6e3_uURpWA0yiK7nrk5mvcHDfBYic,69124
27
+ gitflow_analytics/core/data_fetcher.py,sha256=YSanxAVKo44yR_T38YEpHnTZSmioP--0aQSuN29Y6LM,103294
24
28
  gitflow_analytics/core/git_auth.py,sha256=QP7U5_Mi9J-hEtoEhdjoMBl61nCukOGlL8PYXYSyN3g,6369
25
- gitflow_analytics/core/git_timeout_wrapper.py,sha256=wc08xxhzhzcyl7pOBosr_tl2LGF0tV0bXQQGE02y070,12422
29
+ gitflow_analytics/core/git_timeout_wrapper.py,sha256=14K8PHKSOonW4hJpLigB5XQNSWxmFbMFbrpu8cT1h-M,12534
26
30
  gitflow_analytics/core/identity.py,sha256=k7i-vcRJ2eiTU0_kYGY5QOhxcqnitibTTx7DVONW0kg,31237
27
31
  gitflow_analytics/core/metrics_storage.py,sha256=2u4dxVHsCTEaVIO5udWCaHzuefRL7JVS8aN7wIwyMlc,21769
28
- gitflow_analytics/core/progress.py,sha256=HjVL1yyJNSurMN_gY4VDPZc22V7L448i8nh27EcUTCE,20788
32
+ gitflow_analytics/core/progress.py,sha256=KMXwZpJGlmUU8OehNRA7_PONpXUgSIxWl5ZN7INc108,20732
29
33
  gitflow_analytics/core/schema_version.py,sha256=fhYKxerCgPHJoX4SAoJQO38sQcKDNguMEUWSj367Ilc,10660
30
- gitflow_analytics/core/subprocess_git.py,sha256=QBPuIx06N4Eghr1Dv-dkvUI_jyi8e2pxKEB-HL9r0sw,4792
34
+ gitflow_analytics/core/subprocess_git.py,sha256=a6WUQB0tiSox7_ESBgI4AjYEdni_rIOjUW17cjMXVhs,5917
31
35
  gitflow_analytics/extractors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
36
  gitflow_analytics/extractors/base.py,sha256=AKbYkFiMhNxVj7zfNzsJfh0rpyTdNr4Faea3bcZPPBo,1168
33
37
  gitflow_analytics/extractors/ml_tickets.py,sha256=js5OFmbZt9JHy5r_crehhuB1MxrkdfrPj2u_4B6K35c,43304
34
38
  gitflow_analytics/extractors/story_points.py,sha256=IggP-Ei832oV9aD08a3li08kmjF3BqyU9i8EgAZcpfs,5324
35
39
  gitflow_analytics/extractors/tickets.py,sha256=2s5Iu7eZXVi8yl9Yme5HKzrJo3mDjzsSOUr_iJGUeLM,43799
36
40
  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
41
+ gitflow_analytics/identity_llm/analysis_pass.py,sha256=lYfjM6f82agXstTrUBsS0R9-ipfRnviIqe8ExkjKnvc,9459
42
+ gitflow_analytics/identity_llm/analyzer.py,sha256=-a7lUJt_Dlgx9aNOH1YlFqPe7BSxtwY2RoGruIzwrzs,17932
43
+ gitflow_analytics/identity_llm/models.py,sha256=F1RN6g8og9esj-m4TPY_928Ci9TA43G9NFNHYf4zHHQ,2677
40
44
  gitflow_analytics/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
45
  gitflow_analytics/integrations/github_integration.py,sha256=52lyq5GNJIlTXIv7iwrkuxg0firpTcwYtTU9RAn8EIk,13324
42
46
  gitflow_analytics/integrations/jira_integration.py,sha256=3DV1hGNs1HxAOSGt2BfqBrWSigRN5H8BT1-G7E_8hGg,28761
@@ -69,7 +73,7 @@ gitflow_analytics/qualitative/classifiers/llm/base.py,sha256=1_GEb-Q5hxj109nvJaZ
69
73
  gitflow_analytics/qualitative/classifiers/llm/batch_processor.py,sha256=oSlJPYOAp-JXQRYVj9PFNSkoiFp8BdZViH-0cNNpo5E,13138
70
74
  gitflow_analytics/qualitative/classifiers/llm/cache.py,sha256=5UWRMgz0bOc_GRShE5gvYEzLhxB-VBDkhZKRECNmjOI,16930
71
75
  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
76
+ gitflow_analytics/qualitative/classifiers/llm/openai_client.py,sha256=pkLvXpAR9tK2VLPwpUoK3GYmThMb8VcekRj2qpvVrNw,15255
73
77
  gitflow_analytics/qualitative/classifiers/llm/prompts.py,sha256=7dLvCb8bQyZGOsQF_ajZpFxaXyHIbtfUfTO2yQJPxJs,12906
74
78
  gitflow_analytics/qualitative/classifiers/llm/response_parser.py,sha256=AyGfTpmvpx4QvRKJDWGS3CbylLm2SOAD0nSZVrkGTrM,9931
75
79
  gitflow_analytics/qualitative/core/__init__.py,sha256=22tZJDPyYE0k5-9lx_84R2SsZN8PRc_1I1L6prSkoSE,315
@@ -103,15 +107,13 @@ gitflow_analytics/reports/narrative_writer.py,sha256=4s-4VQiHqckvOEy53ueFA9_UZEt
103
107
  gitflow_analytics/reports/story_point_correlation.py,sha256=V9fnqNOxJxK00w0Mx69BMpcZgdgQJyja_Pu4qD-SWw0,51210
104
108
  gitflow_analytics/reports/weekly_trends_writer.py,sha256=m_TQ6ThSaa5rkAwfQpPRms2Jwgq3RReYwVBsts67cLk,15720
105
109
  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
110
+ gitflow_analytics/security/config.py,sha256=JyydqgitGvOJFBORlBDDsv4dOtRELXqIsI0I0LNHcJE,6288
111
+ gitflow_analytics/security/llm_analyzer.py,sha256=08n260LmlKnjX2v-MH8bygH6nmodpwDasPCNlOe7Qks,12986
112
+ gitflow_analytics/security/security_analyzer.py,sha256=6Sxlq8rvR3Cif-FXy_VWuPUqVUxfuxm68rbiPBLwNQs,15261
109
113
  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
114
+ gitflow_analytics/security/extractors/dependency_checker.py,sha256=Tb-Rb4G7jJkH_hsvH3dmEV2SyqjN8ycTHjgItObnCHg,14601
115
+ gitflow_analytics/security/extractors/secret_detector.py,sha256=aa2YiKeR6SurWHMHU5fr71Q83stSmIRKvwWFpp0IVFs,6857
116
+ gitflow_analytics/security/extractors/vulnerability_scanner.py,sha256=TWK1fPMN5s_EM8AwTFpkxRjXEsHIMwxYUZQqM2l5dV0,12682
115
117
  gitflow_analytics/training/__init__.py,sha256=YT5p7Wm4U8trzLnbS5FASJBWPMKhqp3rlAThjpxWnxo,143
116
118
  gitflow_analytics/training/model_loader.py,sha256=xGZLSopGxDhC--2XN6ytRgi2CyjOKY4zS4fZ-ZlO6lM,13245
117
119
  gitflow_analytics/training/pipeline.py,sha256=PQegTk_-OsPexVyRDfiy-3Df-7pcs25C4vPASr-HT9E,19951
@@ -119,7 +121,7 @@ gitflow_analytics/tui/__init__.py,sha256=1liMpth2RvUkmKfNUEnYEZkYi2RpYITFMmGKtBB
119
121
  gitflow_analytics/tui/app.py,sha256=rD0UTQqu9u3yrnJ7akcUNffZbJu-mfAFG5PTTLEcRo0,22200
120
122
  gitflow_analytics/tui/progress_adapter.py,sha256=fb6z2N87VAiQo0IyIpho23XTSHvQ-ydU6rVzHRzzQu8,11300
121
123
  gitflow_analytics/tui/screens/__init__.py,sha256=JVnPy-o4V6D2jehliXAbRET9x8zWmHR7PPk2is-l9OM,327
122
- gitflow_analytics/tui/screens/analysis_progress_screen.py,sha256=13FTjcW3NSsewy2RNQSm7JzFbmOMIGPo30wtdhR-itc,36659
124
+ gitflow_analytics/tui/screens/analysis_progress_screen.py,sha256=j1Mg5WZOKPGMtQoKxLUjy4UM9tKllcl3Ktqr0WJgOaY,36660
123
125
  gitflow_analytics/tui/screens/configuration_screen.py,sha256=QLtTz8xFAGdIxYsRmUyBr4m-1PteAk3_kxfE1UexqgA,19345
124
126
  gitflow_analytics/tui/screens/loading_screen.py,sha256=5kh0kKKCa6-NMlZyPfu2fE4ROgmjU8_jA2xCUX3z5iY,14451
125
127
  gitflow_analytics/tui/screens/main_screen.py,sha256=6aIzJrDtgXDlgGcW--wQUqncBBa_hcUytiLu76fMALw,11482
@@ -129,10 +131,10 @@ gitflow_analytics/tui/widgets/data_table.py,sha256=8fGNG4m7H41vCid3QwCHJa7bd8qu_
129
131
  gitflow_analytics/tui/widgets/export_modal.py,sha256=L-XKPOc6u-fow2TudPgDnC0kXZM1WZuGd_jahtV8lhg,10737
130
132
  gitflow_analytics/tui/widgets/progress_widget.py,sha256=Qny6Q1nU0Pr3aj4aHfXLaRjya9MH3rldR2HWYiaQyGE,6167
131
133
  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,,
134
+ gitflow_analytics/ui/progress_display.py,sha256=3xJnCOSs1DRVAfS-rTu37EsLfWDFW5-mbv-bPS9NMm4,59182
135
+ gitflow_analytics-3.4.7.dist-info/licenses/LICENSE,sha256=xwvSwY1GYXpRpmbnFvvnbmMwpobnrdN9T821sGvjOY0,1066
136
+ gitflow_analytics-3.4.7.dist-info/METADATA,sha256=sDGcvDukmYF2I-sQOvXL-IKzNtYnhrWZxM50Wlgjqxk,34122
137
+ gitflow_analytics-3.4.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
138
+ gitflow_analytics-3.4.7.dist-info/entry_points.txt,sha256=a3y8HnfLOvK1QVOgAkDY6VQXXm3o9ZSQRZrpiaS3hEM,65
139
+ gitflow_analytics-3.4.7.dist-info/top_level.txt,sha256=CQyxZXjKvpSB1kgqqtuE0PCRqfRsXZJL8JrYpJKtkrk,18
140
+ gitflow_analytics-3.4.7.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")