regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.2.1__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 (314) hide show
  1. regscale/_version.py +1 -1
  2. regscale/airflow/hierarchy.py +2 -2
  3. regscale/core/app/api.py +5 -2
  4. regscale/core/app/application.py +36 -6
  5. regscale/core/app/internal/control_editor.py +73 -21
  6. regscale/core/app/internal/evidence.py +727 -204
  7. regscale/core/app/internal/login.py +4 -2
  8. regscale/core/app/internal/model_editor.py +219 -64
  9. regscale/core/app/utils/app_utils.py +86 -12
  10. regscale/core/app/utils/catalog_utils/common.py +1 -1
  11. regscale/core/login.py +21 -4
  12. regscale/core/utils/async_graphql_client.py +363 -0
  13. regscale/core/utils/date.py +77 -1
  14. regscale/dev/cli.py +26 -0
  15. regscale/dev/code_gen.py +109 -24
  16. regscale/dev/version.py +72 -0
  17. regscale/integrations/commercial/__init__.py +30 -2
  18. regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
  19. regscale/integrations/commercial/aws/cli.py +3107 -54
  20. regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
  21. regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
  22. regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
  23. regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
  24. regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
  25. regscale/integrations/commercial/aws/config_compliance.py +914 -0
  26. regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
  27. regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
  28. regscale/integrations/commercial/aws/evidence_generator.py +283 -0
  29. regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
  30. regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
  31. regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
  32. regscale/integrations/commercial/aws/iam_evidence.py +574 -0
  33. regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
  34. regscale/integrations/commercial/aws/inventory/base.py +107 -5
  35. regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
  36. regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
  37. regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
  38. regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
  39. regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
  40. regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
  41. regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
  42. regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
  43. regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
  44. regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
  45. regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
  46. regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
  47. regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
  48. regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
  49. regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
  50. regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
  51. regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
  52. regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
  53. regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
  54. regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
  55. regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
  56. regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
  57. regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
  58. regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
  59. regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
  60. regscale/integrations/commercial/aws/kms_evidence.py +879 -0
  61. regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
  62. regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
  63. regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
  64. regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
  65. regscale/integrations/commercial/aws/org_evidence.py +666 -0
  66. regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
  67. regscale/integrations/commercial/aws/s3_evidence.py +632 -0
  68. regscale/integrations/commercial/aws/scanner.py +1072 -205
  69. regscale/integrations/commercial/aws/security_hub.py +319 -0
  70. regscale/integrations/commercial/aws/session_manager.py +282 -0
  71. regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
  72. regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
  73. regscale/integrations/commercial/jira.py +489 -153
  74. regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
  75. regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
  76. regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
  77. regscale/integrations/commercial/qualys/__init__.py +167 -68
  78. regscale/integrations/commercial/qualys/scanner.py +305 -39
  79. regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
  80. regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
  81. regscale/integrations/commercial/sicura/api.py +79 -42
  82. regscale/integrations/commercial/sicura/commands.py +8 -2
  83. regscale/integrations/commercial/sicura/scanner.py +83 -44
  84. regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
  85. regscale/integrations/commercial/synqly/assets.py +133 -16
  86. regscale/integrations/commercial/synqly/edr.py +2 -8
  87. regscale/integrations/commercial/synqly/query_builder.py +536 -0
  88. regscale/integrations/commercial/synqly/ticketing.py +27 -0
  89. regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
  90. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  91. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  92. regscale/integrations/commercial/tenablev2/commands.py +146 -5
  93. regscale/integrations/commercial/tenablev2/scanner.py +1 -3
  94. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  95. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  96. regscale/integrations/commercial/wizv2/click.py +191 -76
  97. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  98. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  99. regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
  100. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  101. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
  102. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
  103. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  104. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  105. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
  106. regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
  107. regscale/integrations/commercial/wizv2/issue.py +776 -28
  108. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  109. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  110. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  111. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  112. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  113. regscale/integrations/commercial/wizv2/reports.py +243 -0
  114. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  115. regscale/integrations/commercial/wizv2/scanner.py +1031 -441
  116. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  117. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  118. regscale/integrations/commercial/wizv2/variables.py +89 -3
  119. regscale/integrations/compliance_integration.py +1036 -151
  120. regscale/integrations/control_matcher.py +432 -0
  121. regscale/integrations/due_date_handler.py +333 -0
  122. regscale/integrations/milestone_manager.py +291 -0
  123. regscale/integrations/public/__init__.py +14 -0
  124. regscale/integrations/public/cci_importer.py +834 -0
  125. regscale/integrations/public/csam/__init__.py +0 -0
  126. regscale/integrations/public/csam/csam.py +938 -0
  127. regscale/integrations/public/csam/csam_agency_defined.py +179 -0
  128. regscale/integrations/public/csam/csam_common.py +154 -0
  129. regscale/integrations/public/csam/csam_controls.py +432 -0
  130. regscale/integrations/public/csam/csam_poam.py +124 -0
  131. regscale/integrations/public/fedramp/click.py +77 -6
  132. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  133. regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
  134. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  135. regscale/integrations/public/fedramp/poam/scanner.py +75 -7
  136. regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
  137. regscale/integrations/scanner_integration.py +1961 -430
  138. regscale/models/integration_models/CCI_List.xml +1 -0
  139. regscale/models/integration_models/aqua.py +2 -2
  140. regscale/models/integration_models/cisa_kev_data.json +805 -11
  141. regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
  142. regscale/models/integration_models/nexpose.py +36 -10
  143. regscale/models/integration_models/qualys.py +3 -4
  144. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  145. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
  146. regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
  147. regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
  148. regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
  149. regscale/models/locking.py +12 -8
  150. regscale/models/platform.py +4 -2
  151. regscale/models/regscale_models/__init__.py +7 -0
  152. regscale/models/regscale_models/assessment.py +2 -1
  153. regscale/models/regscale_models/catalog.py +1 -1
  154. regscale/models/regscale_models/compliance_settings.py +251 -1
  155. regscale/models/regscale_models/component.py +1 -0
  156. regscale/models/regscale_models/control_implementation.py +236 -41
  157. regscale/models/regscale_models/control_objective.py +74 -5
  158. regscale/models/regscale_models/file.py +2 -0
  159. regscale/models/regscale_models/form_field_value.py +5 -3
  160. regscale/models/regscale_models/inheritance.py +44 -0
  161. regscale/models/regscale_models/issue.py +301 -102
  162. regscale/models/regscale_models/milestone.py +33 -14
  163. regscale/models/regscale_models/organization.py +3 -0
  164. regscale/models/regscale_models/regscale_model.py +310 -73
  165. regscale/models/regscale_models/security_plan.py +4 -2
  166. regscale/models/regscale_models/vulnerability.py +3 -3
  167. regscale/regscale.py +25 -4
  168. regscale/templates/__init__.py +0 -0
  169. regscale/utils/threading/threadhandler.py +20 -15
  170. regscale/validation/record.py +23 -1
  171. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
  172. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
  173. tests/core/__init__.py +0 -0
  174. tests/core/utils/__init__.py +0 -0
  175. tests/core/utils/test_async_graphql_client.py +472 -0
  176. tests/fixtures/test_fixture.py +13 -8
  177. tests/regscale/core/test_login.py +171 -4
  178. tests/regscale/integrations/commercial/__init__.py +0 -0
  179. tests/regscale/integrations/commercial/aws/__init__.py +0 -0
  180. tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
  181. tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
  182. tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
  183. tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
  184. tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
  185. tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
  186. tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
  187. tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
  188. tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
  189. tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
  190. tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
  191. tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
  192. tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
  193. tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
  194. tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
  195. tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
  196. tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
  197. tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
  198. tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
  199. tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
  200. tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
  201. tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
  202. tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
  203. tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
  204. tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
  205. tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
  206. tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
  207. tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
  208. tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
  209. tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
  210. tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
  211. tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
  212. tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
  213. tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
  214. tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
  215. tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
  216. tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
  217. tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
  218. tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
  219. tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
  220. tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
  221. tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
  222. tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
  223. tests/regscale/integrations/commercial/conftest.py +28 -0
  224. tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
  225. tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
  226. tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
  227. tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
  228. tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
  229. tests/regscale/integrations/commercial/test_aws.py +3742 -0
  230. tests/regscale/integrations/commercial/test_burp.py +48 -0
  231. tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
  232. tests/regscale/integrations/commercial/test_dependabot.py +341 -0
  233. tests/regscale/integrations/commercial/test_gcp.py +1543 -0
  234. tests/regscale/integrations/commercial/test_gitlab.py +549 -0
  235. tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
  236. tests/regscale/integrations/commercial/test_jira.py +2204 -0
  237. tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
  238. tests/regscale/integrations/commercial/test_okta.py +1228 -0
  239. tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
  240. tests/regscale/integrations/commercial/test_sicura.py +349 -0
  241. tests/regscale/integrations/commercial/test_snow.py +423 -0
  242. tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
  243. tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
  244. tests/regscale/integrations/commercial/test_stig.py +33 -0
  245. tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
  246. tests/regscale/integrations/commercial/test_stigv2.py +406 -0
  247. tests/regscale/integrations/commercial/test_wiz.py +1365 -0
  248. tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
  249. tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
  250. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  251. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  252. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  253. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  254. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  255. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  256. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  257. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  258. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  259. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  260. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  261. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  262. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  263. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  264. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  265. tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
  266. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  267. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  268. tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
  269. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  270. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  271. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  272. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  273. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
  274. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
  275. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
  276. tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
  277. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  278. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  279. tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
  280. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
  281. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
  282. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  283. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  284. tests/regscale/integrations/public/__init__.py +0 -0
  285. tests/regscale/integrations/public/fedramp/__init__.py +1 -0
  286. tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
  287. tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
  288. tests/regscale/integrations/public/test_alienvault.py +220 -0
  289. tests/regscale/integrations/public/test_cci.py +1053 -0
  290. tests/regscale/integrations/public/test_cisa.py +1021 -0
  291. tests/regscale/integrations/public/test_emass.py +518 -0
  292. tests/regscale/integrations/public/test_fedramp.py +1152 -0
  293. tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
  294. tests/regscale/integrations/public/test_file_uploads.py +506 -0
  295. tests/regscale/integrations/public/test_oscal.py +453 -0
  296. tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
  297. tests/regscale/integrations/test_control_matcher.py +1421 -0
  298. tests/regscale/integrations/test_control_matching.py +155 -0
  299. tests/regscale/integrations/test_milestone_manager.py +408 -0
  300. tests/regscale/models/test_control_implementation.py +118 -3
  301. tests/regscale/models/test_form_field_value_integration.py +304 -0
  302. tests/regscale/models/test_issue.py +378 -1
  303. tests/regscale/models/test_module_integration.py +582 -0
  304. tests/regscale/models/test_tenable_integrations.py +811 -105
  305. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
  306. regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
  307. regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
  308. regscale/integrations/public/fedramp/parts_mapper.py +0 -107
  309. /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
  310. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  311. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
  312. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
  313. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
  314. {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,506 @@
1
+ import os
2
+ import tempfile
3
+ import unittest
4
+ from unittest.mock import patch, MagicMock, mock_open
5
+ import pytest
6
+ import xml.etree.ElementTree as ET
7
+ from regscale.core.app.internal.file_uploads import file_upload, upload_file
8
+ from regscale.models.regscale_models.file import File
9
+ from regscale.core.app.api import Api
10
+
11
+
12
+ class TestFileUploadsAndFileModel(unittest.TestCase):
13
+
14
+ @patch("regscale.core.app.internal.file_uploads.File.upload_file_to_regscale")
15
+ @patch("regscale.core.app.internal.file_uploads.encode_file_to_base64")
16
+ @patch("regscale.core.app.internal.file_uploads.decode_base64_to_bytesio")
17
+ def test_file_upload_success(self, mock_decode_b64, mock_encode_b64, mock_upload):
18
+ mock_encode_b64.return_value = "ZmFrZV9iYXNlNjQ="
19
+ mock_decode_b64.return_value = MagicMock(getvalue=lambda: b"fake_bytes")
20
+ mock_upload.return_value = {"result": "success"}
21
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
22
+ tmp.write(b"test content")
23
+ tmp_path = tmp.name
24
+ try:
25
+ result = file_upload(123, "test_module", tmp_path)
26
+ self.assertEqual(result, {"result": "success"})
27
+ mock_encode_b64.assert_called_once_with(tmp_path)
28
+ mock_upload.assert_called_once()
29
+ finally:
30
+ os.remove(tmp_path)
31
+
32
+ def test_file_upload_missing_file(self):
33
+ result = file_upload(123, "test_module", "nonexistent_file.txt")
34
+ self.assertIsNone(result)
35
+
36
+ @patch("regscale.core.app.internal.file_uploads.File.upload_file_to_regscale")
37
+ @patch("regscale.core.app.internal.file_uploads.decode_base64_to_bytesio")
38
+ def test_upload_file_success(self, mock_decode_b64, mock_upload):
39
+ mock_decode_b64.return_value = MagicMock(getvalue=lambda: b"fake_bytes")
40
+ mock_upload.return_value = {"id": 1, "result": "success"}
41
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
42
+ tmp.write(b"test content")
43
+ tmp_path = tmp.name
44
+ try:
45
+ result = upload_file(
46
+ ssp_id=123,
47
+ parent_module="test_module",
48
+ file_path=tmp_path,
49
+ filestring="ZmFrZV9iYXNlNjQ=",
50
+ filename="test.txt",
51
+ )
52
+ self.assertEqual(result, {"id": 1, "result": "success"})
53
+ mock_decode_b64.assert_called_once_with("ZmFrZV9iYXNlNjQ=")
54
+ mock_upload.assert_called_once()
55
+ finally:
56
+ os.remove(tmp_path)
57
+
58
+ @patch("regscale.core.app.internal.file_uploads.File.upload_file_to_regscale")
59
+ @patch("regscale.core.app.internal.file_uploads.decode_base64_to_bytesio")
60
+ def test_upload_file_exception(self, mock_decode_b64, mock_upload):
61
+ mock_decode_b64.return_value = MagicMock(getvalue=lambda: b"fake_bytes")
62
+ mock_upload.side_effect = Exception("upload failed")
63
+ result = upload_file(
64
+ ssp_id=123,
65
+ parent_module="test_module",
66
+ file_path="fake_path",
67
+ filestring="ZmFrZV9iYXNlNjQ=",
68
+ filename="test.txt",
69
+ )
70
+ self.assertFalse(result)
71
+
72
+ @patch("regscale.core.app.internal.file_uploads.process_base64_tags_in_xml")
73
+ @patch("regscale.core.app.internal.file_uploads.upload_file")
74
+ @patch("regscale.core.app.internal.file_uploads.encode_file_to_base64")
75
+ def test_process_base64_in_xml(self, mock_encode_b64, mock_upload_file, mock_process_tags):
76
+ from regscale.core.app.internal.file_uploads import process_base64_in_xml
77
+
78
+ mock_process_tags.return_value = [
79
+ {"filename": "file1.txt", "base64": "b64str1"},
80
+ {"filename": "file2.txt", "base64": "b64str2"},
81
+ ]
82
+ mock_upload_file.side_effect = ["result1", "result2", "xml_result"]
83
+ mock_encode_b64.return_value = "xml_b64"
84
+ results = process_base64_in_xml(
85
+ regscale_id=123,
86
+ regscale_module="test_module",
87
+ file_path="fake.xml",
88
+ file_name="optional.xml",
89
+ )
90
+ self.assertEqual(results, ["result1", "result2", "xml_result"])
91
+ self.assertEqual(mock_upload_file.call_count, 3)
92
+
93
+ def test_process_base64_tags_in_xml(self):
94
+ from regscale.core.app.internal.file_uploads import process_base64_tags_in_xml
95
+
96
+ xml_content = """<root>
97
+ <base64 filename=\"file1.txt\">YmFzZTY0X2NvbnRlbnQx</base64>
98
+ <base64 filename=\"file2.txt\">YmFzZTY0X2NvbnRlbnQy</base64>
99
+ </root>"""
100
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".xml", mode="w", encoding="utf-8") as tmp:
101
+ tmp.write(xml_content)
102
+ tmp_path = tmp.name
103
+ try:
104
+ results = process_base64_tags_in_xml(tmp_path)
105
+ self.assertEqual(
106
+ results,
107
+ [
108
+ {"filename": "file1.txt", "base64": "YmFzZTY0X2NvbnRlbnQx"},
109
+ {"filename": "file2.txt", "base64": "YmFzZTY0X2NvbnRlbnQy"},
110
+ ],
111
+ )
112
+ finally:
113
+ os.remove(tmp_path)
114
+
115
+ @patch(
116
+ "regscale.models.regscale_models.file.File.upload_file_to_regscale",
117
+ return_value={"id": 42, "result": "success"},
118
+ )
119
+ def test_upload_file_to_regscale_success(self, mock_upload):
120
+ result = File.upload_file_to_regscale(
121
+ file_name="test.txt", parent_id=123, parent_module="test_module", file_data=b"fake_bytes", api=MagicMock()
122
+ )
123
+ self.assertEqual(result, {"id": 42, "result": "success"})
124
+ mock_upload.assert_called_once()
125
+
126
+ @patch("regscale.models.regscale_models.file.Api")
127
+ def test_upload_file_to_regscale_failure(self, mock_api):
128
+ mock_api_instance = mock_api.return_value
129
+ mock_api_instance.upload_file.side_effect = Exception("upload failed")
130
+ result = File.upload_file_to_regscale(
131
+ file_name="fail.txt",
132
+ parent_id=999,
133
+ parent_module="fail_module",
134
+ file_data=b"fail_bytes",
135
+ api=mock_api_instance,
136
+ )
137
+ self.assertFalse(result)
138
+
139
+ @patch("regscale.models.regscale_models.file.Api")
140
+ def test_download_file_from_regscale_to_memory(self, mock_api_class):
141
+ mock_api = mock_api_class.return_value
142
+ mock_api.config = {"domain": "https://test.com"}
143
+ mock_response = MagicMock()
144
+ mock_response.content = b"file content"
145
+ mock_api.get.return_value = mock_response
146
+
147
+ result = File.download_file_from_regscale_to_memory(
148
+ api=mock_api, record_id=123, module="test_module", stored_name="test.txt", file_hash="abc123"
149
+ )
150
+
151
+ self.assertEqual(result, b"file content")
152
+
153
+ @patch("regscale.models.regscale_models.file.Api")
154
+ def test_get_files_for_parent_from_regscale(self, mock_api_class):
155
+ mock_api = mock_api_class.return_value
156
+ mock_api.config = {"domain": "https://test.com"}
157
+ mock_response = MagicMock()
158
+ mock_response.json.return_value = [
159
+ {
160
+ "id": "1",
161
+ "trustedDisplayName": "test1.txt",
162
+ "trustedStorageName": "test1.txt",
163
+ "size": 100,
164
+ "fullPath": "/test1.txt",
165
+ }
166
+ ]
167
+ mock_api.get.return_value = mock_response
168
+
169
+ result = File.get_files_for_parent_from_regscale(parent_id=123, parent_module="test_module", api=mock_api)
170
+
171
+ self.assertEqual(len(result), 1)
172
+ self.assertIsInstance(result[0], File)
173
+
174
+ @patch("regscale.models.regscale_models.file.Api")
175
+ def test_delete_file(self, mock_api_class):
176
+ mock_api = mock_api_class.return_value
177
+ mock_app = MagicMock()
178
+ mock_app.config = {"domain": "https://test.com"}
179
+ mock_response = MagicMock()
180
+ mock_response.ok = True
181
+ mock_api.delete.return_value = mock_response
182
+
183
+ file_obj = File(
184
+ id="123", trustedDisplayName="test.txt", trustedStorageName="test.txt", size=100, fullPath="/test.txt"
185
+ )
186
+
187
+ result = File.delete_file(app=mock_app, file=file_obj)
188
+
189
+ self.assertTrue(result)
190
+
191
+ def test_determine_mime_type(self):
192
+ self.assertEqual(
193
+ File.determine_mime_type(".xlsx"), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
194
+ )
195
+ self.assertEqual(File.determine_mime_type(".nessus"), "text/xml")
196
+ self.assertEqual(File.determine_mime_type(".msg"), "application/vnd.ms-outlook")
197
+ self.assertIsNone(File.determine_mime_type(".unknown"))
198
+
199
+ gz_mime = File.determine_mime_type(".gz")
200
+ self.assertIn(gz_mime, ["application/gzip", "application/x-gzip"])
201
+
202
+ def test_file_model_creation(self):
203
+ file_data = {
204
+ "id": "123",
205
+ "trustedDisplayName": "test.txt",
206
+ "trustedStorageName": "test.txt",
207
+ "size": 1024,
208
+ "fullPath": "/test.txt",
209
+ }
210
+
211
+ file_obj = File(**file_data)
212
+
213
+ self.assertEqual(file_obj.id, "123")
214
+ self.assertEqual(file_obj.trustedDisplayName, "test.txt")
215
+ self.assertEqual(file_obj.size, 1024)
216
+
217
+ @patch("regscale.models.regscale_models.file.Api")
218
+ def test_get_files_for_parent_general_error(self, mock_api_class):
219
+ mock_api = mock_api_class.return_value
220
+ mock_api.config = {"domain": "https://test.com"}
221
+ mock_api.get.side_effect = Exception("General error")
222
+
223
+ with self.assertRaises(SystemExit):
224
+ File.get_files_for_parent_from_regscale(parent_id=123, parent_module="test_module", api=mock_api)
225
+
226
+ @patch("regscale.models.regscale_models.file.File._create_regscale_file")
227
+ @patch("regscale.models.regscale_models.file.Api")
228
+ def test_upload_file_to_regscale_no_file_created(self, mock_api_class, mock_create_file):
229
+ mock_create_file.return_value = None
230
+ mock_api = MagicMock()
231
+
232
+ result = File.upload_file_to_regscale(
233
+ file_name="test.txt", parent_id=123, parent_module="test_module", api=mock_api, file_data=b"test content"
234
+ )
235
+
236
+ self.assertFalse(result)
237
+
238
+ @patch("regscale.models.regscale_models.file.File._create_regscale_file")
239
+ @patch("regscale.models.regscale_models.file.Api")
240
+ def test_upload_file_to_regscale_no_id_in_response(self, mock_api_class, mock_create_file):
241
+ mock_api = mock_api_class.return_value
242
+ mock_api.config = {"domain": "https://test.com", "token": "test_token", "userId": "user123"}
243
+ mock_create_file.return_value = {
244
+ "fullPath": "/test.txt",
245
+ "trustedDisplayName": "test.txt",
246
+ "trustedStorageName": "test.txt",
247
+ "uploadDate": "2024-01-01",
248
+ "fileHash": "hash123",
249
+ "shaHash": "sha123",
250
+ "size": 100,
251
+ }
252
+
253
+ result = File.upload_file_to_regscale(
254
+ file_name="test.txt", parent_id=123, parent_module="test_module", api=mock_api, file_data=b"test content"
255
+ )
256
+
257
+ self.assertFalse(result)
258
+
259
+ @patch("regscale.models.regscale_models.file.File._create_regscale_file")
260
+ @patch("regscale.models.regscale_models.file.Api")
261
+ def test_upload_file_to_regscale_unsupported_mime_type(self, mock_api_class, mock_create_file):
262
+ mock_api = mock_api_class.return_value
263
+ mock_api.config = {"domain": "https://test.com", "token": "test_token", "userId": "user123"}
264
+ mock_create_file.return_value = {
265
+ "id": "file123",
266
+ "fullPath": "/test.txt",
267
+ "trustedDisplayName": "test.txt",
268
+ "trustedStorageName": "test.txt",
269
+ "uploadDate": "2024-01-01",
270
+ "fileHash": "hash123",
271
+ "shaHash": "sha123",
272
+ "size": 100,
273
+ }
274
+
275
+ with patch.object(File, "determine_mime_type", return_value=None):
276
+ result = File.upload_file_to_regscale(
277
+ file_name="test.txt",
278
+ parent_id=123,
279
+ parent_module="test_module",
280
+ api=mock_api,
281
+ file_data=b"test content",
282
+ )
283
+
284
+ self.assertFalse(result)
285
+
286
+ @patch("regscale.models.regscale_models.file.Tag")
287
+ def test_get_existing_tags_dict(self, mock_tag_class):
288
+ mock_tag1 = MagicMock()
289
+ mock_tag1.title = "tag1"
290
+ mock_tag1.id = "1"
291
+ mock_tag2 = MagicMock()
292
+ mock_tag2.title = "tag2"
293
+ mock_tag2.id = "2"
294
+ mock_tag_class.get_list.return_value = [mock_tag1, mock_tag2]
295
+
296
+ result = File.get_existing_tags_dict()
297
+
298
+ self.assertEqual(result, {"tag1": "1", "tag2": "2"})
299
+ mock_tag_class.get_list.assert_called_once()
300
+
301
+ @patch("regscale.models.regscale_models.file.TagMapping")
302
+ def test_process_tag_with_existing_tag(self, mock_tag_mapping_class):
303
+ mock_tag_mapping = MagicMock()
304
+ mock_tag_mapping_class.return_value = mock_tag_mapping
305
+ tags_dict = {"test_tag": "123"}
306
+
307
+ File.process_tag("test_tag", "file123", tags_dict)
308
+
309
+ mock_tag_mapping_class.assert_called_once_with(parentId="file123", parentModule="files", tagId="123")
310
+ mock_tag_mapping.create.assert_called_once()
311
+
312
+ @patch("regscale.models.regscale_models.file.File.get_existing_tags_dict")
313
+ @patch("regscale.models.regscale_models.file.File.process_tag")
314
+ def test_create_tag_mappings(self, mock_process_tag, mock_get_tags_dict):
315
+ mock_get_tags_dict.return_value = {"tag1": "1", "tag2": "2"}
316
+ mock_response = MagicMock()
317
+ mock_response.json.return_value = {"id": "file123", "tags": "tag1,tag2"}
318
+
319
+ File.create_tag_mappings(mock_response)
320
+
321
+ mock_get_tags_dict.assert_called_once()
322
+ self.assertEqual(mock_process_tag.call_count, 2)
323
+
324
+ @patch("regscale.models.regscale_models.file.Application")
325
+ def test_check_compression_file_not_found(self, mock_app_class):
326
+ mock_app = mock_app_class.return_value
327
+ mock_app.logger.debug = MagicMock()
328
+ mock_app.logger.warning = MagicMock()
329
+
330
+ file_data = b"test content"
331
+ result_path, result_size = File._check_compression(file_path="nonexistent.txt", file_data=file_data)
332
+
333
+ self.assertEqual(result_path, "nonexistent.txt")
334
+ import sys
335
+
336
+ expected_size = sys.getsizeof(file_data) / 1024
337
+ self.assertEqual(result_size, expected_size)
338
+
339
+ def test_compress_file(self):
340
+ with tempfile.NamedTemporaryFile(delete=False) as input_file:
341
+ input_file.write(b"test content for compression")
342
+ input_path = input_file.name
343
+
344
+ output_path = input_path + ".gz"
345
+
346
+ try:
347
+ result = File._compress_file(input_path, output_path)
348
+
349
+ self.assertEqual(result, output_path)
350
+ self.assertTrue(os.path.exists(output_path))
351
+ self.assertGreater(os.path.getsize(output_path), 0)
352
+ finally:
353
+ os.remove(input_path)
354
+ if os.path.exists(output_path):
355
+ os.remove(output_path)
356
+
357
+ @patch("regscale.models.regscale_models.file.Api")
358
+ def test_create_regscale_file_success(self, mock_api_class):
359
+ mock_api = mock_api_class.return_value
360
+ mock_api.config = {"domain": "https://test.com", "token": "test_token"}
361
+ mock_response = MagicMock()
362
+ mock_response.ok = True
363
+ mock_response.json.return_value = {
364
+ "id": "file123",
365
+ "fullPath": "/test.txt",
366
+ "trustedDisplayName": "test.txt",
367
+ "trustedStorageName": "test.txt",
368
+ "uploadDate": "2024-01-01",
369
+ "fileHash": "hash123",
370
+ "shaHash": "sha123",
371
+ }
372
+ mock_api.post.return_value = mock_response
373
+
374
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
375
+ tmp.write(b"test content")
376
+ tmp_path = tmp.name
377
+
378
+ try:
379
+ with patch("os.path.getsize", return_value=1024):
380
+ result = File._create_regscale_file(
381
+ file_path=tmp_path, parent_id=123, parent_module="test_module", api=mock_api
382
+ )
383
+
384
+ self.assertIsNotNone(result)
385
+ self.assertEqual(result["id"], "file123")
386
+ self.assertEqual(result["size"], 1024)
387
+ finally:
388
+ os.remove(tmp_path)
389
+
390
+ def test_create_regscale_file_zero_size(self):
391
+ mock_api = MagicMock()
392
+
393
+ with self.assertRaises(ValueError):
394
+ with patch("os.path.getsize", return_value=0):
395
+ File._create_regscale_file(
396
+ file_path="test.txt", parent_id=123, parent_module="test_module", api=mock_api
397
+ )
398
+
399
+ @patch("regscale.models.regscale_models.file.Api")
400
+ def test_create_regscale_file_api_failure(self, mock_api_class):
401
+ mock_api = mock_api_class.return_value
402
+ mock_api.config = {"domain": "https://test.com", "token": "test_token"}
403
+ mock_response = MagicMock()
404
+ mock_response.ok = False
405
+ mock_response.status_code = 400
406
+ mock_response.text = "Bad Request"
407
+ mock_api.post.return_value = mock_response
408
+
409
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
410
+ tmp.write(b"test content")
411
+ tmp_path = tmp.name
412
+
413
+ try:
414
+ with patch("os.path.getsize", return_value=1024):
415
+ result = File._create_regscale_file(
416
+ file_path=tmp_path, parent_id=123, parent_module="test_module", api=mock_api
417
+ )
418
+
419
+ self.assertIsNone(result)
420
+ mock_api.logger.warning.assert_called()
421
+ finally:
422
+ os.remove(tmp_path)
423
+
424
+ @patch("regscale.models.regscale_models.file.Tag")
425
+ def test_process_tag_nonexistent_tag(self, mock_tag_class):
426
+ mock_tag_mapping_class = MagicMock()
427
+ with patch("regscale.models.regscale_models.file.TagMapping", mock_tag_mapping_class):
428
+ tags_dict = {"existing_tag": "123"}
429
+
430
+ File.process_tag("nonexistent_tag", "file123", tags_dict)
431
+
432
+ mock_tag_mapping_class.assert_not_called()
433
+
434
+ @patch("regscale.models.regscale_models.file.File.get_existing_tags_dict")
435
+ @patch("regscale.models.regscale_models.file.File.process_tag")
436
+ def test_create_tag_mappings_no_id(self, mock_process_tag, mock_get_tags_dict):
437
+ mock_get_tags_dict.return_value = {"tag1": "1", "tag2": "2"}
438
+ mock_response = MagicMock()
439
+ mock_response.json.return_value = {"tags": "tag1,tag2"}
440
+
441
+ File.create_tag_mappings(mock_response)
442
+
443
+ mock_process_tag.assert_not_called()
444
+
445
+ def test_determine_mime_type_known_types(self):
446
+ with patch("mimetypes.types_map", {".txt": "text/plain"}):
447
+ result = File.determine_mime_type(".txt")
448
+ self.assertEqual(result, "text/plain")
449
+
450
+ def test_determine_mime_type_unknown_type(self):
451
+ with patch("mimetypes.types_map", {}):
452
+ with patch("logging.getLogger") as mock_logger:
453
+ mock_logger_instance = MagicMock()
454
+ mock_logger.return_value = mock_logger_instance
455
+
456
+ result = File.determine_mime_type(".unknown")
457
+
458
+ self.assertIsNone(result)
459
+ mock_logger_instance.warning.assert_called_once()
460
+
461
+ @patch("regscale.models.regscale_models.file.Api")
462
+ def test_create_regscale_file_no_response(self, mock_api_class):
463
+ mock_api = mock_api_class.return_value
464
+ mock_api.config = {"domain": "https://test.com", "token": "test_token"}
465
+ mock_api.post.return_value = None
466
+
467
+ with tempfile.NamedTemporaryFile(delete=False) as tmp:
468
+ tmp.write(b"test content")
469
+ tmp_path = tmp.name
470
+
471
+ try:
472
+ with patch("os.path.getsize", return_value=1024):
473
+ result = File._create_regscale_file(
474
+ file_path=tmp_path, parent_id=123, parent_module="test_module", api=mock_api
475
+ )
476
+ self.assertIsNone(result)
477
+ mock_api.logger.warning.assert_called_once()
478
+ finally:
479
+ os.remove(tmp_path)
480
+
481
+ @patch("regscale.models.regscale_models.file.Api")
482
+ def test_create_regscale_file_with_file_data(self, mock_api_class):
483
+ mock_api = mock_api_class.return_value
484
+ mock_api.config = {"domain": "https://test.com", "token": "test_token"}
485
+ mock_response = MagicMock()
486
+ mock_response.ok = True
487
+ mock_response.json.return_value = {
488
+ "id": "file123",
489
+ "fullPath": "/test.txt",
490
+ "trustedDisplayName": "test.txt",
491
+ "trustedStorageName": "test.txt",
492
+ "uploadDate": "2024-01-01",
493
+ "fileHash": "hash123",
494
+ "shaHash": "sha123",
495
+ }
496
+ mock_api.post.return_value = mock_response
497
+
498
+ file_data = b"test file content"
499
+ with patch("os.path.getsize", return_value=len(file_data)):
500
+ result = File._create_regscale_file(
501
+ file_path="test.txt", parent_id=123, parent_module="test_module", api=mock_api, file_data=file_data
502
+ )
503
+
504
+ self.assertIsNotNone(result)
505
+ self.assertEqual(result["id"], "file123")
506
+ self.assertEqual(result["size"], len(file_data))