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.
- regscale/__init__.py +1 -0
- regscale/airflow/__init__.py +9 -0
- regscale/airflow/azure/__init__.py +9 -0
- regscale/airflow/azure/cli.py +89 -0
- regscale/airflow/azure/upload_dags.py +116 -0
- regscale/airflow/click_dags.py +127 -0
- regscale/airflow/click_mixins.py +82 -0
- regscale/airflow/config.py +25 -0
- regscale/airflow/factories/__init__.py +0 -0
- regscale/airflow/factories/connections.py +58 -0
- regscale/airflow/factories/workflows.py +78 -0
- regscale/airflow/hierarchy.py +88 -0
- regscale/airflow/operators/__init__.py +0 -0
- regscale/airflow/operators/click.py +36 -0
- regscale/airflow/sensors/__init__.py +0 -0
- regscale/airflow/sensors/sql.py +107 -0
- regscale/airflow/sessions/__init__.py +0 -0
- regscale/airflow/sessions/sql/__init__.py +3 -0
- regscale/airflow/sessions/sql/queries.py +64 -0
- regscale/airflow/sessions/sql/sql_server_queries.py +248 -0
- regscale/airflow/tasks/__init__.py +0 -0
- regscale/airflow/tasks/branches.py +22 -0
- regscale/airflow/tasks/cli.py +116 -0
- regscale/airflow/tasks/click.py +73 -0
- regscale/airflow/tasks/debugging.py +9 -0
- regscale/airflow/tasks/groups.py +116 -0
- regscale/airflow/tasks/init.py +60 -0
- regscale/airflow/tasks/states.py +47 -0
- regscale/airflow/tasks/workflows.py +36 -0
- regscale/ansible/__init__.py +9 -0
- regscale/core/__init__.py +0 -0
- regscale/core/app/__init__.py +3 -0
- regscale/core/app/api.py +571 -0
- regscale/core/app/application.py +665 -0
- regscale/core/app/internal/__init__.py +136 -0
- regscale/core/app/internal/admin_actions.py +230 -0
- regscale/core/app/internal/assessments_editor.py +873 -0
- regscale/core/app/internal/catalog.py +316 -0
- regscale/core/app/internal/comparison.py +459 -0
- regscale/core/app/internal/control_editor.py +571 -0
- regscale/core/app/internal/encrypt.py +79 -0
- regscale/core/app/internal/evidence.py +1240 -0
- regscale/core/app/internal/file_uploads.py +151 -0
- regscale/core/app/internal/healthcheck.py +66 -0
- regscale/core/app/internal/login.py +305 -0
- regscale/core/app/internal/migrations.py +240 -0
- regscale/core/app/internal/model_editor.py +1701 -0
- regscale/core/app/internal/poam_editor.py +632 -0
- regscale/core/app/internal/workflow.py +105 -0
- regscale/core/app/logz.py +74 -0
- regscale/core/app/utils/XMLIR.py +258 -0
- regscale/core/app/utils/__init__.py +0 -0
- regscale/core/app/utils/api_handler.py +358 -0
- regscale/core/app/utils/app_utils.py +1110 -0
- regscale/core/app/utils/catalog_utils/__init__.py +0 -0
- regscale/core/app/utils/catalog_utils/common.py +91 -0
- regscale/core/app/utils/catalog_utils/compare_catalog.py +193 -0
- regscale/core/app/utils/catalog_utils/diagnostic_catalog.py +97 -0
- regscale/core/app/utils/catalog_utils/download_catalog.py +103 -0
- regscale/core/app/utils/catalog_utils/update_catalog.py +718 -0
- regscale/core/app/utils/catalog_utils/update_catalog_v2.py +1378 -0
- regscale/core/app/utils/catalog_utils/update_catalog_v3.py +1272 -0
- regscale/core/app/utils/catalog_utils/update_plans.py +334 -0
- regscale/core/app/utils/file_utils.py +238 -0
- regscale/core/app/utils/parser_utils.py +81 -0
- regscale/core/app/utils/pickle_file_handler.py +57 -0
- regscale/core/app/utils/regscale_utils.py +319 -0
- regscale/core/app/utils/report_utils.py +119 -0
- regscale/core/app/utils/variables.py +226 -0
- regscale/core/decorators.py +31 -0
- regscale/core/lazy_group.py +65 -0
- regscale/core/login.py +63 -0
- regscale/core/server/__init__.py +0 -0
- regscale/core/server/flask_api.py +473 -0
- regscale/core/server/helpers.py +373 -0
- regscale/core/server/rest.py +64 -0
- regscale/core/server/static/css/bootstrap.css +6030 -0
- regscale/core/server/static/css/bootstrap.min.css +6 -0
- regscale/core/server/static/css/main.css +176 -0
- regscale/core/server/static/images/regscale-cli.svg +49 -0
- regscale/core/server/static/images/regscale.svg +38 -0
- regscale/core/server/templates/base.html +74 -0
- regscale/core/server/templates/index.html +43 -0
- regscale/core/server/templates/login.html +28 -0
- regscale/core/server/templates/make_base64.html +22 -0
- regscale/core/server/templates/upload_STIG.html +109 -0
- regscale/core/server/templates/upload_STIG_result.html +26 -0
- regscale/core/server/templates/upload_ssp.html +144 -0
- regscale/core/server/templates/upload_ssp_result.html +128 -0
- regscale/core/static/__init__.py +0 -0
- regscale/core/static/regex.py +14 -0
- regscale/core/utils/__init__.py +117 -0
- regscale/core/utils/click_utils.py +13 -0
- regscale/core/utils/date.py +238 -0
- regscale/core/utils/graphql.py +254 -0
- regscale/core/utils/urls.py +23 -0
- regscale/dev/__init__.py +6 -0
- regscale/dev/analysis.py +454 -0
- regscale/dev/cli.py +235 -0
- regscale/dev/code_gen.py +492 -0
- regscale/dev/dirs.py +69 -0
- regscale/dev/docs.py +384 -0
- regscale/dev/monitoring.py +26 -0
- regscale/dev/profiling.py +216 -0
- regscale/exceptions/__init__.py +4 -0
- regscale/exceptions/license_exception.py +7 -0
- regscale/exceptions/validation_exception.py +9 -0
- regscale/integrations/__init__.py +1 -0
- regscale/integrations/commercial/__init__.py +486 -0
- regscale/integrations/commercial/ad.py +433 -0
- regscale/integrations/commercial/amazon/__init__.py +0 -0
- regscale/integrations/commercial/amazon/common.py +106 -0
- regscale/integrations/commercial/aqua/__init__.py +0 -0
- regscale/integrations/commercial/aqua/aqua.py +91 -0
- regscale/integrations/commercial/aws/__init__.py +6 -0
- regscale/integrations/commercial/aws/cli.py +322 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +110 -0
- regscale/integrations/commercial/aws/inventory/base.py +64 -0
- regscale/integrations/commercial/aws/inventory/resources/__init__.py +19 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +234 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +113 -0
- regscale/integrations/commercial/aws/inventory/resources/database.py +101 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +237 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +253 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +240 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +91 -0
- regscale/integrations/commercial/aws/scanner.py +823 -0
- regscale/integrations/commercial/azure/__init__.py +0 -0
- regscale/integrations/commercial/azure/common.py +32 -0
- regscale/integrations/commercial/azure/intune.py +488 -0
- regscale/integrations/commercial/azure/scanner.py +49 -0
- regscale/integrations/commercial/burp.py +78 -0
- regscale/integrations/commercial/cpe.py +144 -0
- regscale/integrations/commercial/crowdstrike.py +1117 -0
- regscale/integrations/commercial/defender.py +1511 -0
- regscale/integrations/commercial/dependabot.py +210 -0
- regscale/integrations/commercial/durosuite/__init__.py +0 -0
- regscale/integrations/commercial/durosuite/api.py +1546 -0
- regscale/integrations/commercial/durosuite/process_devices.py +101 -0
- regscale/integrations/commercial/durosuite/scanner.py +637 -0
- regscale/integrations/commercial/durosuite/variables.py +21 -0
- regscale/integrations/commercial/ecr.py +90 -0
- regscale/integrations/commercial/gcp/__init__.py +237 -0
- regscale/integrations/commercial/gcp/auth.py +96 -0
- regscale/integrations/commercial/gcp/control_tests.py +238 -0
- regscale/integrations/commercial/gcp/variables.py +18 -0
- regscale/integrations/commercial/gitlab.py +332 -0
- regscale/integrations/commercial/grype.py +165 -0
- regscale/integrations/commercial/ibm.py +90 -0
- regscale/integrations/commercial/import_all/__init__.py +0 -0
- regscale/integrations/commercial/import_all/import_all_cmd.py +467 -0
- regscale/integrations/commercial/import_all/scan_file_fingerprints.json +27 -0
- regscale/integrations/commercial/jira.py +1046 -0
- regscale/integrations/commercial/mappings/__init__.py +0 -0
- regscale/integrations/commercial/mappings/csf_controls.json +713 -0
- regscale/integrations/commercial/mappings/nist_800_53_r5_controls.json +1516 -0
- regscale/integrations/commercial/nessus/__init__.py +0 -0
- regscale/integrations/commercial/nessus/nessus_utils.py +429 -0
- regscale/integrations/commercial/nessus/scanner.py +416 -0
- regscale/integrations/commercial/nexpose.py +90 -0
- regscale/integrations/commercial/okta.py +798 -0
- regscale/integrations/commercial/opentext/__init__.py +0 -0
- regscale/integrations/commercial/opentext/click.py +99 -0
- regscale/integrations/commercial/opentext/scanner.py +143 -0
- regscale/integrations/commercial/prisma.py +91 -0
- regscale/integrations/commercial/qualys.py +1462 -0
- regscale/integrations/commercial/salesforce.py +980 -0
- regscale/integrations/commercial/sap/__init__.py +0 -0
- regscale/integrations/commercial/sap/click.py +31 -0
- regscale/integrations/commercial/sap/sysdig/__init__.py +0 -0
- regscale/integrations/commercial/sap/sysdig/click.py +57 -0
- regscale/integrations/commercial/sap/sysdig/sysdig_scanner.py +190 -0
- regscale/integrations/commercial/sap/tenable/__init__.py +0 -0
- regscale/integrations/commercial/sap/tenable/click.py +49 -0
- regscale/integrations/commercial/sap/tenable/scanner.py +196 -0
- regscale/integrations/commercial/servicenow.py +1756 -0
- regscale/integrations/commercial/sicura/__init__.py +0 -0
- regscale/integrations/commercial/sicura/api.py +855 -0
- regscale/integrations/commercial/sicura/commands.py +73 -0
- regscale/integrations/commercial/sicura/scanner.py +481 -0
- regscale/integrations/commercial/sicura/variables.py +16 -0
- regscale/integrations/commercial/snyk.py +90 -0
- regscale/integrations/commercial/sonarcloud.py +260 -0
- regscale/integrations/commercial/sqlserver.py +369 -0
- regscale/integrations/commercial/stig_mapper_integration/__init__.py +0 -0
- regscale/integrations/commercial/stig_mapper_integration/click_commands.py +38 -0
- regscale/integrations/commercial/stig_mapper_integration/mapping_engine.py +353 -0
- regscale/integrations/commercial/stigv2/__init__.py +0 -0
- regscale/integrations/commercial/stigv2/ckl_parser.py +349 -0
- regscale/integrations/commercial/stigv2/click_commands.py +95 -0
- regscale/integrations/commercial/stigv2/stig_integration.py +202 -0
- regscale/integrations/commercial/synqly/__init__.py +0 -0
- regscale/integrations/commercial/synqly/assets.py +46 -0
- regscale/integrations/commercial/synqly/ticketing.py +132 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +223 -0
- regscale/integrations/commercial/synqly_jira.py +840 -0
- regscale/integrations/commercial/tenablev2/__init__.py +0 -0
- regscale/integrations/commercial/tenablev2/authenticate.py +31 -0
- regscale/integrations/commercial/tenablev2/click.py +1584 -0
- regscale/integrations/commercial/tenablev2/scanner.py +504 -0
- regscale/integrations/commercial/tenablev2/stig_parsers.py +140 -0
- regscale/integrations/commercial/tenablev2/utils.py +78 -0
- regscale/integrations/commercial/tenablev2/variables.py +17 -0
- regscale/integrations/commercial/trivy.py +162 -0
- regscale/integrations/commercial/veracode.py +96 -0
- regscale/integrations/commercial/wizv2/WizDataMixin.py +97 -0
- regscale/integrations/commercial/wizv2/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/click.py +429 -0
- regscale/integrations/commercial/wizv2/constants.py +1001 -0
- regscale/integrations/commercial/wizv2/issue.py +361 -0
- regscale/integrations/commercial/wizv2/models.py +112 -0
- regscale/integrations/commercial/wizv2/parsers.py +339 -0
- regscale/integrations/commercial/wizv2/sbom.py +115 -0
- regscale/integrations/commercial/wizv2/scanner.py +416 -0
- regscale/integrations/commercial/wizv2/utils.py +796 -0
- regscale/integrations/commercial/wizv2/variables.py +39 -0
- regscale/integrations/commercial/wizv2/wiz_auth.py +159 -0
- regscale/integrations/commercial/xray.py +91 -0
- regscale/integrations/integration/__init__.py +2 -0
- regscale/integrations/integration/integration.py +26 -0
- regscale/integrations/integration/inventory.py +17 -0
- regscale/integrations/integration/issue.py +100 -0
- regscale/integrations/integration_override.py +149 -0
- regscale/integrations/public/__init__.py +103 -0
- regscale/integrations/public/cisa.py +641 -0
- regscale/integrations/public/criticality_updater.py +70 -0
- regscale/integrations/public/emass.py +411 -0
- regscale/integrations/public/emass_slcm_import.py +697 -0
- regscale/integrations/public/fedramp/__init__.py +0 -0
- regscale/integrations/public/fedramp/appendix_parser.py +548 -0
- regscale/integrations/public/fedramp/click.py +479 -0
- regscale/integrations/public/fedramp/components.py +714 -0
- regscale/integrations/public/fedramp/docx_parser.py +259 -0
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +1124 -0
- regscale/integrations/public/fedramp/fedramp_common.py +3181 -0
- regscale/integrations/public/fedramp/fedramp_docx.py +388 -0
- regscale/integrations/public/fedramp/fedramp_five.py +2343 -0
- regscale/integrations/public/fedramp/fedramp_traversal.py +138 -0
- regscale/integrations/public/fedramp/import_fedramp_r4_ssp.py +279 -0
- regscale/integrations/public/fedramp/import_workbook.py +495 -0
- regscale/integrations/public/fedramp/inventory_items.py +244 -0
- regscale/integrations/public/fedramp/mappings/__init__.py +0 -0
- regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +7388 -0
- regscale/integrations/public/fedramp/mappings/fedramp_r5_params.json +8636 -0
- regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +9605 -0
- regscale/integrations/public/fedramp/mappings/system_roles.py +34 -0
- regscale/integrations/public/fedramp/mappings/user.py +175 -0
- regscale/integrations/public/fedramp/mappings/values.py +141 -0
- regscale/integrations/public/fedramp/markdown_parser.py +150 -0
- regscale/integrations/public/fedramp/metadata.py +689 -0
- regscale/integrations/public/fedramp/models/__init__.py +59 -0
- regscale/integrations/public/fedramp/models/leveraged_auth_new.py +168 -0
- regscale/integrations/public/fedramp/models/poam_importer.py +522 -0
- regscale/integrations/public/fedramp/parts_mapper.py +107 -0
- regscale/integrations/public/fedramp/poam/__init__.py +0 -0
- regscale/integrations/public/fedramp/poam/scanner.py +851 -0
- regscale/integrations/public/fedramp/properties.py +201 -0
- regscale/integrations/public/fedramp/reporting.py +84 -0
- regscale/integrations/public/fedramp/resources.py +496 -0
- regscale/integrations/public/fedramp/rosetta.py +110 -0
- regscale/integrations/public/fedramp/ssp_logger.py +87 -0
- regscale/integrations/public/fedramp/system_characteristics.py +922 -0
- regscale/integrations/public/fedramp/system_control_implementations.py +582 -0
- regscale/integrations/public/fedramp/system_implementation.py +190 -0
- regscale/integrations/public/fedramp/xml_utils.py +87 -0
- regscale/integrations/public/nist_catalog.py +275 -0
- regscale/integrations/public/oscal.py +1946 -0
- regscale/integrations/public/otx.py +169 -0
- regscale/integrations/scanner_integration.py +2692 -0
- regscale/integrations/variables.py +25 -0
- regscale/models/__init__.py +7 -0
- regscale/models/app_models/__init__.py +5 -0
- regscale/models/app_models/catalog_compare.py +213 -0
- regscale/models/app_models/click.py +252 -0
- regscale/models/app_models/datetime_encoder.py +21 -0
- regscale/models/app_models/import_validater.py +321 -0
- regscale/models/app_models/mapping.py +260 -0
- regscale/models/app_models/pipeline.py +37 -0
- regscale/models/click_models.py +413 -0
- regscale/models/config.py +154 -0
- regscale/models/email_style.css +67 -0
- regscale/models/hierarchy.py +8 -0
- regscale/models/inspect_models.py +79 -0
- regscale/models/integration_models/__init__.py +0 -0
- regscale/models/integration_models/amazon_models/__init__.py +0 -0
- regscale/models/integration_models/amazon_models/inspector.py +262 -0
- regscale/models/integration_models/amazon_models/inspector_scan.py +206 -0
- regscale/models/integration_models/aqua.py +247 -0
- regscale/models/integration_models/azure_alerts.py +255 -0
- regscale/models/integration_models/base64.py +23 -0
- regscale/models/integration_models/burp.py +433 -0
- regscale/models/integration_models/burp_models.py +128 -0
- regscale/models/integration_models/cisa_kev_data.json +19333 -0
- regscale/models/integration_models/defender_data.py +93 -0
- regscale/models/integration_models/defenderimport.py +143 -0
- regscale/models/integration_models/drf.py +443 -0
- regscale/models/integration_models/ecr_models/__init__.py +0 -0
- regscale/models/integration_models/ecr_models/data.py +69 -0
- regscale/models/integration_models/ecr_models/ecr.py +239 -0
- regscale/models/integration_models/flat_file_importer.py +1079 -0
- regscale/models/integration_models/grype_import.py +247 -0
- regscale/models/integration_models/ibm.py +126 -0
- regscale/models/integration_models/implementation_results.py +85 -0
- regscale/models/integration_models/nexpose.py +140 -0
- regscale/models/integration_models/prisma.py +202 -0
- regscale/models/integration_models/qualys.py +720 -0
- regscale/models/integration_models/qualys_scanner.py +160 -0
- regscale/models/integration_models/sbom/__init__.py +0 -0
- regscale/models/integration_models/sbom/cyclone_dx.py +139 -0
- regscale/models/integration_models/send_reminders.py +620 -0
- regscale/models/integration_models/snyk.py +155 -0
- regscale/models/integration_models/synqly_models/__init__.py +0 -0
- regscale/models/integration_models/synqly_models/capabilities.json +1 -0
- regscale/models/integration_models/synqly_models/connector_types.py +22 -0
- regscale/models/integration_models/synqly_models/connectors/__init__.py +7 -0
- regscale/models/integration_models/synqly_models/connectors/assets.py +97 -0
- regscale/models/integration_models/synqly_models/connectors/ticketing.py +583 -0
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +169 -0
- regscale/models/integration_models/synqly_models/ocsf_mapper.py +331 -0
- regscale/models/integration_models/synqly_models/param.py +72 -0
- regscale/models/integration_models/synqly_models/synqly_model.py +733 -0
- regscale/models/integration_models/synqly_models/tenants.py +39 -0
- regscale/models/integration_models/tenable_models/__init__.py +0 -0
- regscale/models/integration_models/tenable_models/integration.py +187 -0
- regscale/models/integration_models/tenable_models/models.py +513 -0
- regscale/models/integration_models/trivy_import.py +231 -0
- regscale/models/integration_models/veracode.py +217 -0
- regscale/models/integration_models/xray.py +135 -0
- regscale/models/locking.py +100 -0
- regscale/models/platform.py +110 -0
- regscale/models/regscale_models/__init__.py +67 -0
- regscale/models/regscale_models/assessment.py +570 -0
- regscale/models/regscale_models/assessment_plan.py +52 -0
- regscale/models/regscale_models/asset.py +567 -0
- regscale/models/regscale_models/asset_mapping.py +190 -0
- regscale/models/regscale_models/case.py +42 -0
- regscale/models/regscale_models/catalog.py +261 -0
- regscale/models/regscale_models/cci.py +46 -0
- regscale/models/regscale_models/change.py +167 -0
- regscale/models/regscale_models/checklist.py +372 -0
- regscale/models/regscale_models/comment.py +49 -0
- regscale/models/regscale_models/compliance_settings.py +112 -0
- regscale/models/regscale_models/component.py +412 -0
- regscale/models/regscale_models/component_mapping.py +65 -0
- regscale/models/regscale_models/control.py +38 -0
- regscale/models/regscale_models/control_implementation.py +1128 -0
- regscale/models/regscale_models/control_objective.py +261 -0
- regscale/models/regscale_models/control_parameter.py +100 -0
- regscale/models/regscale_models/control_test.py +34 -0
- regscale/models/regscale_models/control_test_plan.py +75 -0
- regscale/models/regscale_models/control_test_result.py +52 -0
- regscale/models/regscale_models/custom_field.py +245 -0
- regscale/models/regscale_models/data.py +109 -0
- regscale/models/regscale_models/data_center.py +40 -0
- regscale/models/regscale_models/deviation.py +203 -0
- regscale/models/regscale_models/email.py +97 -0
- regscale/models/regscale_models/evidence.py +47 -0
- regscale/models/regscale_models/evidence_mapping.py +40 -0
- regscale/models/regscale_models/facility.py +59 -0
- regscale/models/regscale_models/file.py +382 -0
- regscale/models/regscale_models/filetag.py +37 -0
- regscale/models/regscale_models/form_field_value.py +94 -0
- regscale/models/regscale_models/group.py +169 -0
- regscale/models/regscale_models/implementation_objective.py +335 -0
- regscale/models/regscale_models/implementation_option.py +275 -0
- regscale/models/regscale_models/implementation_role.py +33 -0
- regscale/models/regscale_models/incident.py +177 -0
- regscale/models/regscale_models/interconnection.py +43 -0
- regscale/models/regscale_models/issue.py +1176 -0
- regscale/models/regscale_models/leveraged_authorization.py +125 -0
- regscale/models/regscale_models/line_of_inquiry.py +52 -0
- regscale/models/regscale_models/link.py +205 -0
- regscale/models/regscale_models/meta_data.py +64 -0
- regscale/models/regscale_models/mixins/__init__.py +0 -0
- regscale/models/regscale_models/mixins/parent_cache.py +124 -0
- regscale/models/regscale_models/module.py +224 -0
- regscale/models/regscale_models/modules.py +191 -0
- regscale/models/regscale_models/objective.py +14 -0
- regscale/models/regscale_models/parameter.py +87 -0
- regscale/models/regscale_models/ports_protocol.py +81 -0
- regscale/models/regscale_models/privacy.py +89 -0
- regscale/models/regscale_models/profile.py +50 -0
- regscale/models/regscale_models/profile_link.py +68 -0
- regscale/models/regscale_models/profile_mapping.py +124 -0
- regscale/models/regscale_models/project.py +63 -0
- regscale/models/regscale_models/property.py +278 -0
- regscale/models/regscale_models/question.py +85 -0
- regscale/models/regscale_models/questionnaire.py +87 -0
- regscale/models/regscale_models/questionnaire_instance.py +177 -0
- regscale/models/regscale_models/rbac.py +132 -0
- regscale/models/regscale_models/reference.py +86 -0
- regscale/models/regscale_models/regscale_model.py +1643 -0
- regscale/models/regscale_models/requirement.py +29 -0
- regscale/models/regscale_models/risk.py +274 -0
- regscale/models/regscale_models/sbom.py +54 -0
- regscale/models/regscale_models/scan_history.py +436 -0
- regscale/models/regscale_models/search.py +53 -0
- regscale/models/regscale_models/security_control.py +132 -0
- regscale/models/regscale_models/security_plan.py +204 -0
- regscale/models/regscale_models/software_inventory.py +159 -0
- regscale/models/regscale_models/stake_holder.py +64 -0
- regscale/models/regscale_models/stig.py +647 -0
- regscale/models/regscale_models/supply_chain.py +152 -0
- regscale/models/regscale_models/system_role.py +188 -0
- regscale/models/regscale_models/system_role_external_assignment.py +40 -0
- regscale/models/regscale_models/tag.py +37 -0
- regscale/models/regscale_models/tag_mapping.py +19 -0
- regscale/models/regscale_models/task.py +133 -0
- regscale/models/regscale_models/threat.py +196 -0
- regscale/models/regscale_models/user.py +175 -0
- regscale/models/regscale_models/user_group.py +55 -0
- regscale/models/regscale_models/vulnerability.py +242 -0
- regscale/models/regscale_models/vulnerability_mapping.py +162 -0
- regscale/models/regscale_models/workflow.py +55 -0
- regscale/models/regscale_models/workflow_action.py +34 -0
- regscale/models/regscale_models/workflow_instance.py +269 -0
- regscale/models/regscale_models/workflow_instance_step.py +114 -0
- regscale/models/regscale_models/workflow_template.py +58 -0
- regscale/models/regscale_models/workflow_template_step.py +45 -0
- regscale/regscale.py +815 -0
- regscale/utils/__init__.py +7 -0
- regscale/utils/b64conversion.py +14 -0
- regscale/utils/click_utils.py +118 -0
- regscale/utils/decorators.py +48 -0
- regscale/utils/dict_utils.py +59 -0
- regscale/utils/files.py +79 -0
- regscale/utils/fxns.py +30 -0
- regscale/utils/graphql_client.py +113 -0
- regscale/utils/lists.py +16 -0
- regscale/utils/numbers.py +12 -0
- regscale/utils/shell.py +148 -0
- regscale/utils/string.py +121 -0
- regscale/utils/synqly_utils.py +165 -0
- regscale/utils/threading/__init__.py +8 -0
- regscale/utils/threading/threadhandler.py +131 -0
- regscale/utils/threading/threadsafe_counter.py +47 -0
- regscale/utils/threading/threadsafe_dict.py +242 -0
- regscale/utils/threading/threadsafe_list.py +83 -0
- regscale/utils/version.py +104 -0
- regscale/validation/__init__.py +0 -0
- regscale/validation/address.py +37 -0
- regscale/validation/record.py +48 -0
- regscale/visualization/__init__.py +5 -0
- regscale/visualization/click.py +34 -0
- regscale_cli-6.16.0.0.dist-info/LICENSE +21 -0
- regscale_cli-6.16.0.0.dist-info/METADATA +659 -0
- regscale_cli-6.16.0.0.dist-info/RECORD +481 -0
- regscale_cli-6.16.0.0.dist-info/WHEEL +5 -0
- regscale_cli-6.16.0.0.dist-info/entry_points.txt +6 -0
- regscale_cli-6.16.0.0.dist-info/top_level.txt +2 -0
- tests/fixtures/__init__.py +2 -0
- tests/fixtures/api.py +87 -0
- tests/fixtures/models.py +91 -0
- tests/fixtures/test_fixture.py +144 -0
- tests/mocks/__init__.py +0 -0
- tests/mocks/objects.py +3 -0
- tests/mocks/response.py +32 -0
- tests/mocks/xml.py +13 -0
- tests/regscale/__init__.py +0 -0
- tests/regscale/core/__init__.py +0 -0
- tests/regscale/core/test_api.py +232 -0
- tests/regscale/core/test_app.py +406 -0
- tests/regscale/core/test_login.py +37 -0
- tests/regscale/core/test_logz.py +66 -0
- tests/regscale/core/test_sbom_generator.py +87 -0
- tests/regscale/core/test_validation_utils.py +163 -0
- tests/regscale/core/test_version.py +78 -0
- tests/regscale/models/__init__.py +0 -0
- tests/regscale/models/test_asset.py +71 -0
- tests/regscale/models/test_config.py +26 -0
- tests/regscale/models/test_control_implementation.py +27 -0
- tests/regscale/models/test_import.py +97 -0
- tests/regscale/models/test_issue.py +36 -0
- tests/regscale/models/test_mapping.py +52 -0
- tests/regscale/models/test_platform.py +31 -0
- tests/regscale/models/test_regscale_model.py +346 -0
- tests/regscale/models/test_report.py +32 -0
- tests/regscale/models/test_tenable_integrations.py +118 -0
- tests/regscale/models/test_user_model.py +121 -0
- tests/regscale/test_about.py +19 -0
- tests/regscale/test_authorization.py +65 -0
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
import codecs
|
|
2
|
+
import csv
|
|
3
|
+
import datetime
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
from contextlib import closing
|
|
9
|
+
from typing import Dict, List, Any, Optional
|
|
10
|
+
from zipfile import ZipFile
|
|
11
|
+
|
|
12
|
+
import cachetools
|
|
13
|
+
import requests
|
|
14
|
+
from pydantic import ValidationError
|
|
15
|
+
|
|
16
|
+
from regscale.core.app.api import Api
|
|
17
|
+
from regscale.core.app.utils.app_utils import (
|
|
18
|
+
error_and_exit,
|
|
19
|
+
check_file_path,
|
|
20
|
+
get_current_datetime,
|
|
21
|
+
format_dict_to_html,
|
|
22
|
+
create_progress_object,
|
|
23
|
+
)
|
|
24
|
+
from regscale.core.utils.date import datetime_obj
|
|
25
|
+
from regscale.integrations.commercial.wizv2.constants import (
|
|
26
|
+
DOWNLOAD_QUERY,
|
|
27
|
+
BEARER,
|
|
28
|
+
REPORTS_QUERY,
|
|
29
|
+
CONTENT_TYPE,
|
|
30
|
+
RATE_LIMIT_MSG,
|
|
31
|
+
CREATE_REPORT_QUERY,
|
|
32
|
+
MAX_RETRIES,
|
|
33
|
+
CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
|
|
34
|
+
)
|
|
35
|
+
from regscale.integrations.commercial.wizv2.variables import WizVariables
|
|
36
|
+
from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
|
|
37
|
+
from regscale.models import File, Sbom, SecurityPlan, Catalog, ControlImplementation, Assessment, regscale_models
|
|
38
|
+
from regscale.integrations.commercial.wizv2.models import ComplianceReport, ComplianceCheckStatus
|
|
39
|
+
from regscale.utils import PaginatedGraphQLClient
|
|
40
|
+
from regscale.utils.decorators import deprecated
|
|
41
|
+
|
|
42
|
+
logger = logging.getLogger("regscale")
|
|
43
|
+
compliance_job_progress = create_progress_object()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_notes_from_wiz_props(wiz_entity_properties: Dict, external_id: str) -> str:
|
|
47
|
+
"""
|
|
48
|
+
Get notes from wiz properties
|
|
49
|
+
:param Dict wiz_entity_properties: Wiz entity properties
|
|
50
|
+
:param str external_id: External ID
|
|
51
|
+
:return: Notes
|
|
52
|
+
:rtype: str
|
|
53
|
+
"""
|
|
54
|
+
notes = []
|
|
55
|
+
notes.append(f"External ID: {external_id}") if external_id else None
|
|
56
|
+
(
|
|
57
|
+
notes.append(f"Cloud Platform: {wiz_entity_properties.get('cloudPlatform')}")
|
|
58
|
+
if wiz_entity_properties.get("cloudPlatform")
|
|
59
|
+
else None
|
|
60
|
+
)
|
|
61
|
+
(
|
|
62
|
+
notes.append(f"Provider Unique ID: {wiz_entity_properties.get('providerUniqueId')}")
|
|
63
|
+
if wiz_entity_properties.get("providerUniqueId")
|
|
64
|
+
else None
|
|
65
|
+
)
|
|
66
|
+
(
|
|
67
|
+
notes.append(
|
|
68
|
+
f"""cloudProviderURL:<a href="{wiz_entity_properties.get("cloudProviderURL")}"
|
|
69
|
+
target="_blank">{wiz_entity_properties.get("cloudProviderURL")}</a>"""
|
|
70
|
+
)
|
|
71
|
+
if wiz_entity_properties.get("cloudProviderURL")
|
|
72
|
+
else None
|
|
73
|
+
)
|
|
74
|
+
(
|
|
75
|
+
notes.append(f"Vertex ID: {wiz_entity_properties.get('_vertexID')}")
|
|
76
|
+
if wiz_entity_properties.get("_vertexID")
|
|
77
|
+
else None
|
|
78
|
+
)
|
|
79
|
+
(
|
|
80
|
+
notes.append(f"Severity Name: {wiz_entity_properties.get('severity_name')}")
|
|
81
|
+
if wiz_entity_properties.get("severity_name")
|
|
82
|
+
else None
|
|
83
|
+
)
|
|
84
|
+
(
|
|
85
|
+
notes.append(f"Severity Description: {wiz_entity_properties.get('severity_description')}")
|
|
86
|
+
if wiz_entity_properties.get("severity_description")
|
|
87
|
+
else None
|
|
88
|
+
)
|
|
89
|
+
return "<br>".join(notes)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def handle_management_type(wiz_entity_properties: Dict) -> str:
|
|
93
|
+
"""
|
|
94
|
+
Handle management type
|
|
95
|
+
:param Dict wiz_entity_properties: Wiz entity properties
|
|
96
|
+
:return: Management type
|
|
97
|
+
:rtype: str
|
|
98
|
+
"""
|
|
99
|
+
return "External/Third Party Managed" if wiz_entity_properties.get("isManaged") else "Internally Managed"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@cachetools.cached(cachetools.TTLCache(maxsize=1024, ttl=3600))
|
|
103
|
+
def create_asset_type(asset_type: str) -> str:
|
|
104
|
+
"""
|
|
105
|
+
Create asset type if it does not exist and reformat the string to Title Case ie
|
|
106
|
+
( "ASSET_TYPE" or "asset_type" -> "Asset Type")
|
|
107
|
+
:param asset_type str Asset_type
|
|
108
|
+
:return: Asset type
|
|
109
|
+
:rtype: str
|
|
110
|
+
"""
|
|
111
|
+
#
|
|
112
|
+
asset_type = asset_type.title().replace("_", " ")
|
|
113
|
+
meta_data_list = regscale_models.Metadata.get_metadata_by_module_field(module="assets", field="Asset Type")
|
|
114
|
+
if not any(meta_data.value == asset_type for meta_data in meta_data_list):
|
|
115
|
+
regscale_models.Metadata(
|
|
116
|
+
field="Asset Type",
|
|
117
|
+
module="assets",
|
|
118
|
+
value=asset_type,
|
|
119
|
+
).create()
|
|
120
|
+
return asset_type
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def map_category(asset_string: str) -> regscale_models.AssetCategory:
|
|
124
|
+
"""
|
|
125
|
+
category mapper
|
|
126
|
+
|
|
127
|
+
:param str asset_string:
|
|
128
|
+
:return: Category
|
|
129
|
+
:rtype: regscale_models.AssetCategory
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
if asset_string in ["CONTAINER_IMAGE"]:
|
|
133
|
+
return regscale_models.AssetCategory.Software
|
|
134
|
+
return getattr(regscale_models.AssetCategory, asset_string)
|
|
135
|
+
except (KeyError, AttributeError) as ex:
|
|
136
|
+
# why map AssetCategory of everything is software?
|
|
137
|
+
logger.debug("Unable to find %s in AssetType enum \n", ex)
|
|
138
|
+
return regscale_models.AssetCategory.Hardware
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def convert_first_seen_to_days(first_seen: str) -> int:
|
|
142
|
+
"""
|
|
143
|
+
Converts the first seen date to days
|
|
144
|
+
:param str first_seen: First seen date
|
|
145
|
+
:returns: Days
|
|
146
|
+
:rtype: int
|
|
147
|
+
"""
|
|
148
|
+
first_seen_date = datetime_obj(first_seen)
|
|
149
|
+
if not first_seen_date:
|
|
150
|
+
return 0
|
|
151
|
+
first_seen_date_naive = first_seen_date.replace(tzinfo=None)
|
|
152
|
+
return (datetime.datetime.now() - first_seen_date_naive).days
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def fetch_report_by_id(
|
|
156
|
+
report_id: str, parent_id: int, report_file_name: str = "evidence_report", report_file_extension: str = "csv"
|
|
157
|
+
):
|
|
158
|
+
"""
|
|
159
|
+
Fetch report by id and add it to evidence
|
|
160
|
+
|
|
161
|
+
:param str report_id: Wiz report ID
|
|
162
|
+
:param int parent_id: RegScale Parent ID
|
|
163
|
+
:param str report_file_name: Report file name, defaults to "evidence_report"
|
|
164
|
+
:param str report_file_extension: Report file extension, defaults to "csv"
|
|
165
|
+
:rtype: None
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
current_datetime = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
169
|
+
report_file_path = f"artifacts/{report_file_name}_{current_datetime}.{report_file_extension}"
|
|
170
|
+
variables = {"reportId": report_id}
|
|
171
|
+
api_endpoint_url = WizVariables.wizUrl
|
|
172
|
+
token = WizVariables.wizAccessToken
|
|
173
|
+
if not token:
|
|
174
|
+
error_and_exit("Wiz Access Token is missing. Authenticate with Wiz first.")
|
|
175
|
+
client = PaginatedGraphQLClient(
|
|
176
|
+
endpoint=api_endpoint_url,
|
|
177
|
+
query=DOWNLOAD_QUERY,
|
|
178
|
+
headers={
|
|
179
|
+
"Content-Type": "application/json",
|
|
180
|
+
"Authorization": BEARER + token,
|
|
181
|
+
},
|
|
182
|
+
)
|
|
183
|
+
downloaded_report = client.fetch_results(variables=variables)
|
|
184
|
+
logger.debug(f"Download Report result: {downloaded_report}")
|
|
185
|
+
if "errors" in downloaded_report:
|
|
186
|
+
logger.error(f"Error fetching report: {downloaded_report['errors']}")
|
|
187
|
+
logger.error(f"Raw Response Data: {downloaded_report}")
|
|
188
|
+
return
|
|
189
|
+
|
|
190
|
+
if download_url := downloaded_report.get("report", {}).get("lastRun", {}).get("url"):
|
|
191
|
+
logger.info(f"Download URL: {download_url}")
|
|
192
|
+
download_file(url=download_url, local_filename=report_file_path)
|
|
193
|
+
api = Api()
|
|
194
|
+
_ = File.upload_file_to_regscale(
|
|
195
|
+
file_name=str(report_file_path),
|
|
196
|
+
parent_id=parent_id,
|
|
197
|
+
parent_module="evidence",
|
|
198
|
+
api=api,
|
|
199
|
+
)
|
|
200
|
+
logger.info("File uploaded successfully")
|
|
201
|
+
else:
|
|
202
|
+
logger.error("Could not retrieve the download URL.")
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def download_file(url, local_filename="artifacts/test_report.csv"):
|
|
206
|
+
"""
|
|
207
|
+
Download a file from a URL and save it to the local file system.
|
|
208
|
+
|
|
209
|
+
:param url: The URL of the file to download.
|
|
210
|
+
:param local_filename: The local path where the file should be saved.
|
|
211
|
+
:return: None
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
check_file_path("artifacts")
|
|
215
|
+
with requests.get(url, stream=True) as response:
|
|
216
|
+
response.raise_for_status() # Check if the request was successful
|
|
217
|
+
with open(local_filename, "wb") as file:
|
|
218
|
+
for chunk in response.iter_content(chunk_size=8192): # Download in chunks
|
|
219
|
+
file.write(chunk)
|
|
220
|
+
logger.info(f"File downloaded successfully and saved to {local_filename}")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def fetch_sbom_report(
|
|
224
|
+
report_id: str,
|
|
225
|
+
parent_id: str,
|
|
226
|
+
report_file_name: str = "sbom_report",
|
|
227
|
+
report_file_extension: str = "zip",
|
|
228
|
+
standard="CycloneDX",
|
|
229
|
+
):
|
|
230
|
+
"""
|
|
231
|
+
Fetch report by id and add it to evidence
|
|
232
|
+
|
|
233
|
+
:param str report_id: Wiz report ID
|
|
234
|
+
:param str parent_id: RegScale Parent ID
|
|
235
|
+
:param str report_file_name: Report file name, defaults to "evidence_report"
|
|
236
|
+
:param str report_file_extension: Report file extension, defaults to "zip"
|
|
237
|
+
:param str standard: SBOM standard, defaults to "CycloneDX"
|
|
238
|
+
:rtype: None
|
|
239
|
+
"""
|
|
240
|
+
|
|
241
|
+
current_datetime = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
242
|
+
report_file_path = f"artifacts/{report_file_name}_{current_datetime}.{report_file_extension}"
|
|
243
|
+
variables = {"reportId": report_id}
|
|
244
|
+
api_endpoint_url = WizVariables.wizUrl
|
|
245
|
+
token = WizVariables.wizAccessToken
|
|
246
|
+
if not token:
|
|
247
|
+
error_and_exit("Wiz Access Token is missing. Authenticate with Wiz first.")
|
|
248
|
+
client = PaginatedGraphQLClient(
|
|
249
|
+
endpoint=api_endpoint_url,
|
|
250
|
+
query=DOWNLOAD_QUERY,
|
|
251
|
+
headers={
|
|
252
|
+
"Content-Type": "application/json",
|
|
253
|
+
"Authorization": BEARER + token,
|
|
254
|
+
},
|
|
255
|
+
)
|
|
256
|
+
download_report = client.fetch_results(variables=variables)
|
|
257
|
+
logger.debug(f"Download Report result: {download_report}")
|
|
258
|
+
if "errors" in download_report:
|
|
259
|
+
logger.error(f"Error fetching report: {download_report['errors']}")
|
|
260
|
+
logger.error(f"Raw Response Data: {download_report}")
|
|
261
|
+
return
|
|
262
|
+
report_data = None
|
|
263
|
+
if download_url := download_report.get("report", {}).get("lastRun", {}).get("url"):
|
|
264
|
+
logger.info(f"Download URL: {download_url}")
|
|
265
|
+
download_file(url=download_url, local_filename=report_file_path)
|
|
266
|
+
with ZipFile(report_file_path, "r") as zObject:
|
|
267
|
+
for filename in zObject.namelist():
|
|
268
|
+
with zObject.open(filename) as json_f:
|
|
269
|
+
file_name = ".".join(filename.split(".")[:-1])
|
|
270
|
+
report_data = json.load(json_f)
|
|
271
|
+
sbom_standard = report_data.get("bomFormat", standard)
|
|
272
|
+
standard_version = report_data.get("specVersion", 1.5)
|
|
273
|
+
Sbom(
|
|
274
|
+
name=file_name,
|
|
275
|
+
tool="Wiz",
|
|
276
|
+
parentId=int(parent_id),
|
|
277
|
+
parentModule=SecurityPlan.get_module_slug(),
|
|
278
|
+
results=json.dumps(report_data),
|
|
279
|
+
standardVersion=standard_version,
|
|
280
|
+
sbomStandard=sbom_standard,
|
|
281
|
+
).create_or_update(
|
|
282
|
+
bulk_update=True
|
|
283
|
+
) # need put in for this endpoint to update SBOMS
|
|
284
|
+
|
|
285
|
+
logger.info("SBOM attached successfully!")
|
|
286
|
+
else:
|
|
287
|
+
logger.error("Could not retrieve the download URL.")
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
@deprecated("Use the 'fetch_report_by_id' command instead.")
|
|
291
|
+
def fetch_report_id(query: str, variables: Dict, url: str) -> str:
|
|
292
|
+
"""
|
|
293
|
+
Fetch report ID from Wiz
|
|
294
|
+
|
|
295
|
+
:param str query: Query string
|
|
296
|
+
:param Dict variables: Variables
|
|
297
|
+
:param str url: Wiz URL
|
|
298
|
+
:return str: Wiz ID
|
|
299
|
+
:rtype str: str
|
|
300
|
+
"""
|
|
301
|
+
try:
|
|
302
|
+
resp = send_request(
|
|
303
|
+
query=query,
|
|
304
|
+
variables=variables,
|
|
305
|
+
api_endpoint_url=url,
|
|
306
|
+
)
|
|
307
|
+
if "error" in resp.json().keys():
|
|
308
|
+
error_and_exit(f'Wiz Error: {resp.json()["error"]}')
|
|
309
|
+
return resp.json()["data"]["createReport"]["report"]["id"]
|
|
310
|
+
except (requests.RequestException, AttributeError, TypeError) as rex:
|
|
311
|
+
logger.error("Unable to pull report id from requests object\n%s", rex)
|
|
312
|
+
return ""
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def get_framework_names(wiz_frameworks: List) -> List:
|
|
316
|
+
"""
|
|
317
|
+
Get the names of frameworks and replace spaces with underscores.
|
|
318
|
+
|
|
319
|
+
:param List wiz_frameworks: List of Wiz frameworks.
|
|
320
|
+
:return List: List of framework names.
|
|
321
|
+
:rtype List: list
|
|
322
|
+
"""
|
|
323
|
+
return [framework["name"].replace(" ", "_") for framework in wiz_frameworks]
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def check_reports_for_frameworks(reports: List, frames: List) -> bool:
|
|
327
|
+
"""
|
|
328
|
+
Check if any reports contain the given frameworks.
|
|
329
|
+
|
|
330
|
+
:param List reports: List of reports.
|
|
331
|
+
:param List frames: List of framework names.
|
|
332
|
+
:return bool: Boolean indicating if any report contains a framework.
|
|
333
|
+
:rtype bool: bool
|
|
334
|
+
"""
|
|
335
|
+
return any(frame in item["name"] for item in reports for frame in frames)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def create_report_if_needed(
|
|
339
|
+
wiz_project_id: str, frames: List, wiz_frameworks: List, reports: List, snake_framework: str
|
|
340
|
+
) -> List:
|
|
341
|
+
"""
|
|
342
|
+
Create a report if needed and return report IDs.
|
|
343
|
+
|
|
344
|
+
:param str wiz_project_id: Wiz Project ID.
|
|
345
|
+
:param List frames: List of framework names.
|
|
346
|
+
:param List wiz_frameworks: List of Wiz frameworks.
|
|
347
|
+
:param List reports: List of reports.
|
|
348
|
+
:param str snake_framework: Framework name with spaces replaced by underscores.
|
|
349
|
+
:return List: List of Wiz report IDs.
|
|
350
|
+
:rtype List: list
|
|
351
|
+
"""
|
|
352
|
+
if not check_reports_for_frameworks(reports, frames):
|
|
353
|
+
selected_frame = snake_framework
|
|
354
|
+
selected_index = frames.index(selected_frame)
|
|
355
|
+
wiz_framework = wiz_frameworks[selected_index]
|
|
356
|
+
wiz_report_id = create_compliance_report(
|
|
357
|
+
wiz_project_id=wiz_project_id,
|
|
358
|
+
report_name=f"{selected_frame}_project_{wiz_project_id}",
|
|
359
|
+
framework_id=wiz_framework.get("id"),
|
|
360
|
+
)
|
|
361
|
+
logger.info(f"Wiz compliance report created with ID {wiz_report_id}")
|
|
362
|
+
return [wiz_report_id]
|
|
363
|
+
|
|
364
|
+
return [report["id"] for report in reports if any(frame in report["name"] for frame in frames)]
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def fetch_and_process_report_data(wiz_report_ids: List) -> List:
|
|
368
|
+
"""
|
|
369
|
+
Fetch and process report data from report IDs.
|
|
370
|
+
|
|
371
|
+
:param List wiz_report_ids: List of Wiz report IDs.
|
|
372
|
+
:return List: List of processed report data.
|
|
373
|
+
:rtype List: List
|
|
374
|
+
"""
|
|
375
|
+
report_data = []
|
|
376
|
+
for wiz_report in wiz_report_ids:
|
|
377
|
+
download_url = get_report_url_and_status(wiz_report)
|
|
378
|
+
logger.debug(f"Download url: {download_url}")
|
|
379
|
+
with closing(requests.get(url=download_url, stream=True, timeout=10)) as data:
|
|
380
|
+
logger.info("Download URL fetched. Streaming and parsing report")
|
|
381
|
+
reader = csv.DictReader(codecs.iterdecode(data.iter_lines(), encoding="utf-8"), delimiter=",")
|
|
382
|
+
for row in reader:
|
|
383
|
+
report_data.append(row)
|
|
384
|
+
return report_data
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def fetch_framework_report(wiz_project_id: str, snake_framework: str) -> List[Any]:
|
|
388
|
+
"""
|
|
389
|
+
Fetch Framework Report from Wiz.
|
|
390
|
+
|
|
391
|
+
:param str wiz_project_id: Wiz Project ID.
|
|
392
|
+
:param str snake_framework: Framework name with spaces replaced by underscores.
|
|
393
|
+
:return: List containing the framework report data.
|
|
394
|
+
:rtype: List[Any]
|
|
395
|
+
"""
|
|
396
|
+
wiz_frameworks = fetch_frameworks()
|
|
397
|
+
frames = get_framework_names(wiz_frameworks)
|
|
398
|
+
reports = list(query_reports())
|
|
399
|
+
|
|
400
|
+
wiz_report_ids = create_report_if_needed(wiz_project_id, frames, wiz_frameworks, reports, snake_framework)
|
|
401
|
+
return fetch_and_process_report_data(wiz_report_ids)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def fetch_frameworks() -> list:
|
|
405
|
+
"""
|
|
406
|
+
Fetch frameworks from Wiz
|
|
407
|
+
|
|
408
|
+
:raises General Error: If error in API response
|
|
409
|
+
:return: List of frameworks
|
|
410
|
+
:rtype: list
|
|
411
|
+
"""
|
|
412
|
+
query = """
|
|
413
|
+
query SecurityFrameworkAutosuggestOptions($policyTypes: [SecurityFrameworkPolicyType!],
|
|
414
|
+
$onlyEnabledPolicies: Boolean) {
|
|
415
|
+
securityFrameworks(
|
|
416
|
+
first: 500
|
|
417
|
+
filterBy: {policyTypes: $policyTypes, enabled: $onlyEnabledPolicies}
|
|
418
|
+
) {
|
|
419
|
+
nodes {
|
|
420
|
+
id
|
|
421
|
+
name
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
"""
|
|
426
|
+
variables = {
|
|
427
|
+
"policyTypes": "CLOUD",
|
|
428
|
+
"first": 500,
|
|
429
|
+
}
|
|
430
|
+
resp = send_request(
|
|
431
|
+
query=query,
|
|
432
|
+
variables=variables,
|
|
433
|
+
api_endpoint_url=WizVariables.wizUrl,
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
if resp.ok:
|
|
437
|
+
# ["data"]["securityFrameworks"]["nodes"]
|
|
438
|
+
data = resp.json()
|
|
439
|
+
return data.get("data", {}).get("securityFrameworks", {}).get("nodes")
|
|
440
|
+
else:
|
|
441
|
+
error_and_exit(f"Wiz Error: {resp.status_code if resp else None} - {resp.text if resp else 'No response'}")
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def query_reports() -> list:
|
|
445
|
+
"""
|
|
446
|
+
Query Report table from Wiz
|
|
447
|
+
|
|
448
|
+
:return: list object from an API response from Wiz
|
|
449
|
+
:rtype: list
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
# The variables sent along with the above query
|
|
453
|
+
variables = {"first": 100, "filterBy": {}}
|
|
454
|
+
|
|
455
|
+
res = send_request(
|
|
456
|
+
query=REPORTS_QUERY,
|
|
457
|
+
variables=variables,
|
|
458
|
+
api_endpoint_url=WizVariables.wizUrl,
|
|
459
|
+
)
|
|
460
|
+
result = []
|
|
461
|
+
try:
|
|
462
|
+
if "errors" in res.json().keys():
|
|
463
|
+
error_and_exit(f'Wiz Error: {res.json()["errors"]}')
|
|
464
|
+
json_result = res.json()
|
|
465
|
+
result = json_result.get("data", {}).get("reports", {}).get("nodes")
|
|
466
|
+
except requests.JSONDecodeError:
|
|
467
|
+
error_and_exit(f"Unable to fetch reports from Wiz: {res.status_code}, {res.reason}")
|
|
468
|
+
return result
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def send_request(
|
|
472
|
+
query: str,
|
|
473
|
+
variables: Dict,
|
|
474
|
+
api_endpoint_url: Optional[str] = None,
|
|
475
|
+
) -> requests.Response:
|
|
476
|
+
"""
|
|
477
|
+
Send a graphQL request to Wiz.
|
|
478
|
+
|
|
479
|
+
:param str query: Query to use for GraphQL
|
|
480
|
+
:param Dict variables:
|
|
481
|
+
:param Optional[str] api_endpoint_url: Wiz GraphQL URL Default is None
|
|
482
|
+
:raises ValueError: Value Error if the access token is missing from wizAccessToken in init.yaml
|
|
483
|
+
:return requests.Response: response from post call to provided api_endpoint_url
|
|
484
|
+
:rtype requests.Response: requests.Response
|
|
485
|
+
"""
|
|
486
|
+
logger.debug("Sending a request to Wiz API")
|
|
487
|
+
api = Api()
|
|
488
|
+
payload = dict({"query": query, "variables": variables})
|
|
489
|
+
if api_endpoint_url is None:
|
|
490
|
+
api_endpoint_url = WizVariables.wizUrl
|
|
491
|
+
if WizVariables.wizAccessToken:
|
|
492
|
+
return api.post(
|
|
493
|
+
url=api_endpoint_url,
|
|
494
|
+
headers={
|
|
495
|
+
"Content-Type": CONTENT_TYPE,
|
|
496
|
+
"Authorization": BEARER + WizVariables.wizAccessToken,
|
|
497
|
+
},
|
|
498
|
+
json=payload,
|
|
499
|
+
)
|
|
500
|
+
raise ValueError("An access token is missing.")
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def create_compliance_report(
|
|
504
|
+
report_name: str,
|
|
505
|
+
wiz_project_id: str,
|
|
506
|
+
framework_id: str,
|
|
507
|
+
) -> str:
|
|
508
|
+
"""Create Wiz compliance report
|
|
509
|
+
|
|
510
|
+
:param str report_name: Report name
|
|
511
|
+
:param str wiz_project_id: Wiz Project ID
|
|
512
|
+
:param str framework_id: Wiz Framework ID
|
|
513
|
+
:return str: Compliance Report id
|
|
514
|
+
:rtype str: str
|
|
515
|
+
"""
|
|
516
|
+
report_variables = {
|
|
517
|
+
"input": {
|
|
518
|
+
"name": report_name,
|
|
519
|
+
"type": "COMPLIANCE_ASSESSMENTS",
|
|
520
|
+
"csvDelimiter": "US",
|
|
521
|
+
"projectId": wiz_project_id,
|
|
522
|
+
"complianceAssessmentsParams": {"securityFrameworkIds": [framework_id]},
|
|
523
|
+
"emailTargetParams": None,
|
|
524
|
+
"exportDestinations": None,
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return fetch_report_id(CREATE_REPORT_QUERY, report_variables, url=WizVariables.wizUrl)
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
def get_report_url_and_status(report_id: str) -> str:
|
|
532
|
+
"""
|
|
533
|
+
Generate Report URL from Wiz report
|
|
534
|
+
|
|
535
|
+
:param str report_id: Wiz report ID
|
|
536
|
+
:raises: requests.RequestException if download failed and exceeded max # of retries
|
|
537
|
+
:return: URL of report
|
|
538
|
+
:rtype: str
|
|
539
|
+
"""
|
|
540
|
+
for attempt in range(MAX_RETRIES):
|
|
541
|
+
if attempt:
|
|
542
|
+
logger.info(
|
|
543
|
+
"Report %s is still updating, waiting %.2f seconds", report_id, CHECK_INTERVAL_FOR_DOWNLOAD_REPORT
|
|
544
|
+
)
|
|
545
|
+
time.sleep(CHECK_INTERVAL_FOR_DOWNLOAD_REPORT)
|
|
546
|
+
|
|
547
|
+
response = download_report({"reportId": report_id})
|
|
548
|
+
if not response or not response.ok:
|
|
549
|
+
raise requests.RequestException("Failed to download report")
|
|
550
|
+
|
|
551
|
+
response_json = response.json()
|
|
552
|
+
errors = response_json.get("errors")
|
|
553
|
+
if errors:
|
|
554
|
+
message = errors[0]["message"]
|
|
555
|
+
if RATE_LIMIT_MSG in message:
|
|
556
|
+
rate = errors[0]["extensions"]["retryAfter"]
|
|
557
|
+
logger.warning("Sleeping %i seconds due to rate limit", rate)
|
|
558
|
+
time.sleep(rate)
|
|
559
|
+
continue
|
|
560
|
+
|
|
561
|
+
logger.error(errors)
|
|
562
|
+
else:
|
|
563
|
+
status = response_json.get("data", {}).get("report", {}).get("lastRun", {}).get("status")
|
|
564
|
+
if status == "COMPLETED":
|
|
565
|
+
return response_json["data"]["report"]["lastRun"]["url"]
|
|
566
|
+
|
|
567
|
+
raise requests.RequestException("Download failed, exceeding the maximum number of retries")
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def download_report(variables: Dict) -> requests.Response:
|
|
571
|
+
"""
|
|
572
|
+
Return a download URL for a provided Wiz report id
|
|
573
|
+
|
|
574
|
+
:param Dict variables: Variables for Wiz request
|
|
575
|
+
:return: response from Wiz API
|
|
576
|
+
:rtype: requests.Response
|
|
577
|
+
"""
|
|
578
|
+
response = send_request(DOWNLOAD_QUERY, variables=variables)
|
|
579
|
+
return response
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def _sync_compliance(
|
|
583
|
+
wiz_project_id: str,
|
|
584
|
+
regscale_id: int,
|
|
585
|
+
regscale_module: str,
|
|
586
|
+
include_not_implemented: bool,
|
|
587
|
+
client_id: str,
|
|
588
|
+
client_secret: str,
|
|
589
|
+
catalog_id: int,
|
|
590
|
+
framework: Optional[str] = "NIST800-53R5",
|
|
591
|
+
) -> List[ComplianceReport]:
|
|
592
|
+
"""
|
|
593
|
+
Sync compliance posture from Wiz to RegScale
|
|
594
|
+
|
|
595
|
+
:param str wiz_project_id: Wiz Project ID
|
|
596
|
+
:param int regscale_id: RegScale ID
|
|
597
|
+
:param str regscale_module: RegScale module
|
|
598
|
+
:param bool include_not_implemented: Include not implemented controls
|
|
599
|
+
:param str client_id: Wiz Client ID
|
|
600
|
+
:param str client_secret: Wiz Client Secret
|
|
601
|
+
:param int catalog_id: Catalog ID, defaults to None
|
|
602
|
+
:param Optional[str] framework: Framework, defaults to NIST800-53R5
|
|
603
|
+
:return: List of ComplianceReport objects
|
|
604
|
+
:rtype: List[ComplianceReport]
|
|
605
|
+
"""
|
|
606
|
+
|
|
607
|
+
logger.info("Syncing compliance from Wiz with project ID %s", wiz_project_id)
|
|
608
|
+
wiz_authenticate(
|
|
609
|
+
client_id=client_id,
|
|
610
|
+
client_secret=client_secret,
|
|
611
|
+
)
|
|
612
|
+
report_job = compliance_job_progress.add_task("[#f68d1f]Fetching Wiz compliance report...", total=1)
|
|
613
|
+
fetch_regscale_data_job = compliance_job_progress.add_task(
|
|
614
|
+
"[#f68d1f]Fetching RegScale Catalog info for framework...", total=1
|
|
615
|
+
)
|
|
616
|
+
logger.info("Fetching Wiz compliance report for project ID %s...", wiz_project_id)
|
|
617
|
+
compliance_job_progress.update(report_job, completed=True, advance=1)
|
|
618
|
+
|
|
619
|
+
framework_mapping = {
|
|
620
|
+
"CSF": "NIST CSF v1.1",
|
|
621
|
+
"NIST800-53R5": "NIST SP 800-53 Revision 5",
|
|
622
|
+
"NIST800-53R4": "NIST SP 800-53 Revision 4",
|
|
623
|
+
}
|
|
624
|
+
sync_framework = framework_mapping.get(framework)
|
|
625
|
+
snake_framework = sync_framework.replace(" ", "_")
|
|
626
|
+
logger.info(snake_framework)
|
|
627
|
+
logger.info("Fetching Wiz compliance report for project ID %s", wiz_project_id)
|
|
628
|
+
report_data = fetch_framework_report(wiz_project_id, snake_framework)
|
|
629
|
+
report_models = []
|
|
630
|
+
compliance_job_progress.update(report_job, completed=True, advance=1)
|
|
631
|
+
|
|
632
|
+
catalog = Catalog.get_with_all_details(catalog_id=catalog_id)
|
|
633
|
+
controls = catalog.get("controls") if catalog else []
|
|
634
|
+
passing_controls = dict()
|
|
635
|
+
failing_controls = dict()
|
|
636
|
+
controls_to_reports = dict()
|
|
637
|
+
existing_implementations = ControlImplementation.get_existing_control_implementations(parent_id=regscale_id)
|
|
638
|
+
compliance_job_progress.update(fetch_regscale_data_job, completed=True, advance=1)
|
|
639
|
+
logger.info(f"Analyzing ComplianceReport for framework {sync_framework} from Wiz")
|
|
640
|
+
running_compliance_job = compliance_job_progress.add_task(
|
|
641
|
+
"[#f68d1f]Building compliance posture from wiz report...",
|
|
642
|
+
total=len(report_data),
|
|
643
|
+
)
|
|
644
|
+
for row in report_data:
|
|
645
|
+
try:
|
|
646
|
+
cr = ComplianceReport(**row)
|
|
647
|
+
if cr.framework == sync_framework:
|
|
648
|
+
check_compliance(
|
|
649
|
+
cr,
|
|
650
|
+
controls,
|
|
651
|
+
passing_controls,
|
|
652
|
+
failing_controls,
|
|
653
|
+
controls_to_reports,
|
|
654
|
+
)
|
|
655
|
+
report_models.append(cr)
|
|
656
|
+
compliance_job_progress.update(running_compliance_job, advance=1)
|
|
657
|
+
except ValidationError as e:
|
|
658
|
+
logger.error(f"Error creating ComplianceReport: {e}")
|
|
659
|
+
try:
|
|
660
|
+
saving_regscale_data_job = compliance_job_progress.add_task("[#f68d1f]Saving RegScale data...", total=1)
|
|
661
|
+
ControlImplementation.create_control_implementations(
|
|
662
|
+
controls=controls,
|
|
663
|
+
parent_id=regscale_id,
|
|
664
|
+
parent_module=regscale_module,
|
|
665
|
+
existing_implementation_dict=existing_implementations,
|
|
666
|
+
full_controls=passing_controls,
|
|
667
|
+
partial_controls={},
|
|
668
|
+
failing_controls=failing_controls,
|
|
669
|
+
include_not_implemented=include_not_implemented,
|
|
670
|
+
)
|
|
671
|
+
create_assessment_from_compliance_report(
|
|
672
|
+
controls_to_reports=controls_to_reports,
|
|
673
|
+
regscale_id=regscale_id,
|
|
674
|
+
regscale_module=regscale_module,
|
|
675
|
+
controls=controls,
|
|
676
|
+
)
|
|
677
|
+
compliance_job_progress.update(saving_regscale_data_job, completed=True, advance=1)
|
|
678
|
+
|
|
679
|
+
except Exception as e:
|
|
680
|
+
logger.error(f"Error creating ControlImplementations from compliance report: {e}")
|
|
681
|
+
traceback.print_exc()
|
|
682
|
+
return report_models
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def check_compliance(
|
|
686
|
+
cr: ComplianceReport,
|
|
687
|
+
controls: List[Dict],
|
|
688
|
+
passing: Dict,
|
|
689
|
+
failing: Dict,
|
|
690
|
+
controls_to_reports: Dict,
|
|
691
|
+
) -> None:
|
|
692
|
+
"""
|
|
693
|
+
Check compliance report for against controls
|
|
694
|
+
|
|
695
|
+
:param ComplianceReport cr: Compliance Report
|
|
696
|
+
:param List[Dict] controls: Controls List
|
|
697
|
+
:param Dict passing: Passing controls
|
|
698
|
+
:param Dict failing: Failing controls
|
|
699
|
+
:param Dict controls_to_reports: Controls to reports
|
|
700
|
+
:return: None
|
|
701
|
+
:rtype: None
|
|
702
|
+
"""
|
|
703
|
+
for control in controls:
|
|
704
|
+
if f"{control.get('controlId').lower()} " in cr.compliance_check.lower():
|
|
705
|
+
_add_controls_to_controls_to_report_dict(control, controls_to_reports, cr)
|
|
706
|
+
if cr.result == ComplianceCheckStatus.PASS.value:
|
|
707
|
+
if control.get("controlId").lower() not in passing:
|
|
708
|
+
passing[control.get("controlId").lower()] = control
|
|
709
|
+
else:
|
|
710
|
+
if control.get("controlId").lower() not in failing:
|
|
711
|
+
failing[control.get("controlId").lower()] = control
|
|
712
|
+
_clean_passing_list(passing, failing)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def _add_controls_to_controls_to_report_dict(control: Dict, controls_to_reports: Dict, cr: ComplianceReport) -> None:
|
|
716
|
+
"""
|
|
717
|
+
Add controls to dict to process assessments from later
|
|
718
|
+
|
|
719
|
+
:param Dict control: Control
|
|
720
|
+
:param Dict controls_to_reports: Controls to reports
|
|
721
|
+
:param ComplianceReport cr: Compliance Report
|
|
722
|
+
:return: None
|
|
723
|
+
:rtype: None
|
|
724
|
+
"""
|
|
725
|
+
if control.get("controlId").lower() not in controls_to_reports.keys():
|
|
726
|
+
controls_to_reports[control.get("controlId").lower()] = [cr]
|
|
727
|
+
else:
|
|
728
|
+
controls_to_reports[control.get("controlId").lower()].append(cr)
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
def _clean_passing_list(passing: Dict, failing: Dict) -> None:
|
|
732
|
+
"""
|
|
733
|
+
Clean passing list. Ensures that controls that are passing are not also failing
|
|
734
|
+
|
|
735
|
+
:param Dict passing: Passing controls
|
|
736
|
+
:param Dict failing: Failing controls
|
|
737
|
+
:return: None
|
|
738
|
+
:rtype: None
|
|
739
|
+
"""
|
|
740
|
+
for control_id in failing:
|
|
741
|
+
if control_id in passing:
|
|
742
|
+
passing.pop(control_id, None)
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
def create_assessment_from_compliance_report(
|
|
746
|
+
controls_to_reports: Dict, regscale_id: int, regscale_module: str, controls: List
|
|
747
|
+
) -> None:
|
|
748
|
+
"""
|
|
749
|
+
Create assessment from compliance report
|
|
750
|
+
|
|
751
|
+
:param Dict controls_to_reports: Controls to reports
|
|
752
|
+
:param int regscale_id: RegScale ID
|
|
753
|
+
:param str regscale_module: RegScale module
|
|
754
|
+
:param List controls: Controls
|
|
755
|
+
:return: None
|
|
756
|
+
:rtype: None
|
|
757
|
+
"""
|
|
758
|
+
implementations = ControlImplementation.get_all_by_parent(parent_module=regscale_module, parent_id=regscale_id)
|
|
759
|
+
for control_id, reports in controls_to_reports.items():
|
|
760
|
+
control_record_id = None
|
|
761
|
+
for control in controls:
|
|
762
|
+
if control.get("controlId").lower() == control_id:
|
|
763
|
+
control_record_id = control.get("id")
|
|
764
|
+
break
|
|
765
|
+
filtered_results = [x for x in implementations if x.controlID == control_record_id]
|
|
766
|
+
create_report_assessment(filtered_results, reports, control_id)
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def create_report_assessment(filtered_results: List, reports: List, control_id: str) -> None:
|
|
770
|
+
"""
|
|
771
|
+
Create report assessment
|
|
772
|
+
|
|
773
|
+
:param List filtered_results: Filtered results
|
|
774
|
+
:param List reports: Reports
|
|
775
|
+
:param str control_id: Control ID
|
|
776
|
+
:return: None
|
|
777
|
+
:rtype: None
|
|
778
|
+
"""
|
|
779
|
+
implementation = filtered_results[0] if len(filtered_results) > 0 else None
|
|
780
|
+
for report in reports:
|
|
781
|
+
html_summary = format_dict_to_html(report.dict())
|
|
782
|
+
if implementation:
|
|
783
|
+
Assessment(
|
|
784
|
+
leadAssessorId=implementation.createdById,
|
|
785
|
+
title=f"Wiz compliance report assessment for {control_id}",
|
|
786
|
+
assessmentType="Control Testing",
|
|
787
|
+
plannedStart=get_current_datetime(),
|
|
788
|
+
plannedFinish=get_current_datetime(),
|
|
789
|
+
actualFinish=get_current_datetime(),
|
|
790
|
+
assessmentResult=report.result,
|
|
791
|
+
assessmentReport=html_summary,
|
|
792
|
+
status="Complete",
|
|
793
|
+
parentId=implementation.id,
|
|
794
|
+
parentModule="controls",
|
|
795
|
+
isPublic=True,
|
|
796
|
+
).create()
|