regscale-cli 6.16.0.0__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.

Potentially problematic release.


This version of regscale-cli might be problematic. Click here for more details.

Files changed (481) hide show
  1. regscale/__init__.py +1 -0
  2. regscale/airflow/__init__.py +9 -0
  3. regscale/airflow/azure/__init__.py +9 -0
  4. regscale/airflow/azure/cli.py +89 -0
  5. regscale/airflow/azure/upload_dags.py +116 -0
  6. regscale/airflow/click_dags.py +127 -0
  7. regscale/airflow/click_mixins.py +82 -0
  8. regscale/airflow/config.py +25 -0
  9. regscale/airflow/factories/__init__.py +0 -0
  10. regscale/airflow/factories/connections.py +58 -0
  11. regscale/airflow/factories/workflows.py +78 -0
  12. regscale/airflow/hierarchy.py +88 -0
  13. regscale/airflow/operators/__init__.py +0 -0
  14. regscale/airflow/operators/click.py +36 -0
  15. regscale/airflow/sensors/__init__.py +0 -0
  16. regscale/airflow/sensors/sql.py +107 -0
  17. regscale/airflow/sessions/__init__.py +0 -0
  18. regscale/airflow/sessions/sql/__init__.py +3 -0
  19. regscale/airflow/sessions/sql/queries.py +64 -0
  20. regscale/airflow/sessions/sql/sql_server_queries.py +248 -0
  21. regscale/airflow/tasks/__init__.py +0 -0
  22. regscale/airflow/tasks/branches.py +22 -0
  23. regscale/airflow/tasks/cli.py +116 -0
  24. regscale/airflow/tasks/click.py +73 -0
  25. regscale/airflow/tasks/debugging.py +9 -0
  26. regscale/airflow/tasks/groups.py +116 -0
  27. regscale/airflow/tasks/init.py +60 -0
  28. regscale/airflow/tasks/states.py +47 -0
  29. regscale/airflow/tasks/workflows.py +36 -0
  30. regscale/ansible/__init__.py +9 -0
  31. regscale/core/__init__.py +0 -0
  32. regscale/core/app/__init__.py +3 -0
  33. regscale/core/app/api.py +571 -0
  34. regscale/core/app/application.py +665 -0
  35. regscale/core/app/internal/__init__.py +136 -0
  36. regscale/core/app/internal/admin_actions.py +230 -0
  37. regscale/core/app/internal/assessments_editor.py +873 -0
  38. regscale/core/app/internal/catalog.py +316 -0
  39. regscale/core/app/internal/comparison.py +459 -0
  40. regscale/core/app/internal/control_editor.py +571 -0
  41. regscale/core/app/internal/encrypt.py +79 -0
  42. regscale/core/app/internal/evidence.py +1240 -0
  43. regscale/core/app/internal/file_uploads.py +151 -0
  44. regscale/core/app/internal/healthcheck.py +66 -0
  45. regscale/core/app/internal/login.py +305 -0
  46. regscale/core/app/internal/migrations.py +240 -0
  47. regscale/core/app/internal/model_editor.py +1701 -0
  48. regscale/core/app/internal/poam_editor.py +632 -0
  49. regscale/core/app/internal/workflow.py +105 -0
  50. regscale/core/app/logz.py +74 -0
  51. regscale/core/app/utils/XMLIR.py +258 -0
  52. regscale/core/app/utils/__init__.py +0 -0
  53. regscale/core/app/utils/api_handler.py +358 -0
  54. regscale/core/app/utils/app_utils.py +1110 -0
  55. regscale/core/app/utils/catalog_utils/__init__.py +0 -0
  56. regscale/core/app/utils/catalog_utils/common.py +91 -0
  57. regscale/core/app/utils/catalog_utils/compare_catalog.py +193 -0
  58. regscale/core/app/utils/catalog_utils/diagnostic_catalog.py +97 -0
  59. regscale/core/app/utils/catalog_utils/download_catalog.py +103 -0
  60. regscale/core/app/utils/catalog_utils/update_catalog.py +718 -0
  61. regscale/core/app/utils/catalog_utils/update_catalog_v2.py +1378 -0
  62. regscale/core/app/utils/catalog_utils/update_catalog_v3.py +1272 -0
  63. regscale/core/app/utils/catalog_utils/update_plans.py +334 -0
  64. regscale/core/app/utils/file_utils.py +238 -0
  65. regscale/core/app/utils/parser_utils.py +81 -0
  66. regscale/core/app/utils/pickle_file_handler.py +57 -0
  67. regscale/core/app/utils/regscale_utils.py +319 -0
  68. regscale/core/app/utils/report_utils.py +119 -0
  69. regscale/core/app/utils/variables.py +226 -0
  70. regscale/core/decorators.py +31 -0
  71. regscale/core/lazy_group.py +65 -0
  72. regscale/core/login.py +63 -0
  73. regscale/core/server/__init__.py +0 -0
  74. regscale/core/server/flask_api.py +473 -0
  75. regscale/core/server/helpers.py +373 -0
  76. regscale/core/server/rest.py +64 -0
  77. regscale/core/server/static/css/bootstrap.css +6030 -0
  78. regscale/core/server/static/css/bootstrap.min.css +6 -0
  79. regscale/core/server/static/css/main.css +176 -0
  80. regscale/core/server/static/images/regscale-cli.svg +49 -0
  81. regscale/core/server/static/images/regscale.svg +38 -0
  82. regscale/core/server/templates/base.html +74 -0
  83. regscale/core/server/templates/index.html +43 -0
  84. regscale/core/server/templates/login.html +28 -0
  85. regscale/core/server/templates/make_base64.html +22 -0
  86. regscale/core/server/templates/upload_STIG.html +109 -0
  87. regscale/core/server/templates/upload_STIG_result.html +26 -0
  88. regscale/core/server/templates/upload_ssp.html +144 -0
  89. regscale/core/server/templates/upload_ssp_result.html +128 -0
  90. regscale/core/static/__init__.py +0 -0
  91. regscale/core/static/regex.py +14 -0
  92. regscale/core/utils/__init__.py +117 -0
  93. regscale/core/utils/click_utils.py +13 -0
  94. regscale/core/utils/date.py +238 -0
  95. regscale/core/utils/graphql.py +254 -0
  96. regscale/core/utils/urls.py +23 -0
  97. regscale/dev/__init__.py +6 -0
  98. regscale/dev/analysis.py +454 -0
  99. regscale/dev/cli.py +235 -0
  100. regscale/dev/code_gen.py +492 -0
  101. regscale/dev/dirs.py +69 -0
  102. regscale/dev/docs.py +384 -0
  103. regscale/dev/monitoring.py +26 -0
  104. regscale/dev/profiling.py +216 -0
  105. regscale/exceptions/__init__.py +4 -0
  106. regscale/exceptions/license_exception.py +7 -0
  107. regscale/exceptions/validation_exception.py +9 -0
  108. regscale/integrations/__init__.py +1 -0
  109. regscale/integrations/commercial/__init__.py +486 -0
  110. regscale/integrations/commercial/ad.py +433 -0
  111. regscale/integrations/commercial/amazon/__init__.py +0 -0
  112. regscale/integrations/commercial/amazon/common.py +106 -0
  113. regscale/integrations/commercial/aqua/__init__.py +0 -0
  114. regscale/integrations/commercial/aqua/aqua.py +91 -0
  115. regscale/integrations/commercial/aws/__init__.py +6 -0
  116. regscale/integrations/commercial/aws/cli.py +322 -0
  117. regscale/integrations/commercial/aws/inventory/__init__.py +110 -0
  118. regscale/integrations/commercial/aws/inventory/base.py +64 -0
  119. regscale/integrations/commercial/aws/inventory/resources/__init__.py +19 -0
  120. regscale/integrations/commercial/aws/inventory/resources/compute.py +234 -0
  121. regscale/integrations/commercial/aws/inventory/resources/containers.py +113 -0
  122. regscale/integrations/commercial/aws/inventory/resources/database.py +101 -0
  123. regscale/integrations/commercial/aws/inventory/resources/integration.py +237 -0
  124. regscale/integrations/commercial/aws/inventory/resources/networking.py +253 -0
  125. regscale/integrations/commercial/aws/inventory/resources/security.py +240 -0
  126. regscale/integrations/commercial/aws/inventory/resources/storage.py +91 -0
  127. regscale/integrations/commercial/aws/scanner.py +823 -0
  128. regscale/integrations/commercial/azure/__init__.py +0 -0
  129. regscale/integrations/commercial/azure/common.py +32 -0
  130. regscale/integrations/commercial/azure/intune.py +488 -0
  131. regscale/integrations/commercial/azure/scanner.py +49 -0
  132. regscale/integrations/commercial/burp.py +78 -0
  133. regscale/integrations/commercial/cpe.py +144 -0
  134. regscale/integrations/commercial/crowdstrike.py +1117 -0
  135. regscale/integrations/commercial/defender.py +1511 -0
  136. regscale/integrations/commercial/dependabot.py +210 -0
  137. regscale/integrations/commercial/durosuite/__init__.py +0 -0
  138. regscale/integrations/commercial/durosuite/api.py +1546 -0
  139. regscale/integrations/commercial/durosuite/process_devices.py +101 -0
  140. regscale/integrations/commercial/durosuite/scanner.py +637 -0
  141. regscale/integrations/commercial/durosuite/variables.py +21 -0
  142. regscale/integrations/commercial/ecr.py +90 -0
  143. regscale/integrations/commercial/gcp/__init__.py +237 -0
  144. regscale/integrations/commercial/gcp/auth.py +96 -0
  145. regscale/integrations/commercial/gcp/control_tests.py +238 -0
  146. regscale/integrations/commercial/gcp/variables.py +18 -0
  147. regscale/integrations/commercial/gitlab.py +332 -0
  148. regscale/integrations/commercial/grype.py +165 -0
  149. regscale/integrations/commercial/ibm.py +90 -0
  150. regscale/integrations/commercial/import_all/__init__.py +0 -0
  151. regscale/integrations/commercial/import_all/import_all_cmd.py +467 -0
  152. regscale/integrations/commercial/import_all/scan_file_fingerprints.json +27 -0
  153. regscale/integrations/commercial/jira.py +1046 -0
  154. regscale/integrations/commercial/mappings/__init__.py +0 -0
  155. regscale/integrations/commercial/mappings/csf_controls.json +713 -0
  156. regscale/integrations/commercial/mappings/nist_800_53_r5_controls.json +1516 -0
  157. regscale/integrations/commercial/nessus/__init__.py +0 -0
  158. regscale/integrations/commercial/nessus/nessus_utils.py +429 -0
  159. regscale/integrations/commercial/nessus/scanner.py +416 -0
  160. regscale/integrations/commercial/nexpose.py +90 -0
  161. regscale/integrations/commercial/okta.py +798 -0
  162. regscale/integrations/commercial/opentext/__init__.py +0 -0
  163. regscale/integrations/commercial/opentext/click.py +99 -0
  164. regscale/integrations/commercial/opentext/scanner.py +143 -0
  165. regscale/integrations/commercial/prisma.py +91 -0
  166. regscale/integrations/commercial/qualys.py +1462 -0
  167. regscale/integrations/commercial/salesforce.py +980 -0
  168. regscale/integrations/commercial/sap/__init__.py +0 -0
  169. regscale/integrations/commercial/sap/click.py +31 -0
  170. regscale/integrations/commercial/sap/sysdig/__init__.py +0 -0
  171. regscale/integrations/commercial/sap/sysdig/click.py +57 -0
  172. regscale/integrations/commercial/sap/sysdig/sysdig_scanner.py +190 -0
  173. regscale/integrations/commercial/sap/tenable/__init__.py +0 -0
  174. regscale/integrations/commercial/sap/tenable/click.py +49 -0
  175. regscale/integrations/commercial/sap/tenable/scanner.py +196 -0
  176. regscale/integrations/commercial/servicenow.py +1756 -0
  177. regscale/integrations/commercial/sicura/__init__.py +0 -0
  178. regscale/integrations/commercial/sicura/api.py +855 -0
  179. regscale/integrations/commercial/sicura/commands.py +73 -0
  180. regscale/integrations/commercial/sicura/scanner.py +481 -0
  181. regscale/integrations/commercial/sicura/variables.py +16 -0
  182. regscale/integrations/commercial/snyk.py +90 -0
  183. regscale/integrations/commercial/sonarcloud.py +260 -0
  184. regscale/integrations/commercial/sqlserver.py +369 -0
  185. regscale/integrations/commercial/stig_mapper_integration/__init__.py +0 -0
  186. regscale/integrations/commercial/stig_mapper_integration/click_commands.py +38 -0
  187. regscale/integrations/commercial/stig_mapper_integration/mapping_engine.py +353 -0
  188. regscale/integrations/commercial/stigv2/__init__.py +0 -0
  189. regscale/integrations/commercial/stigv2/ckl_parser.py +349 -0
  190. regscale/integrations/commercial/stigv2/click_commands.py +95 -0
  191. regscale/integrations/commercial/stigv2/stig_integration.py +202 -0
  192. regscale/integrations/commercial/synqly/__init__.py +0 -0
  193. regscale/integrations/commercial/synqly/assets.py +46 -0
  194. regscale/integrations/commercial/synqly/ticketing.py +132 -0
  195. regscale/integrations/commercial/synqly/vulnerabilities.py +223 -0
  196. regscale/integrations/commercial/synqly_jira.py +840 -0
  197. regscale/integrations/commercial/tenablev2/__init__.py +0 -0
  198. regscale/integrations/commercial/tenablev2/authenticate.py +31 -0
  199. regscale/integrations/commercial/tenablev2/click.py +1584 -0
  200. regscale/integrations/commercial/tenablev2/scanner.py +504 -0
  201. regscale/integrations/commercial/tenablev2/stig_parsers.py +140 -0
  202. regscale/integrations/commercial/tenablev2/utils.py +78 -0
  203. regscale/integrations/commercial/tenablev2/variables.py +17 -0
  204. regscale/integrations/commercial/trivy.py +162 -0
  205. regscale/integrations/commercial/veracode.py +96 -0
  206. regscale/integrations/commercial/wizv2/WizDataMixin.py +97 -0
  207. regscale/integrations/commercial/wizv2/__init__.py +0 -0
  208. regscale/integrations/commercial/wizv2/click.py +429 -0
  209. regscale/integrations/commercial/wizv2/constants.py +1001 -0
  210. regscale/integrations/commercial/wizv2/issue.py +361 -0
  211. regscale/integrations/commercial/wizv2/models.py +112 -0
  212. regscale/integrations/commercial/wizv2/parsers.py +339 -0
  213. regscale/integrations/commercial/wizv2/sbom.py +115 -0
  214. regscale/integrations/commercial/wizv2/scanner.py +416 -0
  215. regscale/integrations/commercial/wizv2/utils.py +796 -0
  216. regscale/integrations/commercial/wizv2/variables.py +39 -0
  217. regscale/integrations/commercial/wizv2/wiz_auth.py +159 -0
  218. regscale/integrations/commercial/xray.py +91 -0
  219. regscale/integrations/integration/__init__.py +2 -0
  220. regscale/integrations/integration/integration.py +26 -0
  221. regscale/integrations/integration/inventory.py +17 -0
  222. regscale/integrations/integration/issue.py +100 -0
  223. regscale/integrations/integration_override.py +149 -0
  224. regscale/integrations/public/__init__.py +103 -0
  225. regscale/integrations/public/cisa.py +641 -0
  226. regscale/integrations/public/criticality_updater.py +70 -0
  227. regscale/integrations/public/emass.py +411 -0
  228. regscale/integrations/public/emass_slcm_import.py +697 -0
  229. regscale/integrations/public/fedramp/__init__.py +0 -0
  230. regscale/integrations/public/fedramp/appendix_parser.py +548 -0
  231. regscale/integrations/public/fedramp/click.py +479 -0
  232. regscale/integrations/public/fedramp/components.py +714 -0
  233. regscale/integrations/public/fedramp/docx_parser.py +259 -0
  234. regscale/integrations/public/fedramp/fedramp_cis_crm.py +1124 -0
  235. regscale/integrations/public/fedramp/fedramp_common.py +3181 -0
  236. regscale/integrations/public/fedramp/fedramp_docx.py +388 -0
  237. regscale/integrations/public/fedramp/fedramp_five.py +2343 -0
  238. regscale/integrations/public/fedramp/fedramp_traversal.py +138 -0
  239. regscale/integrations/public/fedramp/import_fedramp_r4_ssp.py +279 -0
  240. regscale/integrations/public/fedramp/import_workbook.py +495 -0
  241. regscale/integrations/public/fedramp/inventory_items.py +244 -0
  242. regscale/integrations/public/fedramp/mappings/__init__.py +0 -0
  243. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +7388 -0
  244. regscale/integrations/public/fedramp/mappings/fedramp_r5_params.json +8636 -0
  245. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +9605 -0
  246. regscale/integrations/public/fedramp/mappings/system_roles.py +34 -0
  247. regscale/integrations/public/fedramp/mappings/user.py +175 -0
  248. regscale/integrations/public/fedramp/mappings/values.py +141 -0
  249. regscale/integrations/public/fedramp/markdown_parser.py +150 -0
  250. regscale/integrations/public/fedramp/metadata.py +689 -0
  251. regscale/integrations/public/fedramp/models/__init__.py +59 -0
  252. regscale/integrations/public/fedramp/models/leveraged_auth_new.py +168 -0
  253. regscale/integrations/public/fedramp/models/poam_importer.py +522 -0
  254. regscale/integrations/public/fedramp/parts_mapper.py +107 -0
  255. regscale/integrations/public/fedramp/poam/__init__.py +0 -0
  256. regscale/integrations/public/fedramp/poam/scanner.py +851 -0
  257. regscale/integrations/public/fedramp/properties.py +201 -0
  258. regscale/integrations/public/fedramp/reporting.py +84 -0
  259. regscale/integrations/public/fedramp/resources.py +496 -0
  260. regscale/integrations/public/fedramp/rosetta.py +110 -0
  261. regscale/integrations/public/fedramp/ssp_logger.py +87 -0
  262. regscale/integrations/public/fedramp/system_characteristics.py +922 -0
  263. regscale/integrations/public/fedramp/system_control_implementations.py +582 -0
  264. regscale/integrations/public/fedramp/system_implementation.py +190 -0
  265. regscale/integrations/public/fedramp/xml_utils.py +87 -0
  266. regscale/integrations/public/nist_catalog.py +275 -0
  267. regscale/integrations/public/oscal.py +1946 -0
  268. regscale/integrations/public/otx.py +169 -0
  269. regscale/integrations/scanner_integration.py +2692 -0
  270. regscale/integrations/variables.py +25 -0
  271. regscale/models/__init__.py +7 -0
  272. regscale/models/app_models/__init__.py +5 -0
  273. regscale/models/app_models/catalog_compare.py +213 -0
  274. regscale/models/app_models/click.py +252 -0
  275. regscale/models/app_models/datetime_encoder.py +21 -0
  276. regscale/models/app_models/import_validater.py +321 -0
  277. regscale/models/app_models/mapping.py +260 -0
  278. regscale/models/app_models/pipeline.py +37 -0
  279. regscale/models/click_models.py +413 -0
  280. regscale/models/config.py +154 -0
  281. regscale/models/email_style.css +67 -0
  282. regscale/models/hierarchy.py +8 -0
  283. regscale/models/inspect_models.py +79 -0
  284. regscale/models/integration_models/__init__.py +0 -0
  285. regscale/models/integration_models/amazon_models/__init__.py +0 -0
  286. regscale/models/integration_models/amazon_models/inspector.py +262 -0
  287. regscale/models/integration_models/amazon_models/inspector_scan.py +206 -0
  288. regscale/models/integration_models/aqua.py +247 -0
  289. regscale/models/integration_models/azure_alerts.py +255 -0
  290. regscale/models/integration_models/base64.py +23 -0
  291. regscale/models/integration_models/burp.py +433 -0
  292. regscale/models/integration_models/burp_models.py +128 -0
  293. regscale/models/integration_models/cisa_kev_data.json +19333 -0
  294. regscale/models/integration_models/defender_data.py +93 -0
  295. regscale/models/integration_models/defenderimport.py +143 -0
  296. regscale/models/integration_models/drf.py +443 -0
  297. regscale/models/integration_models/ecr_models/__init__.py +0 -0
  298. regscale/models/integration_models/ecr_models/data.py +69 -0
  299. regscale/models/integration_models/ecr_models/ecr.py +239 -0
  300. regscale/models/integration_models/flat_file_importer.py +1079 -0
  301. regscale/models/integration_models/grype_import.py +247 -0
  302. regscale/models/integration_models/ibm.py +126 -0
  303. regscale/models/integration_models/implementation_results.py +85 -0
  304. regscale/models/integration_models/nexpose.py +140 -0
  305. regscale/models/integration_models/prisma.py +202 -0
  306. regscale/models/integration_models/qualys.py +720 -0
  307. regscale/models/integration_models/qualys_scanner.py +160 -0
  308. regscale/models/integration_models/sbom/__init__.py +0 -0
  309. regscale/models/integration_models/sbom/cyclone_dx.py +139 -0
  310. regscale/models/integration_models/send_reminders.py +620 -0
  311. regscale/models/integration_models/snyk.py +155 -0
  312. regscale/models/integration_models/synqly_models/__init__.py +0 -0
  313. regscale/models/integration_models/synqly_models/capabilities.json +1 -0
  314. regscale/models/integration_models/synqly_models/connector_types.py +22 -0
  315. regscale/models/integration_models/synqly_models/connectors/__init__.py +7 -0
  316. regscale/models/integration_models/synqly_models/connectors/assets.py +97 -0
  317. regscale/models/integration_models/synqly_models/connectors/ticketing.py +583 -0
  318. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +169 -0
  319. regscale/models/integration_models/synqly_models/ocsf_mapper.py +331 -0
  320. regscale/models/integration_models/synqly_models/param.py +72 -0
  321. regscale/models/integration_models/synqly_models/synqly_model.py +733 -0
  322. regscale/models/integration_models/synqly_models/tenants.py +39 -0
  323. regscale/models/integration_models/tenable_models/__init__.py +0 -0
  324. regscale/models/integration_models/tenable_models/integration.py +187 -0
  325. regscale/models/integration_models/tenable_models/models.py +513 -0
  326. regscale/models/integration_models/trivy_import.py +231 -0
  327. regscale/models/integration_models/veracode.py +217 -0
  328. regscale/models/integration_models/xray.py +135 -0
  329. regscale/models/locking.py +100 -0
  330. regscale/models/platform.py +110 -0
  331. regscale/models/regscale_models/__init__.py +67 -0
  332. regscale/models/regscale_models/assessment.py +570 -0
  333. regscale/models/regscale_models/assessment_plan.py +52 -0
  334. regscale/models/regscale_models/asset.py +567 -0
  335. regscale/models/regscale_models/asset_mapping.py +190 -0
  336. regscale/models/regscale_models/case.py +42 -0
  337. regscale/models/regscale_models/catalog.py +261 -0
  338. regscale/models/regscale_models/cci.py +46 -0
  339. regscale/models/regscale_models/change.py +167 -0
  340. regscale/models/regscale_models/checklist.py +372 -0
  341. regscale/models/regscale_models/comment.py +49 -0
  342. regscale/models/regscale_models/compliance_settings.py +112 -0
  343. regscale/models/regscale_models/component.py +412 -0
  344. regscale/models/regscale_models/component_mapping.py +65 -0
  345. regscale/models/regscale_models/control.py +38 -0
  346. regscale/models/regscale_models/control_implementation.py +1128 -0
  347. regscale/models/regscale_models/control_objective.py +261 -0
  348. regscale/models/regscale_models/control_parameter.py +100 -0
  349. regscale/models/regscale_models/control_test.py +34 -0
  350. regscale/models/regscale_models/control_test_plan.py +75 -0
  351. regscale/models/regscale_models/control_test_result.py +52 -0
  352. regscale/models/regscale_models/custom_field.py +245 -0
  353. regscale/models/regscale_models/data.py +109 -0
  354. regscale/models/regscale_models/data_center.py +40 -0
  355. regscale/models/regscale_models/deviation.py +203 -0
  356. regscale/models/regscale_models/email.py +97 -0
  357. regscale/models/regscale_models/evidence.py +47 -0
  358. regscale/models/regscale_models/evidence_mapping.py +40 -0
  359. regscale/models/regscale_models/facility.py +59 -0
  360. regscale/models/regscale_models/file.py +382 -0
  361. regscale/models/regscale_models/filetag.py +37 -0
  362. regscale/models/regscale_models/form_field_value.py +94 -0
  363. regscale/models/regscale_models/group.py +169 -0
  364. regscale/models/regscale_models/implementation_objective.py +335 -0
  365. regscale/models/regscale_models/implementation_option.py +275 -0
  366. regscale/models/regscale_models/implementation_role.py +33 -0
  367. regscale/models/regscale_models/incident.py +177 -0
  368. regscale/models/regscale_models/interconnection.py +43 -0
  369. regscale/models/regscale_models/issue.py +1176 -0
  370. regscale/models/regscale_models/leveraged_authorization.py +125 -0
  371. regscale/models/regscale_models/line_of_inquiry.py +52 -0
  372. regscale/models/regscale_models/link.py +205 -0
  373. regscale/models/regscale_models/meta_data.py +64 -0
  374. regscale/models/regscale_models/mixins/__init__.py +0 -0
  375. regscale/models/regscale_models/mixins/parent_cache.py +124 -0
  376. regscale/models/regscale_models/module.py +224 -0
  377. regscale/models/regscale_models/modules.py +191 -0
  378. regscale/models/regscale_models/objective.py +14 -0
  379. regscale/models/regscale_models/parameter.py +87 -0
  380. regscale/models/regscale_models/ports_protocol.py +81 -0
  381. regscale/models/regscale_models/privacy.py +89 -0
  382. regscale/models/regscale_models/profile.py +50 -0
  383. regscale/models/regscale_models/profile_link.py +68 -0
  384. regscale/models/regscale_models/profile_mapping.py +124 -0
  385. regscale/models/regscale_models/project.py +63 -0
  386. regscale/models/regscale_models/property.py +278 -0
  387. regscale/models/regscale_models/question.py +85 -0
  388. regscale/models/regscale_models/questionnaire.py +87 -0
  389. regscale/models/regscale_models/questionnaire_instance.py +177 -0
  390. regscale/models/regscale_models/rbac.py +132 -0
  391. regscale/models/regscale_models/reference.py +86 -0
  392. regscale/models/regscale_models/regscale_model.py +1643 -0
  393. regscale/models/regscale_models/requirement.py +29 -0
  394. regscale/models/regscale_models/risk.py +274 -0
  395. regscale/models/regscale_models/sbom.py +54 -0
  396. regscale/models/regscale_models/scan_history.py +436 -0
  397. regscale/models/regscale_models/search.py +53 -0
  398. regscale/models/regscale_models/security_control.py +132 -0
  399. regscale/models/regscale_models/security_plan.py +204 -0
  400. regscale/models/regscale_models/software_inventory.py +159 -0
  401. regscale/models/regscale_models/stake_holder.py +64 -0
  402. regscale/models/regscale_models/stig.py +647 -0
  403. regscale/models/regscale_models/supply_chain.py +152 -0
  404. regscale/models/regscale_models/system_role.py +188 -0
  405. regscale/models/regscale_models/system_role_external_assignment.py +40 -0
  406. regscale/models/regscale_models/tag.py +37 -0
  407. regscale/models/regscale_models/tag_mapping.py +19 -0
  408. regscale/models/regscale_models/task.py +133 -0
  409. regscale/models/regscale_models/threat.py +196 -0
  410. regscale/models/regscale_models/user.py +175 -0
  411. regscale/models/regscale_models/user_group.py +55 -0
  412. regscale/models/regscale_models/vulnerability.py +242 -0
  413. regscale/models/regscale_models/vulnerability_mapping.py +162 -0
  414. regscale/models/regscale_models/workflow.py +55 -0
  415. regscale/models/regscale_models/workflow_action.py +34 -0
  416. regscale/models/regscale_models/workflow_instance.py +269 -0
  417. regscale/models/regscale_models/workflow_instance_step.py +114 -0
  418. regscale/models/regscale_models/workflow_template.py +58 -0
  419. regscale/models/regscale_models/workflow_template_step.py +45 -0
  420. regscale/regscale.py +815 -0
  421. regscale/utils/__init__.py +7 -0
  422. regscale/utils/b64conversion.py +14 -0
  423. regscale/utils/click_utils.py +118 -0
  424. regscale/utils/decorators.py +48 -0
  425. regscale/utils/dict_utils.py +59 -0
  426. regscale/utils/files.py +79 -0
  427. regscale/utils/fxns.py +30 -0
  428. regscale/utils/graphql_client.py +113 -0
  429. regscale/utils/lists.py +16 -0
  430. regscale/utils/numbers.py +12 -0
  431. regscale/utils/shell.py +148 -0
  432. regscale/utils/string.py +121 -0
  433. regscale/utils/synqly_utils.py +165 -0
  434. regscale/utils/threading/__init__.py +8 -0
  435. regscale/utils/threading/threadhandler.py +131 -0
  436. regscale/utils/threading/threadsafe_counter.py +47 -0
  437. regscale/utils/threading/threadsafe_dict.py +242 -0
  438. regscale/utils/threading/threadsafe_list.py +83 -0
  439. regscale/utils/version.py +104 -0
  440. regscale/validation/__init__.py +0 -0
  441. regscale/validation/address.py +37 -0
  442. regscale/validation/record.py +48 -0
  443. regscale/visualization/__init__.py +5 -0
  444. regscale/visualization/click.py +34 -0
  445. regscale_cli-6.16.0.0.dist-info/LICENSE +21 -0
  446. regscale_cli-6.16.0.0.dist-info/METADATA +659 -0
  447. regscale_cli-6.16.0.0.dist-info/RECORD +481 -0
  448. regscale_cli-6.16.0.0.dist-info/WHEEL +5 -0
  449. regscale_cli-6.16.0.0.dist-info/entry_points.txt +6 -0
  450. regscale_cli-6.16.0.0.dist-info/top_level.txt +2 -0
  451. tests/fixtures/__init__.py +2 -0
  452. tests/fixtures/api.py +87 -0
  453. tests/fixtures/models.py +91 -0
  454. tests/fixtures/test_fixture.py +144 -0
  455. tests/mocks/__init__.py +0 -0
  456. tests/mocks/objects.py +3 -0
  457. tests/mocks/response.py +32 -0
  458. tests/mocks/xml.py +13 -0
  459. tests/regscale/__init__.py +0 -0
  460. tests/regscale/core/__init__.py +0 -0
  461. tests/regscale/core/test_api.py +232 -0
  462. tests/regscale/core/test_app.py +406 -0
  463. tests/regscale/core/test_login.py +37 -0
  464. tests/regscale/core/test_logz.py +66 -0
  465. tests/regscale/core/test_sbom_generator.py +87 -0
  466. tests/regscale/core/test_validation_utils.py +163 -0
  467. tests/regscale/core/test_version.py +78 -0
  468. tests/regscale/models/__init__.py +0 -0
  469. tests/regscale/models/test_asset.py +71 -0
  470. tests/regscale/models/test_config.py +26 -0
  471. tests/regscale/models/test_control_implementation.py +27 -0
  472. tests/regscale/models/test_import.py +97 -0
  473. tests/regscale/models/test_issue.py +36 -0
  474. tests/regscale/models/test_mapping.py +52 -0
  475. tests/regscale/models/test_platform.py +31 -0
  476. tests/regscale/models/test_regscale_model.py +346 -0
  477. tests/regscale/models/test_report.py +32 -0
  478. tests/regscale/models/test_tenable_integrations.py +118 -0
  479. tests/regscale/models/test_user_model.py +121 -0
  480. tests/regscale/test_about.py +19 -0
  481. tests/regscale/test_authorization.py +65 -0
@@ -0,0 +1,548 @@
1
+ """
2
+ This module is used to parse a DOCX file containing FedRAMP Security Controls and their implementation statuses.
3
+ """
4
+
5
+ import logging
6
+ import re
7
+ import sys
8
+ from typing import Dict, Union, Any, List, Optional
9
+
10
+ import docx
11
+ from lxml import etree
12
+ from rapidfuzz import fuzz
13
+
14
+ logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
15
+ logger = logging.getLogger(__name__)
16
+
17
+ SCHEMA = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" # noqa
18
+ TEXT_ELEMENT = ".//{%s}%s" % (SCHEMA, "t")
19
+ CHECKBOX_ELEMENT = ".//{%s}%s" % (SCHEMA, "checkBox")
20
+ NA_STATUS = "Not Applicable"
21
+
22
+ # define our statuses we are looking for in the document
23
+ STATUSES = [
24
+ "Implemented",
25
+ "Partially Implemented",
26
+ "Planned",
27
+ "In Remediation",
28
+ "Inherited",
29
+ "Alternative Implementation",
30
+ NA_STATUS,
31
+ "Archived",
32
+ "Risk Accepted",
33
+ ]
34
+ LOWER_STATUSES = [status.lower() for status in STATUSES]
35
+
36
+ ORIGINATIONS = [
37
+ "Service Provider Corporate",
38
+ "Service Provider System Specific",
39
+ "Service Provider Hybrid (Corporate and System Specific)",
40
+ "Configured by Customer (Customer System Specific)",
41
+ "Provided by Customer (Customer System Specific)",
42
+ "Shared (Service Provider and Customer Responsibility)",
43
+ "Inherited from pre-existing FedRAMP Authorization",
44
+ ]
45
+ LOWER_ORIGINATIONS = [origin.lower() for origin in ORIGINATIONS]
46
+ DEFAULT_ORIGINATION = "Service Provider Corporate"
47
+ POSITIVE_KEYWORDS = ["yes", "true", "1", "☒", "True", "Yes", "☑", "☑️"]
48
+
49
+ # Define your keywords or phrases that map to each status
50
+ STATUS_KEYWORDS = {
51
+ "Implemented": ["implemented", "complete", "done", "yes", "☒", "1"],
52
+ "Partially Implemented": [
53
+ "partially implemented",
54
+ "incomplete",
55
+ "partially done",
56
+ "partial",
57
+ "In process",
58
+ "in process",
59
+ "☒",
60
+ "1",
61
+ ],
62
+ "Planned": ["planned", "scheduled", "Planned", "☒", "1"],
63
+ "Alternative Implementation": [
64
+ "alternative implementation",
65
+ "alternative",
66
+ "Equivalent",
67
+ "☒",
68
+ "1",
69
+ ],
70
+ NA_STATUS: ["not applicable", "irrelevant", "not relevant", "no", "☒", "1"],
71
+ }
72
+ DEFAULT_STATUS = "Not Implemented"
73
+ CONTROL_ORIGIN_KEY = "Control Origination"
74
+ CONTROL_SUMMARY_KEY = "Control Summary Information"
75
+
76
+ STATEMENT_CHECK = "What is the solution and how is it implemented".lower()
77
+ DEFAULT_PART = "Default Part"
78
+
79
+
80
+ class AppendixAParser:
81
+ """
82
+ A class to parse a DOCX file containing FedRAMP Security Controls and their implementation statuses.
83
+ """
84
+
85
+ def __init__(self, filename: str):
86
+ self.controls_implementations = {}
87
+ self.control_id = ""
88
+ self.doc = docx.Document(filename)
89
+ self.header_row_text = ""
90
+ self.cell_data_status = None
91
+ self.processed_texts = []
92
+ self.joined_processed_texts = ""
93
+ self.xml = None
94
+ self.text_elements = None
95
+ self.checkbox_states = None
96
+ self.cell_data = {}
97
+ self.parts = self.generate_parts_full_alphabet()
98
+ self.parts_set = {p.lower() for p in self.parts}
99
+
100
+ def fetch_controls_implementations(self) -> Dict:
101
+ """
102
+ Fetch the implementation statuses of the controls from the DOCX file.
103
+ :return: A dictionary containing the control IDs and their implementation statuses.
104
+ :rtype: Dict
105
+
106
+ """
107
+ return self.get_implementation_statuses()
108
+
109
+ @staticmethod
110
+ def score_similarity(string1: str, string2: str) -> int:
111
+ """
112
+ Score the similarity between two strings using the RapidFuzz library.
113
+ :param str string1: The first string to compare.
114
+ :param str string2: The second string to compare.
115
+ :return: The similarity score between the two strings.
116
+ :rtype: int
117
+ """
118
+ # Scoring the similarity
119
+ score = fuzz.ratio(string1.lower(), string2.lower())
120
+
121
+ # Optionally, convert to a percentage
122
+ percentage = score # fuzz.ratio already gives a score out of 100
123
+
124
+ return round(percentage)
125
+
126
+ @staticmethod
127
+ def determine_origination(text: str) -> Optional[str]:
128
+ tokens = text.split()
129
+ rejoined_text = " ".join(tokens) # this removes any newlines or spaces
130
+ rejoined_text = rejoined_text.replace("( ", "(")
131
+ rejoined_text = rejoined_text.replace(" )", ")")
132
+
133
+ if CONTROL_ORIGIN_KEY not in text:
134
+ return None
135
+ for origin in ORIGINATIONS:
136
+ for keyword in POSITIVE_KEYWORDS:
137
+ valid_option = f"{keyword} {origin}".lower()
138
+ lower_text = rejoined_text.lower()
139
+ if valid_option in lower_text:
140
+ return origin # Return the first matching status
141
+ return None
142
+
143
+ @staticmethod
144
+ def determine_status(text: str) -> str:
145
+ # Tokenize the input text
146
+ tokens = text.split()
147
+
148
+ # Convert tokens to a single lowercased string for comparison
149
+ token_string = " ".join(tokens).lower()
150
+
151
+ matches = []
152
+
153
+ # Search for keywords in the tokenized text to determine the status
154
+ for status, keywords in STATUS_KEYWORDS.items():
155
+ for keyword in keywords:
156
+ if f"1 {keyword}" in token_string or f"☒ {keyword}" in token_string:
157
+ matches.append(status)
158
+
159
+ # Determine the status to return
160
+ if len(matches) > 1:
161
+ # More than one match found
162
+ # not applicable takes presendence over planned/partially implemented (only 2 valid multi select statuses for fedramp)
163
+ if matches[1] == NA_STATUS:
164
+ return matches[1]
165
+ else:
166
+ return matches[0]
167
+ elif matches:
168
+ return matches[0] # Return the first match if only one
169
+ else:
170
+ return DEFAULT_STATUS # No matches found
171
+
172
+ @staticmethod
173
+ def _process_text_element(input_text: str) -> Union[Dict, str]:
174
+ """
175
+ Process a text element from a DOCX cell, checking for structured checkbox information.
176
+ :param str input_text: The text content of the element.
177
+ :return: The processed text or a dictionary containing checkbox information.
178
+ :rtype: Union[Dict, str]
179
+ """
180
+ # Check if the text contains structured checkbox information
181
+ checkbox_info = re.findall(r"\[(.*?): (True|False)\]", input_text)
182
+ if checkbox_info:
183
+ return {item[0].strip(): item[1] == "True" for item in checkbox_info}
184
+ else:
185
+ return input_text
186
+
187
+ @staticmethod
188
+ def _get_checkbox_state(checkbox_element: Any) -> bool:
189
+ """
190
+ Get the state of a checkbox element from a DOCX cell.
191
+ :param Any checkbox_element: The checkbox element from the DOCX cell.
192
+ :return: The state of the checkbox.
193
+ :rtype: bool
194
+ """
195
+ # First, try getting the attribute 'val' directly
196
+ val = "{%s}%s" % (SCHEMA, "val")
197
+ checked = "{%s}%s" % (SCHEMA, "checked")
198
+ default = "{%s}%s" % (SCHEMA, "default")
199
+ state = checkbox_element.get(val)
200
+ if state is not None:
201
+ return state == "1"
202
+
203
+ # If not found, look for a child element 'checked' that may contain the 'val' attribute
204
+ checked_element = checkbox_element.find(checked)
205
+ if checked_element is not None:
206
+ state = checked_element.get(val)
207
+ return state == "1"
208
+
209
+ # If still not found, check for a 'default' state as a fallback
210
+ default_element = checkbox_element.find(default)
211
+ if default_element is not None:
212
+ state = default_element.get(val)
213
+ return state == "1"
214
+
215
+ # If there's no indication of the state, return False or handle accordingly
216
+ return False
217
+
218
+ def get_implementation_statuses(self) -> Dict:
219
+ """
220
+ Get the implementation statuses of the controls from the DOCX file.
221
+ :return: A dictionary containing the control IDs and their implementation statuses.
222
+ :rtype: Dict
223
+ """
224
+ for table in self.doc.tables:
225
+ for i, row in enumerate(table.rows):
226
+ self._handle_row(i, row)
227
+
228
+ logger.debug(f"Found {len(self.controls_implementations.items())} Controls")
229
+ return self.controls_implementations
230
+
231
+ def _handle_row(self, i: int, row: Any):
232
+ """
233
+ Handle a row in the DOCX table.
234
+ :param int i: The index of the row.
235
+ :param Any row: The row element from the DOCX table.
236
+ """
237
+ self.header_row_text = " ".join([c.text.strip() for c in row.cells]) if i == 0 else self.header_row_text
238
+ if CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower():
239
+ self.control_id = self.header_row_text.split(" ")[0] if self.header_row_text else None
240
+ if self.control_id not in self.controls_implementations:
241
+ self.controls_implementations[self.control_id] = {}
242
+
243
+ cells = row.cells
244
+ cell_count = len(cells)
245
+ self.handle_row_parts(cells, cell_count)
246
+ for cell_index, cell in enumerate(row.cells):
247
+ self._handle_cell(cell)
248
+
249
+ def handle_row_parts(self, cells: Any, cell_count: int) -> None:
250
+ """
251
+ Handle the parts of the control implementation.
252
+ :param Any cells: The cells in the DOCX row.
253
+ :param int cell_count: The number of cells in the row.
254
+ :return: None
255
+ :rtype: None
256
+ """
257
+ check = "what is the solution and how is it implemented".lower()
258
+ if check not in self.header_row_text.lower():
259
+ return
260
+ control_dict = self.controls_implementations.get(self.control_id, {})
261
+ self.handle_part(cells, cell_count, control_dict, check)
262
+
263
+ def handle_part(self, cells: Any, cell_count: int, control_dict: Dict, check: str):
264
+ """
265
+ Handle the parts of the control implementation.
266
+ :param Any cells: The cells in the DOCX row.
267
+ :param int cell_count: The number of cells in the row.
268
+ :param Dict control_dict: The dictionary containing the control implementation data.
269
+ :param str check: The check string to exclude from the part value.
270
+ """
271
+ if cell_count > 1:
272
+ name = self.get_cell_text(cells[0]) if cells[0].text else DEFAULT_PART
273
+ value = self.get_cell_text(cells[1])
274
+ part_list = control_dict.get("parts", [])
275
+ val_dict = {"name": name, "value": value}
276
+ if check not in value.lower() and val_dict not in part_list:
277
+ part_list.append(val_dict)
278
+ control_dict["parts"] = part_list
279
+ else:
280
+ value = self.get_cell_text(cells[0])
281
+ value_lower = value.lower()
282
+ pattern = re.compile(r"\b(" + "|".join(re.escape(part) for part in self.parts_set) + r")\b", re.IGNORECASE)
283
+ match = pattern.search(value_lower)
284
+ name = match.group(1) if match else DEFAULT_PART
285
+ part_list = control_dict.get("parts", [])
286
+ val_dict = {"name": name, "value": value}
287
+ if check.lower() not in value_lower and val_dict not in part_list:
288
+ part_list.append(val_dict)
289
+ control_dict["parts"] = part_list
290
+
291
+ def set_cell_text(self, cell: Any):
292
+ """
293
+ Set the text content of the cell and process it.
294
+ :param Any cell: The cell element from the DOCX table.
295
+ """
296
+ processed_texts = ""
297
+ self.xml = etree.fromstring(cell._element.xml)
298
+ self.text_elements = self.xml.findall(TEXT_ELEMENT)
299
+ self.checkbox_states = self.xml.findall(CHECKBOX_ELEMENT)
300
+ for element in self.text_elements:
301
+ if element.text:
302
+ processed_texts += self._process_text_element(element.text)
303
+ self.joined_processed_texts = re.sub(r"\.(?!\s|\d|$)", ". ", processed_texts)
304
+
305
+ def get_cell_text(self, cell: Any) -> str:
306
+ """
307
+ Get the text content of the cell.
308
+ :param Any cell: The cell element from the DOCX table.
309
+ :return: The text content of the cell.
310
+ :rtype: str
311
+ """
312
+ processed_texts = ""
313
+ xml = etree.fromstring(cell._element.xml)
314
+ text_elements = xml.findall(TEXT_ELEMENT)
315
+ for element in text_elements:
316
+ if element.text:
317
+ processed_texts += self._process_text_element(element.text)
318
+ return re.sub(r"\.(?!\s|\d|$)", ". ", processed_texts)
319
+
320
+ def _handle_cell(self, cell: Any):
321
+ """
322
+ Handle a cell in the DOCX table.
323
+ :param Any cell: The cell element from the DOCX table.
324
+ """
325
+ self.set_cell_text(cell)
326
+ self.cell_data = {}
327
+ self._handle_params()
328
+ self.cell_data_status = None
329
+ self._handle_checkbox_states()
330
+ self._handle_implementation_status()
331
+ self._handle_implementation_origination()
332
+ self._handle_implementation_statement()
333
+ # self._handle_implementation_parts(cell_index, cells)
334
+ self._handle_responsibility()
335
+
336
+ def _handle_params(self):
337
+ """
338
+ Handle the parameters of the control implementation.
339
+ """
340
+ if (
341
+ CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower()
342
+ and "parameter" in self.joined_processed_texts.lower()
343
+ and self.control_id in self.controls_implementations
344
+ ):
345
+ control_dict = self.controls_implementations[self.control_id]
346
+ if "parameters" not in control_dict:
347
+ control_dict["parameters"] = []
348
+ # split the first occurrence of : to get the parameter name and value
349
+ parts = self.joined_processed_texts.split(":", 1)
350
+ param_text = self.joined_processed_texts
351
+ param = {"name": "Default Name", "value": "Default Value"}
352
+ if len(parts) == 2:
353
+ param["name"] = parts[0].strip().replace("Parameter", "")
354
+ param["value"] = parts[1].strip()
355
+ if param not in control_dict["parameters"]:
356
+ control_dict["parameters"].append(param)
357
+ else:
358
+ param["value"] = param_text.replace("parameters", "").strip()
359
+ if param not in control_dict["parameters"]:
360
+ control_dict["parameters"].append(param)
361
+
362
+ def _handle_implementation_origination(self):
363
+ """
364
+ Handle the origination of the control implementation.
365
+ """
366
+ if (
367
+ self.cell_data_status
368
+ and any(
369
+ [self.score_similarity(self.cell_data_status.lower(), origin) > 90 for origin in LOWER_ORIGINATIONS]
370
+ )
371
+ and CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower()
372
+ and CONTROL_ORIGIN_KEY.lower() in self.joined_processed_texts.lower()
373
+ and self.header_row_text.split(" ")[0] in self.controls_implementations
374
+ ):
375
+ if self.control_id in self.controls_implementations:
376
+ control_dict = self.controls_implementations[self.control_id]
377
+ control_dict["origination"] = self.cell_data_status
378
+ elif origination := self.determine_origination(self.joined_processed_texts):
379
+ if origination in ORIGINATIONS:
380
+ if self.control_id in self.controls_implementations:
381
+ control_dict = self.controls_implementations[self.control_id]
382
+ control_dict["origination"] = origination
383
+
384
+ def _handle_implementation_status(self):
385
+ """
386
+ Handle the implementation status of the control.
387
+ """
388
+ if (
389
+ self.cell_data_status
390
+ and self.cell_data_status.lower() in LOWER_STATUSES
391
+ and CONTROL_SUMMARY_KEY in self.header_row_text
392
+ ):
393
+ # logger.debug(header_row_text)
394
+ if self.control_id in self.controls_implementations:
395
+ control_dict = self.controls_implementations[self.control_id]
396
+ control_dict["status"] = self.cell_data_status
397
+ elif status := self.determine_status(self.joined_processed_texts):
398
+ if status.lower() in LOWER_STATUSES and CONTROL_SUMMARY_KEY in self.header_row_text:
399
+ if self.control_id in self.controls_implementations:
400
+ control_dict = self.controls_implementations[self.control_id]
401
+ control_dict["status"] = status
402
+
403
+ def _handle_implementation_statement(self):
404
+ """
405
+ Handle the implementation statement of the control.
406
+ """
407
+
408
+ value_check = f"{self.control_id} What is the solution and how is it implemented?"
409
+ if (
410
+ STATEMENT_CHECK in self.header_row_text.lower()
411
+ and value_check.lower() != self.joined_processed_texts.lower()
412
+ and self.control_id in self.controls_implementations
413
+ ):
414
+ control_dict = self.controls_implementations.get(self.control_id, {})
415
+ imp_list = control_dict.get("statement", [])
416
+ if (
417
+ self.joined_processed_texts.strip() != ""
418
+ and STATEMENT_CHECK not in self.joined_processed_texts.strip().lower()
419
+ ):
420
+ imp_list.append(self.joined_processed_texts.strip())
421
+ control_dict["statement"] = imp_list
422
+
423
+ @staticmethod
424
+ def generate_parts_full_alphabet() -> List[str]:
425
+ """
426
+ Generates a list of strings in the format "part {letter}"
427
+ for each letter of the alphabet from 'a' to 'z'.
428
+
429
+ :return: A list of strings in the format "part {letter}"
430
+ :rtype: List[str]
431
+ """
432
+ # Use chr to convert ASCII codes to letters: 97 is 'a', 122 is 'z'
433
+ parts = [f"part {chr(letter)}" for letter in range(97, 122 + 1)]
434
+ return parts
435
+
436
+ def _handle_implementation_parts(self, cell_index: int, cells: Any):
437
+ """
438
+ Handle the implementation statement of the control.
439
+ """
440
+ value_check = f"{self.control_id} What is the solution and how is it implemented?"
441
+ generic_value_check = "What is the solution and how is it implemented".lower()
442
+ if (
443
+ generic_value_check in self.header_row_text.lower()
444
+ and value_check.lower() != self.joined_processed_texts.lower()
445
+ and self.control_id in self.controls_implementations
446
+ ):
447
+ part_value = self.joined_processed_texts.strip()
448
+ control_dict = self.controls_implementations.get(self.control_id, {})
449
+ part_list = control_dict.get("parts", [])
450
+ if any(
451
+ [
452
+ part_value.strip().lower() == p.lower() or part_value.strip().lower() == f"{p.lower()}:"
453
+ for p in self.parts
454
+ ]
455
+ ):
456
+ part_name = part_value.strip() or DEFAULT_PART
457
+ next_cell_text = self.get_cell_text(cells[cell_index + 1])
458
+ if ":" not in part_value:
459
+ part_value = ": ".join(
460
+ [
461
+ part_value.strip(),
462
+ next_cell_text.strip(),
463
+ ]
464
+ )
465
+ else:
466
+ part_value = " ".join([part_value.strip(), next_cell_text.strip()])
467
+ self.build_part_dict(
468
+ part_name=part_name,
469
+ part_value=part_value,
470
+ control_dict=control_dict,
471
+ part_list=part_list,
472
+ generic_value_check=generic_value_check,
473
+ )
474
+
475
+ def build_part_dict(
476
+ self, part_name: str, part_value: str, control_dict: Dict, part_list: List, generic_value_check: str
477
+ ):
478
+ """
479
+ Build a dictionary for a part of the control implementation.
480
+ :param str part_name: The name of the part.
481
+ :param str part_value: The value of the part.
482
+ :param Dict control_dict: The dictionary containing the control implementation data.
483
+ :param List part_list: The list of parts in the control implementation.
484
+ :param str generic_value_check: The generic value check string.
485
+ """
486
+ if part_value.lower().startswith("part"):
487
+ parts = part_value.split(":", 1)
488
+ part_dict = {"name": part_name, "value": DEFAULT_PART}
489
+ if len(parts) == 2 and parts[1].strip() != "":
490
+ part_dict["name"] = parts[0].strip()
491
+ part_dict["value"] = parts[1].strip()
492
+ logger.debug(f"Part: {part_dict}")
493
+ self.add_to_list(new_dict=part_dict, the_list=part_list)
494
+ elif part_value.strip() != "" and generic_value_check not in part_value.lower():
495
+ part_dict["value"] = part_value.strip()
496
+ self.add_to_list(new_dict=part_dict, the_list=part_list)
497
+ elif generic_value_check not in part_value.lower():
498
+ pdict = {
499
+ "name": DEFAULT_PART,
500
+ "value": part_value.strip(),
501
+ }
502
+ self.add_to_list(new_dict=pdict, the_list=part_list)
503
+ control_dict["parts"] = part_list
504
+
505
+ @staticmethod
506
+ def add_to_list(new_dict: Dict, the_list: List):
507
+ """
508
+ Add a value to a list in the control dictionary.
509
+ :param Dict new_dict: The new dictionary to add to the list.
510
+ :param List the_list: The list to add the dictionary to.
511
+ """
512
+ if new_dict not in the_list:
513
+ the_list.append(new_dict)
514
+
515
+ def _handle_responsibility(self):
516
+ """
517
+ Handle the responsible roles of the control.
518
+ """
519
+ if (
520
+ CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower()
521
+ and self.control_id in self.controls_implementations
522
+ and self.joined_processed_texts.lower().startswith("responsible role:")
523
+ ):
524
+ control_dict = self.controls_implementations.get(self.control_id, {})
525
+ parts = self.joined_processed_texts.split(":")
526
+ if len(parts) == 2:
527
+ control_dict["responsibility"] = parts[1].strip()
528
+
529
+ def _handle_checkbox_states(self):
530
+ """
531
+ Handle the checkbox states in the DOCX table.
532
+ """
533
+ updated_checkbox_states = [self._get_checkbox_state(state) for state in self.checkbox_states]
534
+ for item in self.processed_texts[1:]:
535
+ if isinstance(item, dict):
536
+ self.cell_data.update(item)
537
+ else:
538
+ self.cell_data[item.strip()] = updated_checkbox_states.pop(0) if updated_checkbox_states else None
539
+ self._get_cell_data_status()
540
+
541
+ def _get_cell_data_status(self):
542
+ """
543
+ Get the status of the cell data.
544
+ """
545
+ if self.cell_data != {}:
546
+ for k, v in self.cell_data.items():
547
+ if v:
548
+ self.cell_data_status = k