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,190 @@
|
|
|
1
|
+
# flake8: noqa
|
|
2
|
+
"""
|
|
3
|
+
System Implementation is a Model Layer in the OSCAL SSP implementation model. The
|
|
4
|
+
model is documented at https://pages.nist.gov/OSCAL/concepts/layer/implementation/ssp/
|
|
5
|
+
Note that the RegScale SSP Data model collapses several NIST OSCAL model layers together
|
|
6
|
+
including Metadata, System Characteristics, and System Implementation.
|
|
7
|
+
"""
|
|
8
|
+
from typing import Dict, List
|
|
9
|
+
|
|
10
|
+
from lxml import etree
|
|
11
|
+
|
|
12
|
+
from regscale.core.app.api import Api
|
|
13
|
+
from regscale.core.app.application import Application
|
|
14
|
+
from regscale.core.app.logz import create_logger
|
|
15
|
+
from regscale.integrations.public.fedramp.components import parse_ssp_components
|
|
16
|
+
from regscale.integrations.public.fedramp.fedramp_traversal import FedrampTraversal
|
|
17
|
+
from regscale.integrations.public.fedramp.inventory_items import parse_inventory_items
|
|
18
|
+
from regscale.integrations.public.fedramp.mappings.user import handle_user
|
|
19
|
+
from regscale.integrations.public.fedramp.xml_utils import update_ssp
|
|
20
|
+
|
|
21
|
+
logger = create_logger()
|
|
22
|
+
|
|
23
|
+
sc_events = [] # returned after tracking import events
|
|
24
|
+
SYSTEM_INFO = "System Information"
|
|
25
|
+
SYSTEM_IMP = "System Implementation"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def parse_user_extra_datas(trv: FedrampTraversal):
|
|
29
|
+
root = trv.root
|
|
30
|
+
|
|
31
|
+
ssp_updates_dict = {}
|
|
32
|
+
# USERS NOW
|
|
33
|
+
users = []
|
|
34
|
+
users = root.xpath(
|
|
35
|
+
"/ns1:system-security-plan/ns1:system-implementation/ns1:prop[@name='users-internal']/@value",
|
|
36
|
+
namespaces=trv.namespaces,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if len(users) > 0:
|
|
40
|
+
ssp_updates_dict["internalUsers"] = users[0]
|
|
41
|
+
trv.log_info(
|
|
42
|
+
{
|
|
43
|
+
"record_type": SYSTEM_INFO,
|
|
44
|
+
"model_layer": SYSTEM_IMP,
|
|
45
|
+
"event_msg": f"Recorded number of internal system users: {ssp_updates_dict['internalUsers']}",
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
else:
|
|
49
|
+
trv.log_error(
|
|
50
|
+
{
|
|
51
|
+
"record_type": SYSTEM_INFO,
|
|
52
|
+
"model_layer": SYSTEM_IMP,
|
|
53
|
+
"missing_element": "Number of Internal Users",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# USERS EXTERNAL
|
|
58
|
+
users = []
|
|
59
|
+
users = root.xpath(
|
|
60
|
+
"/ns1:system-security-plan/ns1:system-implementation/ns1:prop[@name='users-external']/@value",
|
|
61
|
+
namespaces=trv.namespaces,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if len(users) > 0:
|
|
65
|
+
ssp_updates_dict["externalUsers"] = users[0]
|
|
66
|
+
trv.log_info(
|
|
67
|
+
{
|
|
68
|
+
"record_type": SYSTEM_INFO,
|
|
69
|
+
"model_layer": SYSTEM_IMP,
|
|
70
|
+
"event_msg": f"Recorded number of external system users: {ssp_updates_dict['externalUsers']}",
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
trv.log_error(
|
|
75
|
+
{
|
|
76
|
+
"record_type": SYSTEM_INFO,
|
|
77
|
+
"model_layer": SYSTEM_IMP,
|
|
78
|
+
"missing_element": "Number of External Users",
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# USERS FUTURE
|
|
83
|
+
users = []
|
|
84
|
+
users = root.xpath(
|
|
85
|
+
"/ns1:system-security-plan/ns1:system-implementation/ns1:prop[@name='users-internal-future']/@value",
|
|
86
|
+
namespaces=trv.namespaces,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if len(users) > 0:
|
|
90
|
+
ssp_updates_dict["internalUsersFuture"] = users[0]
|
|
91
|
+
trv.log_info(
|
|
92
|
+
{
|
|
93
|
+
"record_type": SYSTEM_INFO,
|
|
94
|
+
"model_layer": SYSTEM_IMP,
|
|
95
|
+
"event_msg": f"Recorded number of future internal system users: {ssp_updates_dict['internalUsersFuture']}",
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
trv.log_error(
|
|
100
|
+
{
|
|
101
|
+
"record_type": SYSTEM_INFO,
|
|
102
|
+
"model_layer": SYSTEM_IMP,
|
|
103
|
+
"missing_element": "Number of Internal Users (Future)",
|
|
104
|
+
}
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Users External
|
|
108
|
+
users = []
|
|
109
|
+
users = root.xpath(
|
|
110
|
+
"/ns1:system-security-plan/ns1:system-implementation/ns1:prop[@name='users-external']/@value",
|
|
111
|
+
namespaces=trv.namespaces,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
if len(users) > 0:
|
|
115
|
+
ssp_updates_dict["externalUsersFuture"] = users[0]
|
|
116
|
+
trv.log_info(
|
|
117
|
+
{
|
|
118
|
+
"record_type": SYSTEM_INFO,
|
|
119
|
+
"model_layer": SYSTEM_IMP,
|
|
120
|
+
"event_msg": f"Recorded number of future external system users: {ssp_updates_dict['externalUsersFuture']}",
|
|
121
|
+
}
|
|
122
|
+
)
|
|
123
|
+
else:
|
|
124
|
+
trv.log_error(
|
|
125
|
+
{
|
|
126
|
+
"record_type": SYSTEM_INFO,
|
|
127
|
+
"model_layer": SYSTEM_IMP,
|
|
128
|
+
"missing_element": "Number of External Users (Future)",
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if len(ssp_updates_dict) > 0:
|
|
133
|
+
update_ssp(ssp_updates_dict, trv.ssp_id)
|
|
134
|
+
else:
|
|
135
|
+
trv.log_error(
|
|
136
|
+
{
|
|
137
|
+
"record_type": SYSTEM_INFO,
|
|
138
|
+
"model_layer": SYSTEM_IMP,
|
|
139
|
+
"missing_element": "Number of Users",
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def parse_system_implementation(trv: FedrampTraversal) -> List[Dict[str, str]]:
|
|
145
|
+
"""Parse system implementation from OSCAL SSP XML to RegScale SSP JSON
|
|
146
|
+
|
|
147
|
+
:param FedrampTraversal trv: FedrampTraversal instance
|
|
148
|
+
:return: List of events
|
|
149
|
+
:rtype: List[Dict[str, str]]
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
# Handle:
|
|
153
|
+
# <system-implementation>
|
|
154
|
+
# <user>
|
|
155
|
+
handle_user(trv)
|
|
156
|
+
|
|
157
|
+
# parses Components
|
|
158
|
+
components_dict = parse_ssp_components(trv)
|
|
159
|
+
inventory_items = parse_inventory_items(trv, components_dict)
|
|
160
|
+
|
|
161
|
+
# Handle:
|
|
162
|
+
# <system-implementation>
|
|
163
|
+
# <users-internal>
|
|
164
|
+
# <users-external>
|
|
165
|
+
# <users-internal-future>
|
|
166
|
+
parse_user_extra_datas(trv)
|
|
167
|
+
|
|
168
|
+
# Handle:
|
|
169
|
+
# <system-implementation>
|
|
170
|
+
# <leveraged-authorization>
|
|
171
|
+
# parse_leveraged_auth_new(trv)
|
|
172
|
+
|
|
173
|
+
return [{"event": "System Implementation parsed successfully"}]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
if __name__ == "__main__":
|
|
177
|
+
# Example usage
|
|
178
|
+
app = Application()
|
|
179
|
+
api = Api()
|
|
180
|
+
tree = etree.parse("./artifacts/AwesomeCloudSSP.xml")
|
|
181
|
+
root = tree.getroot()
|
|
182
|
+
|
|
183
|
+
ssp_id = 2037
|
|
184
|
+
namespaces = {
|
|
185
|
+
"oscal": "http://csrc.nist.gov/ns/oscal/1.0",
|
|
186
|
+
"fedramp": "https://fedramp.gov/ns/oscal",
|
|
187
|
+
"ns1": "http://csrc.nist.gov/ns/oscal/1.0",
|
|
188
|
+
}
|
|
189
|
+
trv = FedrampTraversal(api=api, root=root, ssp_id=ssp_id, namespaces=namespaces)
|
|
190
|
+
parse_system_implementation(trv)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""XML Utility functions"""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import urljoin
|
|
4
|
+
|
|
5
|
+
from lxml import etree
|
|
6
|
+
|
|
7
|
+
from regscale.core.app.api import Api
|
|
8
|
+
from regscale.core.app.application import Application
|
|
9
|
+
from regscale.core.app.logz import create_logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extract_markup_content(element: etree._Element) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Extract the text content from an XML element, including text content from child elements
|
|
15
|
+
|
|
16
|
+
:param etree._Element element: The XML element to extract text content from
|
|
17
|
+
:return: String of text content
|
|
18
|
+
:rtype: str
|
|
19
|
+
"""
|
|
20
|
+
# List of tags to look for in the content
|
|
21
|
+
markup_tags = [
|
|
22
|
+
"p",
|
|
23
|
+
"h1",
|
|
24
|
+
"h2",
|
|
25
|
+
"h3",
|
|
26
|
+
"h4",
|
|
27
|
+
"h5",
|
|
28
|
+
"h6",
|
|
29
|
+
"ol",
|
|
30
|
+
"ul",
|
|
31
|
+
"pre",
|
|
32
|
+
"table",
|
|
33
|
+
"li",
|
|
34
|
+
]
|
|
35
|
+
# Initialize a list to store the extracted text content
|
|
36
|
+
content = []
|
|
37
|
+
# Iterate through all the child elements, including text and specific tags
|
|
38
|
+
for child in element.iter():
|
|
39
|
+
# Extract the local name (i.e., the tag name without the namespace) of each child element
|
|
40
|
+
tag_without_namespace = etree.QName(child.tag).localname
|
|
41
|
+
# If the child's tag is one of the specified markup tags, add its text content
|
|
42
|
+
if tag_without_namespace in markup_tags and child.text:
|
|
43
|
+
content.append(child.text.strip())
|
|
44
|
+
# If the child has a tail (text following a tag), add it as well
|
|
45
|
+
if child.tail and child.tail.strip():
|
|
46
|
+
content.append(child.tail.strip())
|
|
47
|
+
# Join the content with newlines and return
|
|
48
|
+
return "\n".join(content).replace(" ", " ").replace(" ", " ")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# Joshua you may or may not agree with putting here. Expect I may need to update SSP from different places. We can talk!
|
|
52
|
+
def update_ssp(ssp_updates_dict: dict, ssp_id: int) -> None:
|
|
53
|
+
"""
|
|
54
|
+
This function will attempt to PUT any key-value pairs found in the dict as updates to an existing SSP in RegScale
|
|
55
|
+
It assumes that each key corresponds to a valid SSP field. First retrieves existing SSP using ID, then overwrites
|
|
56
|
+
fields as found in updates before submitting as a PUT to update the record.
|
|
57
|
+
|
|
58
|
+
:param dict ssp_updates_dict: Object to update SSP with
|
|
59
|
+
:param int ssp_id: SSP ID to update in RegScale
|
|
60
|
+
:rtype: None
|
|
61
|
+
"""
|
|
62
|
+
app = Application()
|
|
63
|
+
api = Api()
|
|
64
|
+
config = app.config
|
|
65
|
+
logger = create_logger()
|
|
66
|
+
headers = {"accept": "*/*", "Authorization": config["token"]}
|
|
67
|
+
headers_json = {
|
|
68
|
+
"accept": "*/*",
|
|
69
|
+
"Content-Type": "application/json-patch+json",
|
|
70
|
+
"Authorization": config["token"],
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
response = api.get(url=urljoin(config["domain"], f"/api/securityplans/{ssp_id}"), headers=headers)
|
|
74
|
+
if response.ok:
|
|
75
|
+
existing_ssp = response.json()
|
|
76
|
+
for key, value in ssp_updates_dict.items():
|
|
77
|
+
existing_ssp[key] = value
|
|
78
|
+
ssp_json = existing_ssp
|
|
79
|
+
response = api.put(
|
|
80
|
+
url=urljoin(config["domain"], f"/api/securityplans/{ssp_id}"),
|
|
81
|
+
json=ssp_json,
|
|
82
|
+
headers=headers_json,
|
|
83
|
+
)
|
|
84
|
+
if response.ok:
|
|
85
|
+
logger.info(f"Successfully updated SSP {ssp_id} with additional data.")
|
|
86
|
+
else:
|
|
87
|
+
logger.error("Problems updating SSP with latest additional data.")
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Module to allow sorting nist catalog controls into RegScale"""
|
|
4
|
+
|
|
5
|
+
# standard python imports
|
|
6
|
+
import re
|
|
7
|
+
from typing import Tuple, Any, Union, TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from regscale.core.app.application import Application
|
|
13
|
+
from regscale.core.app.api import Api
|
|
14
|
+
|
|
15
|
+
import click
|
|
16
|
+
from requests import JSONDecodeError, Response
|
|
17
|
+
from regscale.core.app.api import normalize_url
|
|
18
|
+
|
|
19
|
+
from regscale.core.app.utils.app_utils import (
|
|
20
|
+
create_logger,
|
|
21
|
+
error_and_exit,
|
|
22
|
+
check_file_path,
|
|
23
|
+
save_data_to,
|
|
24
|
+
create_progress_object,
|
|
25
|
+
)
|
|
26
|
+
from regscale.utils.threading.threadhandler import create_threads, thread_assignment
|
|
27
|
+
|
|
28
|
+
# initialize Application and Api objects
|
|
29
|
+
|
|
30
|
+
logger = create_logger()
|
|
31
|
+
job_progress = create_progress_object()
|
|
32
|
+
|
|
33
|
+
# create global variables for threads to store successful
|
|
34
|
+
# and failed control updates
|
|
35
|
+
updated_controls, failed_controls, retry_failed, retry_success = [], [], [], []
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@click.group()
|
|
39
|
+
def nist():
|
|
40
|
+
"""Sort the controls of a catalog in RegScale."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@nist.command(name="sort_control_ids")
|
|
44
|
+
@click.option(
|
|
45
|
+
"--catalog_id",
|
|
46
|
+
type=click.INT,
|
|
47
|
+
help="The RegScale catalog ID number.",
|
|
48
|
+
prompt="RegScale catalog ID#",
|
|
49
|
+
required=True,
|
|
50
|
+
)
|
|
51
|
+
def sort_control_ids(catalog_id: int) -> None:
|
|
52
|
+
"""Sort the provided catalog's controls in RegScale with the provided ID #."""
|
|
53
|
+
sort_controls_by_id(catalog_id)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def sort_controls_by_id(catalog_id: int) -> None:
|
|
57
|
+
"""
|
|
58
|
+
Sort the provided catalog's controls in RegScale with the provided ID #
|
|
59
|
+
|
|
60
|
+
:param int catalog_id: ID # of the catalog in RegScale to sort controls for
|
|
61
|
+
:rtype: None
|
|
62
|
+
"""
|
|
63
|
+
from regscale.core.app.application import Application
|
|
64
|
+
from regscale.core.app.api import Api
|
|
65
|
+
|
|
66
|
+
app = Application()
|
|
67
|
+
api = Api()
|
|
68
|
+
config = app.config
|
|
69
|
+
# update api limits depending on maxThreads
|
|
70
|
+
api.pool_connections = max(api.pool_connections, config["maxThreads"])
|
|
71
|
+
api.pool_maxsize = max(api.pool_maxsize, config["maxThreads"])
|
|
72
|
+
security_control_count: int = 0
|
|
73
|
+
|
|
74
|
+
# get all controls by catalog
|
|
75
|
+
url_controls_get_all = f"{app.config['domain']}/api/SecurityControls/getAllByCatalog/{catalog_id}"
|
|
76
|
+
|
|
77
|
+
# get all existing control implementations
|
|
78
|
+
security_control_res = api.get(url_controls_get_all)
|
|
79
|
+
security_control_data = None
|
|
80
|
+
try:
|
|
81
|
+
# try to convert the response to a JSON object
|
|
82
|
+
security_control_data = security_control_res.json()
|
|
83
|
+
security_control_count = len(security_control_data)
|
|
84
|
+
except JSONDecodeError:
|
|
85
|
+
error_and_exit("Unable to retrieve control implementations for this SSP in RegScale.")
|
|
86
|
+
|
|
87
|
+
# output the RegScale controls, if there are any, else exit
|
|
88
|
+
if security_control_count == 0 or not security_control_data:
|
|
89
|
+
# generate URL to the provided catalog id
|
|
90
|
+
catalog_url = normalize_url(f'{app.config["domain"]}/form/catalogues/{catalog_id}')
|
|
91
|
+
error_and_exit(f"No controls were received for catalog #{catalog_id}.\nPlease verify: {catalog_url}")
|
|
92
|
+
# verify artifacts directory exists before saving the received security controls
|
|
93
|
+
check_file_path("artifacts")
|
|
94
|
+
save_data_to(
|
|
95
|
+
file=Path(f"./artifacts/regscale-catalog-{catalog_id}-controls.json"),
|
|
96
|
+
data=security_control_data,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# loop over the controls and add a sortId to each control
|
|
100
|
+
sorted_controls: list = []
|
|
101
|
+
for control in security_control_data:
|
|
102
|
+
control["sortId"] = parse_control_id(control)
|
|
103
|
+
sorted_controls.append(control["sortId"])
|
|
104
|
+
|
|
105
|
+
# output the RegScale controls
|
|
106
|
+
save_data_to(
|
|
107
|
+
file=Path(f"artifacts/catalog-{catalog_id}-sorted-control-ids.json"),
|
|
108
|
+
data=sorted_controls,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# create threads to process all controls
|
|
112
|
+
with job_progress:
|
|
113
|
+
logger.info(
|
|
114
|
+
"%s security control(s) will be updated.",
|
|
115
|
+
security_control_count,
|
|
116
|
+
)
|
|
117
|
+
# create progress bar and update the controls in RegScale
|
|
118
|
+
updating_controls = job_progress.add_task(
|
|
119
|
+
f"[#f8b737]Updating {security_control_count} security control(s)...",
|
|
120
|
+
total=security_control_count,
|
|
121
|
+
)
|
|
122
|
+
create_threads(
|
|
123
|
+
process=update_security_controls,
|
|
124
|
+
args=(security_control_data, api, updating_controls, False),
|
|
125
|
+
thread_count=security_control_count,
|
|
126
|
+
)
|
|
127
|
+
# output the result
|
|
128
|
+
logger.info(
|
|
129
|
+
"Updated %s/%s control(s) successfully with %s failure(s).",
|
|
130
|
+
security_control_count,
|
|
131
|
+
len(updated_controls),
|
|
132
|
+
len(failed_controls),
|
|
133
|
+
)
|
|
134
|
+
# check if any controls need to be retried
|
|
135
|
+
if failed_controls:
|
|
136
|
+
save_data_to(file=Path("./artifacts/failed-controls.json"), data=failed_controls)
|
|
137
|
+
with job_progress:
|
|
138
|
+
logger.info(
|
|
139
|
+
"%s security control(s) will be updated.",
|
|
140
|
+
security_control_count,
|
|
141
|
+
)
|
|
142
|
+
# create progress bar and retry the failed controls
|
|
143
|
+
retrying_controls = job_progress.add_task(
|
|
144
|
+
f"[#ffff00]Retrying {len(failed_controls)} failed security control(s)...",
|
|
145
|
+
total=len(failed_controls),
|
|
146
|
+
)
|
|
147
|
+
create_threads(
|
|
148
|
+
process=update_security_controls,
|
|
149
|
+
args=(failed_controls, api, retrying_controls, True),
|
|
150
|
+
thread_count=len(failed_controls),
|
|
151
|
+
)
|
|
152
|
+
logger.info("%i/%i retrie(s) were successful.", len(retry_success), len(failed_controls))
|
|
153
|
+
save_data_to(
|
|
154
|
+
file=Path("./artifacts/retry-successful-controls.json"),
|
|
155
|
+
data=retry_success,
|
|
156
|
+
)
|
|
157
|
+
if retry_failed:
|
|
158
|
+
logger.info("%i failed retrie(s)", len(retry_failed))
|
|
159
|
+
save_data_to(file=Path("./artifacts/retry-failed-controls.json"), data=retry_failed)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def update_security_controls(args: Tuple, thread: int) -> None:
|
|
163
|
+
"""
|
|
164
|
+
Function to utilize threading and update security controls in RegScale
|
|
165
|
+
|
|
166
|
+
:param Tuple args: Tuple of args to use during the process
|
|
167
|
+
:param int thread: Thread number of current thread
|
|
168
|
+
:rtype: None
|
|
169
|
+
"""
|
|
170
|
+
# set up local variables from args passed
|
|
171
|
+
security_control_data, api, task, retry = args
|
|
172
|
+
|
|
173
|
+
# find which records should be executed by the current thread
|
|
174
|
+
threads = thread_assignment(thread=thread, total_items=len(security_control_data))
|
|
175
|
+
|
|
176
|
+
# iterate through the thread assignment items and process them
|
|
177
|
+
for i in range(len(threads)):
|
|
178
|
+
# set the control for the thread & update it in RegScale
|
|
179
|
+
control = security_control_data[threads[i]]
|
|
180
|
+
control_url = f'{api.config["domain"]}/api/SecurityControls/{control["id"]}'
|
|
181
|
+
# check if the description is populated
|
|
182
|
+
control["description"] = control.get("description", control.get("title"))
|
|
183
|
+
# update control in RegScale
|
|
184
|
+
response = api.put(control_url, json=control)
|
|
185
|
+
# verify update was successful
|
|
186
|
+
append_to_list(response, retry, control)
|
|
187
|
+
# update progress bar
|
|
188
|
+
job_progress.update(task, advance=1)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def append_to_list(response: Response, retry: bool, control: dict) -> None:
|
|
192
|
+
"""
|
|
193
|
+
Function to append the control to the correct list based if it passed and if it was a retry
|
|
194
|
+
|
|
195
|
+
:param Response response: The response from the update API call to RegScale
|
|
196
|
+
:param bool retry: Whether this was an attempted retry for a control update in RegScale
|
|
197
|
+
:param dict control: The control data to append to the correct list
|
|
198
|
+
:rtype: None
|
|
199
|
+
"""
|
|
200
|
+
if response.ok:
|
|
201
|
+
logger.debug("Success: control #%s was updated successfully.", control["sortId"])
|
|
202
|
+
if retry:
|
|
203
|
+
retry_success.append(control)
|
|
204
|
+
else:
|
|
205
|
+
updated_controls.append(control)
|
|
206
|
+
else:
|
|
207
|
+
logger.debug(
|
|
208
|
+
"Error: unable to update control #%s\n%s: %s",
|
|
209
|
+
control["sortId"],
|
|
210
|
+
response.status_code,
|
|
211
|
+
response.text,
|
|
212
|
+
)
|
|
213
|
+
if retry:
|
|
214
|
+
retry_failed.append(control)
|
|
215
|
+
else:
|
|
216
|
+
failed_controls.append(control)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def parse_control_id(control: Union[dict, str]) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Function to parse the provided control dictionary from RegScale and returns a sortId as a string
|
|
222
|
+
|
|
223
|
+
:param Union[dict, str] control: A control from RegScale or a control string
|
|
224
|
+
:raises KeyError: If the control doesn't have a sortId or controlId
|
|
225
|
+
:return: string to use as a sortId
|
|
226
|
+
:rtype: str
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
def _pad_zeros(match: Any) -> str:
|
|
230
|
+
"""
|
|
231
|
+
Function to pad zeros to the control's sortId if needed
|
|
232
|
+
|
|
233
|
+
:param Any match: Match object from the regex
|
|
234
|
+
:return: string to use as a sortId
|
|
235
|
+
:rtype: str
|
|
236
|
+
"""
|
|
237
|
+
prefix = match.group(1)
|
|
238
|
+
digits = match.group(2)
|
|
239
|
+
return prefix + digits.zfill(2) if len(digits) == 1 else prefix + digits
|
|
240
|
+
|
|
241
|
+
def _extract_id(control: Union[dict, str]) -> str:
|
|
242
|
+
"""
|
|
243
|
+
Extracts the ID from the control, handling both string and dictionary types.
|
|
244
|
+
|
|
245
|
+
:param Union[dict, str] control: A control from RegScale or a control string
|
|
246
|
+
:return: ID of the control
|
|
247
|
+
:rtype: str
|
|
248
|
+
"""
|
|
249
|
+
if isinstance(control, str):
|
|
250
|
+
return control
|
|
251
|
+
try:
|
|
252
|
+
return control["sortId"]
|
|
253
|
+
except KeyError:
|
|
254
|
+
return control.get("controlId", "")
|
|
255
|
+
|
|
256
|
+
def _format_id(original_id: str, control: dict) -> str:
|
|
257
|
+
"""
|
|
258
|
+
Formats the ID by removing leading zeros in specific patterns.
|
|
259
|
+
|
|
260
|
+
:param str original_id: Original ID of the control
|
|
261
|
+
:param dict control: Control data
|
|
262
|
+
:return: Formatted ID of the control
|
|
263
|
+
:rtype: str
|
|
264
|
+
"""
|
|
265
|
+
formatted_id = re.sub(r"(-|\.)0*(\d+)", _pad_zeros, original_id)
|
|
266
|
+
if isinstance(control, dict):
|
|
267
|
+
expected_control = re.sub(r"(?<=-)(0+)(?=\d)", "", control["title"].split(" ")[0])
|
|
268
|
+
if expected_control not in formatted_id:
|
|
269
|
+
return control["title"].split(" ")[0]
|
|
270
|
+
return formatted_id
|
|
271
|
+
|
|
272
|
+
original_id = _extract_id(control)
|
|
273
|
+
if original_id == "":
|
|
274
|
+
raise KeyError("Control ID is missing.")
|
|
275
|
+
return _format_id(original_id, control)
|