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,665 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Application Configuration"""
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import inspect
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import sys
|
|
11
|
+
from threading import Lock
|
|
12
|
+
from typing import Any, Optional, Union
|
|
13
|
+
from urllib.parse import urljoin
|
|
14
|
+
|
|
15
|
+
import requests
|
|
16
|
+
import yaml
|
|
17
|
+
from pydantic import Field
|
|
18
|
+
from requests import Response
|
|
19
|
+
from yaml.scanner import ScannerError
|
|
20
|
+
|
|
21
|
+
from regscale.core.app.internal.encrypt import IOA21H98
|
|
22
|
+
from regscale.core.app.logz import create_logger
|
|
23
|
+
from regscale.utils.threading.threadhandler import ThreadManager
|
|
24
|
+
|
|
25
|
+
DEFAULT_CLIENT = "<myClientIdGoesHere>"
|
|
26
|
+
DEFAULT_SECRET = "<mySecretGoesHere>"
|
|
27
|
+
DEFAULT_POPULATED = "<createdProgrammatically>"
|
|
28
|
+
DEFAULT_TENANT = "<myTenantIdGoesHere>"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Singleton(type):
|
|
32
|
+
"""
|
|
33
|
+
Singleton class to prevent multiple instances of Application
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_instances = {}
|
|
37
|
+
|
|
38
|
+
def __call__(cls, *args, **kwargs):
|
|
39
|
+
if cls not in cls._instances or kwargs.get("config"):
|
|
40
|
+
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
|
41
|
+
return cls._instances[cls]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class Application(metaclass=Singleton):
|
|
45
|
+
"""
|
|
46
|
+
RegScale CLI configuration class
|
|
47
|
+
|
|
48
|
+
:param Optional[dict] config: Configuration dictionary to use instead of init.yaml, defaults to None
|
|
49
|
+
:param bool local_config: Whether to use the local config file, defaults to True
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
config: dict = Field(default_factory=dict)
|
|
53
|
+
_config_lock = Lock()
|
|
54
|
+
|
|
55
|
+
def __init__(
|
|
56
|
+
self,
|
|
57
|
+
config: Optional[dict] = None,
|
|
58
|
+
local_config: bool = True,
|
|
59
|
+
):
|
|
60
|
+
self.config_file = os.getenv("REGSCALE_CONFIG_FILE", "init.yaml")
|
|
61
|
+
self.api_handler = None
|
|
62
|
+
template = {
|
|
63
|
+
"stigBatchSize": 100,
|
|
64
|
+
"adAccessToken": DEFAULT_POPULATED,
|
|
65
|
+
"adAuthUrl": "https://login.microsoftonline.com/",
|
|
66
|
+
"adClientId": DEFAULT_CLIENT,
|
|
67
|
+
"adClientSecret": DEFAULT_SECRET,
|
|
68
|
+
"adGraphUrl": "https://graph.microsoft.com/.default",
|
|
69
|
+
"adTenantId": DEFAULT_TENANT,
|
|
70
|
+
"assessmentDays": 10,
|
|
71
|
+
"azure365AccessToken": DEFAULT_POPULATED,
|
|
72
|
+
"azure365ClientId": DEFAULT_CLIENT,
|
|
73
|
+
"azure365Secret": DEFAULT_SECRET,
|
|
74
|
+
"azure365TenantId": DEFAULT_TENANT,
|
|
75
|
+
"azureCloudAccessToken": DEFAULT_POPULATED,
|
|
76
|
+
"azureCloudClientId": DEFAULT_CLIENT,
|
|
77
|
+
"azureCloudSecret": DEFAULT_SECRET,
|
|
78
|
+
"azureCloudTenantId": DEFAULT_TENANT,
|
|
79
|
+
"azureCloudSubscriptionId": "<mySubscriptionIdGoesHere>",
|
|
80
|
+
"cisaKev": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
|
|
81
|
+
"crowdstrikeClientId": DEFAULT_CLIENT,
|
|
82
|
+
"crowdstrikeClientSecret": DEFAULT_SECRET,
|
|
83
|
+
"crowdstrikeBaseUrl": "<crowdstrikeApiUrl>",
|
|
84
|
+
"dependabotId": "<myGithubUserIdGoesHere>",
|
|
85
|
+
"dependabotOwner": "<myGithubRepoOwnerGoesHere>",
|
|
86
|
+
"dependabotRepo": "<myGithubRepoNameGoesHere>",
|
|
87
|
+
"dependabotToken": "<myGithubPersonalAccessTokenGoesHere>",
|
|
88
|
+
"domain": "https://regscale.yourcompany.com/",
|
|
89
|
+
"evidenceFolder": "./evidence",
|
|
90
|
+
"passScore": 80,
|
|
91
|
+
"failScore": 30,
|
|
92
|
+
"gcpCredentials": "<path/to/credentials.json>",
|
|
93
|
+
"gcpOrganizationId": "<000000000000>",
|
|
94
|
+
"gcpProjectId": "<000000000000>",
|
|
95
|
+
"gcpScanType": "<organization | project>",
|
|
96
|
+
"githubDomain": "api.github.com",
|
|
97
|
+
"issues": {
|
|
98
|
+
"aqua": {
|
|
99
|
+
"critical": 30,
|
|
100
|
+
"high": 30,
|
|
101
|
+
"moderate": 90,
|
|
102
|
+
"low": 180,
|
|
103
|
+
"status": "Open",
|
|
104
|
+
"minimumSeverity": "low",
|
|
105
|
+
"useKev": True,
|
|
106
|
+
},
|
|
107
|
+
"amazon": {
|
|
108
|
+
"high": 30,
|
|
109
|
+
"low": 365,
|
|
110
|
+
"moderate": 90,
|
|
111
|
+
"status": "Open",
|
|
112
|
+
"minimumSeverity": "low",
|
|
113
|
+
"useKev": True,
|
|
114
|
+
},
|
|
115
|
+
"defender365": {
|
|
116
|
+
"high": 30,
|
|
117
|
+
"low": 365,
|
|
118
|
+
"moderate": 90,
|
|
119
|
+
"status": "Open",
|
|
120
|
+
},
|
|
121
|
+
"defenderCloud": {
|
|
122
|
+
"high": 30,
|
|
123
|
+
"low": 365,
|
|
124
|
+
"moderate": 90,
|
|
125
|
+
"status": "Open",
|
|
126
|
+
},
|
|
127
|
+
"defenderFile": {
|
|
128
|
+
"high": 30,
|
|
129
|
+
"low": 365,
|
|
130
|
+
"moderate": 90,
|
|
131
|
+
"status": "Open",
|
|
132
|
+
"useKev": True,
|
|
133
|
+
},
|
|
134
|
+
"ecr": {
|
|
135
|
+
"critical": 30,
|
|
136
|
+
"high": 30,
|
|
137
|
+
"moderate": 90,
|
|
138
|
+
"low": 180,
|
|
139
|
+
"status": "Open",
|
|
140
|
+
"minimumSeverity": "low",
|
|
141
|
+
"useKev": True,
|
|
142
|
+
},
|
|
143
|
+
"jira": {
|
|
144
|
+
"highest": 7,
|
|
145
|
+
"high": 30,
|
|
146
|
+
"medium": 90,
|
|
147
|
+
"low": 180,
|
|
148
|
+
"lowest": 365,
|
|
149
|
+
"status": "Open",
|
|
150
|
+
},
|
|
151
|
+
"qualys": {
|
|
152
|
+
"high": 30,
|
|
153
|
+
"moderate": 90,
|
|
154
|
+
"low": 365,
|
|
155
|
+
"status": "Open",
|
|
156
|
+
"useKev": True,
|
|
157
|
+
},
|
|
158
|
+
"salesforce": {
|
|
159
|
+
"critical": 7,
|
|
160
|
+
"high": 30,
|
|
161
|
+
"medium": 90,
|
|
162
|
+
"low": 365,
|
|
163
|
+
"status": "Open",
|
|
164
|
+
},
|
|
165
|
+
"snyk": {
|
|
166
|
+
"critical": 30,
|
|
167
|
+
"high": 30,
|
|
168
|
+
"moderate": 90,
|
|
169
|
+
"low": 180,
|
|
170
|
+
"status": "Open",
|
|
171
|
+
"minimumSeverity": "low",
|
|
172
|
+
"useKev": True, # Override the issue due date with the KEV date
|
|
173
|
+
},
|
|
174
|
+
"nexpose": {
|
|
175
|
+
"critical": 30,
|
|
176
|
+
"high": 30,
|
|
177
|
+
"moderate": 90,
|
|
178
|
+
"low": 180,
|
|
179
|
+
"status": "Open",
|
|
180
|
+
"minimumSeverity": "low",
|
|
181
|
+
"useKev": True, # Override the issue due date with the KEV date
|
|
182
|
+
},
|
|
183
|
+
"prisma": {
|
|
184
|
+
"critical": 30,
|
|
185
|
+
"high": 30,
|
|
186
|
+
"moderate": 90,
|
|
187
|
+
"low": 180,
|
|
188
|
+
"status": "Open",
|
|
189
|
+
"minimumSeverity": "low",
|
|
190
|
+
"useKev": True, # Override the issue due date with the KEV date
|
|
191
|
+
},
|
|
192
|
+
"tenable": {
|
|
193
|
+
"critical": 30,
|
|
194
|
+
"high": 30,
|
|
195
|
+
"moderate": 90,
|
|
196
|
+
"low": 180,
|
|
197
|
+
"status": "Open",
|
|
198
|
+
"useKev": False, # Override the issue due date with the KEV date
|
|
199
|
+
},
|
|
200
|
+
"wiz": {
|
|
201
|
+
"critical": 30,
|
|
202
|
+
"high": 90,
|
|
203
|
+
"low": 365,
|
|
204
|
+
"medium": 90,
|
|
205
|
+
"status": "Open",
|
|
206
|
+
},
|
|
207
|
+
"xray": {
|
|
208
|
+
"critical": 30,
|
|
209
|
+
"high": 30,
|
|
210
|
+
"moderate": 90,
|
|
211
|
+
"low": 180,
|
|
212
|
+
"status": "Open",
|
|
213
|
+
"minimumSeverity": "low",
|
|
214
|
+
"useKev": True,
|
|
215
|
+
},
|
|
216
|
+
"veracode": {
|
|
217
|
+
"critical": 30,
|
|
218
|
+
"high": 30,
|
|
219
|
+
"moderate": 90,
|
|
220
|
+
"low": 180,
|
|
221
|
+
"status": "Open",
|
|
222
|
+
"minimumSeverity": "low",
|
|
223
|
+
"useKev": False,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
"jiraApiToken": "<jiraAPIToken>",
|
|
227
|
+
"jiraUrl": "<myJiraUrl>",
|
|
228
|
+
"jiraUserName": "<jiraUserName>",
|
|
229
|
+
"maxThreads": 1000,
|
|
230
|
+
"nistCpeApiKey": "<myNistCpeApiKey>",
|
|
231
|
+
"oktaApiToken": "Can be a SSWS token from Okta or created programmatically",
|
|
232
|
+
"oktaClientId": "<oktaClientIdGoesHere>",
|
|
233
|
+
"oktaUrl": "<oktaUrlGoesHere>",
|
|
234
|
+
"oscalLocation": "/opt/OSCAL",
|
|
235
|
+
"pwshPath": "/opt/microsoft/powershell/7/pwsh",
|
|
236
|
+
"qualysUrl": "https://yourcompany.qualys.com/api/2.0/fo/scan/",
|
|
237
|
+
"qualysUserName": "<qualysUserName>",
|
|
238
|
+
"qualysPassword": "<qualysPassword>",
|
|
239
|
+
"sicuraUrl": "<mySicuraUrl>",
|
|
240
|
+
"sicuraToken": "<mySicuraToken>",
|
|
241
|
+
"salesforceUserName": "<salesforceUserName>",
|
|
242
|
+
"salesforcePassword": "<salesforcePassword>",
|
|
243
|
+
"salesforceToken": "<salesforceSecurityToken>",
|
|
244
|
+
"snowPassword": "<snowPassword>",
|
|
245
|
+
"snowUrl": "<mySnowUrl>",
|
|
246
|
+
"snowUserName": "<snowUserName>",
|
|
247
|
+
"sonarToken": "<mySonarToken>",
|
|
248
|
+
"tenableAccessKey": "<tenableAccessKeyGoesHere>",
|
|
249
|
+
"tenableSecretKey": "<tenableSecretKeyGoesHere>",
|
|
250
|
+
"tenableUrl": "https://sc.tenalab.online",
|
|
251
|
+
"tenableMinimumSeverityFilter": "low",
|
|
252
|
+
"token": DEFAULT_POPULATED,
|
|
253
|
+
"userId": "enter RegScale user id here",
|
|
254
|
+
"otx": "enter AlienVault API key here",
|
|
255
|
+
"wizAccessToken": DEFAULT_POPULATED,
|
|
256
|
+
"wizAuthUrl": "https://auth.wiz.io/oauth/token",
|
|
257
|
+
"wizExcludes": "My things to exclude here",
|
|
258
|
+
"wizScope": "<filled out programmatically after authenticating to Wiz>",
|
|
259
|
+
"wizUrl": "<my Wiz URL goes here>",
|
|
260
|
+
"wizReportAge": 15,
|
|
261
|
+
"wizLastInventoryPull": "<wizLastInventoryPull>",
|
|
262
|
+
"wizInventoryFilterBy": "<wizInventoryFilterBy>",
|
|
263
|
+
"wizIssueFilterBy": "<wizIssueFilterBy>",
|
|
264
|
+
"wizFullPullLimitHours": 8,
|
|
265
|
+
"wizStigMapperFile": os.path.join(
|
|
266
|
+
os.getcwd(), os.makedirs("artifacts", exist_ok=True) or "artifacts/stig_mapper_rules.json"
|
|
267
|
+
), # could blow up on missing artifacts folder
|
|
268
|
+
"timeout": 60,
|
|
269
|
+
"tenableGroupByPlugin": False,
|
|
270
|
+
"findingFromMapping": {
|
|
271
|
+
"aqua": {
|
|
272
|
+
"remediation": "default",
|
|
273
|
+
"title": "default",
|
|
274
|
+
"description": "default",
|
|
275
|
+
},
|
|
276
|
+
"tenable_sc": {
|
|
277
|
+
"remediation": "default",
|
|
278
|
+
"title": "default",
|
|
279
|
+
"description": "default",
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
logger = create_logger()
|
|
284
|
+
if os.environ.get("LOGLEVEL", "INFO").upper() == "DEBUG":
|
|
285
|
+
stack = inspect.stack()
|
|
286
|
+
logger.debug("*" * 80)
|
|
287
|
+
logger.debug(f"Initializing Application from {stack[1].filename}")
|
|
288
|
+
logger.debug("*" * 80)
|
|
289
|
+
logger.debug(f"Initializing in directory: {os.getcwd()}")
|
|
290
|
+
self.template = template
|
|
291
|
+
self.templated = False
|
|
292
|
+
self.logger = logger
|
|
293
|
+
self.local_config = local_config
|
|
294
|
+
self.running_in_airflow = os.getenv("REGSCALE_AIRFLOW") == "true"
|
|
295
|
+
if isinstance(config, str):
|
|
296
|
+
config = self._read_config_from_str(config)
|
|
297
|
+
self.config = self._gen_config(config)
|
|
298
|
+
self.os = platform.system()
|
|
299
|
+
self.input_host = ""
|
|
300
|
+
self.thread_manager = ThreadManager(self.config.get("maxThreads", 100))
|
|
301
|
+
logger.debug("Finished Initializing Application")
|
|
302
|
+
logger.debug("*" * 80)
|
|
303
|
+
|
|
304
|
+
def __getitem__(self, key: Any) -> Any:
|
|
305
|
+
"""
|
|
306
|
+
Get an item
|
|
307
|
+
|
|
308
|
+
:param Any key: key to retrieve
|
|
309
|
+
:return: value of provided key
|
|
310
|
+
:rtype: Any
|
|
311
|
+
"""
|
|
312
|
+
return self.config.__getitem__(key)
|
|
313
|
+
|
|
314
|
+
def __setitem__(self, key: Any, value: Any) -> None:
|
|
315
|
+
"""
|
|
316
|
+
Set an item
|
|
317
|
+
|
|
318
|
+
:param Any key: Key to set the provided value
|
|
319
|
+
:param Any value: Value to set the provided key
|
|
320
|
+
:rtype: None
|
|
321
|
+
"""
|
|
322
|
+
self.config.__setitem__(key, value)
|
|
323
|
+
|
|
324
|
+
def __delitem__(self, key: Any) -> None:
|
|
325
|
+
"""
|
|
326
|
+
Delete an item
|
|
327
|
+
|
|
328
|
+
:param Any key: Key desired to delete
|
|
329
|
+
:rtype: None
|
|
330
|
+
"""
|
|
331
|
+
self.config.__delitem__(key)
|
|
332
|
+
|
|
333
|
+
def __iter__(self):
|
|
334
|
+
"""
|
|
335
|
+
Return iterator
|
|
336
|
+
"""
|
|
337
|
+
return self.config.__iter__()
|
|
338
|
+
|
|
339
|
+
def __len__(self) -> int:
|
|
340
|
+
"""
|
|
341
|
+
Get the length of the config
|
|
342
|
+
|
|
343
|
+
:return: # of items in config
|
|
344
|
+
:rtype: int
|
|
345
|
+
"""
|
|
346
|
+
return len(self.config) if self.config is not None else 0
|
|
347
|
+
|
|
348
|
+
def __contains__(self, x: str) -> bool:
|
|
349
|
+
"""
|
|
350
|
+
Check config if it contains string
|
|
351
|
+
|
|
352
|
+
:param str x: String to check if it exists in the config
|
|
353
|
+
:return: Whether the provided string exists in the config
|
|
354
|
+
:rtype: bool
|
|
355
|
+
"""
|
|
356
|
+
return self.config.__contains__(x)
|
|
357
|
+
|
|
358
|
+
def _read_config_from_str(self, config: str) -> dict:
|
|
359
|
+
"""
|
|
360
|
+
Tries to convert the provided config string to a dictionary, and if it fails, try to use the
|
|
361
|
+
string as a file path, and if that fails it will return an empty dictionary
|
|
362
|
+
|
|
363
|
+
:param str config: String to try and convert to a dictionary before trying to use as a file path
|
|
364
|
+
:return: Dictionary of provided string or file, or an empty dictionary
|
|
365
|
+
:rtype: dict
|
|
366
|
+
"""
|
|
367
|
+
try:
|
|
368
|
+
return json.loads(config)
|
|
369
|
+
except json.JSONDecodeError:
|
|
370
|
+
self.config_file = config
|
|
371
|
+
try:
|
|
372
|
+
config = self._get_conf()
|
|
373
|
+
return config
|
|
374
|
+
except Exception as ex:
|
|
375
|
+
self.logger.debug(f"Unable to load config from file: {ex}")
|
|
376
|
+
return {}
|
|
377
|
+
|
|
378
|
+
def _fetch_config_from_regscale(self, config: Optional[dict] = None) -> dict:
|
|
379
|
+
"""
|
|
380
|
+
Fetch config from RegScale via API
|
|
381
|
+
|
|
382
|
+
:param Optional[dict] config: configuration dictionary, defaults to None
|
|
383
|
+
:return: Combined config from RegScale and the provided config
|
|
384
|
+
:rtype: dict
|
|
385
|
+
"""
|
|
386
|
+
if config is None:
|
|
387
|
+
config = {}
|
|
388
|
+
self.logger.debug(f"Provided config in _fetch_config_from_regscale is: {type(config)}")
|
|
389
|
+
token = config.get("token") or os.getenv("REGSCALE_TOKEN")
|
|
390
|
+
domain = config.get("domain") or os.getenv("REGSCALE_DOMAIN")
|
|
391
|
+
if domain is None or "http" not in domain or domain == self.template["domain"]:
|
|
392
|
+
domain = self.retrieve_domain()[:-1] if self.retrieve_domain().endswith("/") else self.retrieve_domain()
|
|
393
|
+
self.logger.debug(f"domain: {domain}, token: {token}")
|
|
394
|
+
if domain is not None and token is not None:
|
|
395
|
+
self.logger.info(f"Fetching config from {domain}...")
|
|
396
|
+
try:
|
|
397
|
+
response = requests.get(
|
|
398
|
+
url=urljoin(domain, "/api/tenants/getDetailedCliConfig"),
|
|
399
|
+
headers={
|
|
400
|
+
"Content-Type": "application/json",
|
|
401
|
+
"Accept": "application/json",
|
|
402
|
+
"Authorization": token,
|
|
403
|
+
},
|
|
404
|
+
)
|
|
405
|
+
self.logger.debug(f"status_code: {response.status_code} text: {response.text}")
|
|
406
|
+
res_data = response.json()
|
|
407
|
+
if config := res_data.get("cliConfig"):
|
|
408
|
+
parsed_dict = yaml.safe_load(config)
|
|
409
|
+
self.logger.debug(f"parsed_dict: {parsed_dict}")
|
|
410
|
+
parsed_dict["token"] = token
|
|
411
|
+
parsed_dict["domain"] = domain
|
|
412
|
+
from regscale.core.app.internal.login import parse_user_id_from_jwt
|
|
413
|
+
|
|
414
|
+
parsed_dict["userId"] = res_data.get("userId") or parse_user_id_from_jwt(self, token)
|
|
415
|
+
self.logger.debug(f"Updated domain, token and userId: {parsed_dict}")
|
|
416
|
+
self.logger.info("Successfully fetched config from RegScale.")
|
|
417
|
+
# fill in any missing keys with the template
|
|
418
|
+
return {**self.template, **parsed_dict}
|
|
419
|
+
except Exception as ex:
|
|
420
|
+
self.logger.error("Unable to fetch config from RegScale.\n%s", ex)
|
|
421
|
+
return {}
|
|
422
|
+
|
|
423
|
+
def _load_config_from_click_context(self) -> Optional[dict]:
|
|
424
|
+
"""
|
|
425
|
+
Load configuration from Click context
|
|
426
|
+
|
|
427
|
+
:return: Configuration dictionary
|
|
428
|
+
:rtype: Optional[dict]
|
|
429
|
+
"""
|
|
430
|
+
try:
|
|
431
|
+
import click
|
|
432
|
+
|
|
433
|
+
ctx = click.get_current_context()
|
|
434
|
+
if ctx and ctx.obj and "CONFIG" in ctx.obj:
|
|
435
|
+
self.logger.debug("Found config in Click context...")
|
|
436
|
+
if click_config := ctx.obj["CONFIG"]:
|
|
437
|
+
self.logger.debug("Using config from Click context")
|
|
438
|
+
config = self.verify_config(template=self.template, config=click_config)
|
|
439
|
+
self.save_config(config)
|
|
440
|
+
ctx.obj["CONFIG"] = config
|
|
441
|
+
return config
|
|
442
|
+
except (RuntimeError, ImportError):
|
|
443
|
+
# RuntimeError is raised when there's no active Click context
|
|
444
|
+
# ImportError is raised if Click is not available
|
|
445
|
+
pass
|
|
446
|
+
return None
|
|
447
|
+
|
|
448
|
+
def _gen_config(self, config: Optional[Union[dict, str]] = None) -> dict:
|
|
449
|
+
"""
|
|
450
|
+
Generate the Application config from file or environment
|
|
451
|
+
|
|
452
|
+
:param Optional[Union[dict, str]] config: Configuration dictionary, defaults to None
|
|
453
|
+
:raises: TypeError if unable to generate config file
|
|
454
|
+
:return: configuration as a dictionary
|
|
455
|
+
:rtype: dict
|
|
456
|
+
"""
|
|
457
|
+
# Check for Click context first
|
|
458
|
+
if click_config := self._load_config_from_click_context():
|
|
459
|
+
self.logger.debug("Successfully retrieved config from Click context.")
|
|
460
|
+
return click_config
|
|
461
|
+
|
|
462
|
+
if self.running_in_airflow:
|
|
463
|
+
if airflow_config := self._get_airflow_config(config):
|
|
464
|
+
self.logger.debug("Successfully retrieved config from Airflow.")
|
|
465
|
+
return airflow_config
|
|
466
|
+
try:
|
|
467
|
+
if config and self.local_config:
|
|
468
|
+
self.logger.debug(f"Config provided as :\n{type(config)}")
|
|
469
|
+
file_config = config
|
|
470
|
+
elif not self.local_config:
|
|
471
|
+
file_config = {}
|
|
472
|
+
else:
|
|
473
|
+
file_config = self._get_conf() or {}
|
|
474
|
+
# Merge
|
|
475
|
+
env = self._get_env()
|
|
476
|
+
if self.templated is False:
|
|
477
|
+
self.logger.debug(f"Starting with {self.config_file}:{len(file_config)} and merging environment.")
|
|
478
|
+
config = {**file_config, **env}
|
|
479
|
+
else:
|
|
480
|
+
self.logger.debug(
|
|
481
|
+
f"Starting with config from environment and merging {self.config_file}:{len(file_config)}."
|
|
482
|
+
)
|
|
483
|
+
config = {**env, **file_config}
|
|
484
|
+
except ScannerError:
|
|
485
|
+
config = self.template
|
|
486
|
+
except TypeError:
|
|
487
|
+
self.logger.error(f"ERROR: {self.config_file} has been encrypted! Please decrypt it before proceeding.\n")
|
|
488
|
+
IOA21H98(self.config_file)
|
|
489
|
+
sys.exit()
|
|
490
|
+
if config is not None:
|
|
491
|
+
# verify keys aren't null and the values are the expected data type
|
|
492
|
+
config = self.verify_config(template=self.template, config=config)
|
|
493
|
+
self.save_config(config)
|
|
494
|
+
# Return config
|
|
495
|
+
return config
|
|
496
|
+
|
|
497
|
+
def _get_airflow_config(self, config: Optional[Union[dict, str]] = None) -> Optional[dict]:
|
|
498
|
+
if config:
|
|
499
|
+
self.logger.debug(f"Received config from Airflow as: {type(config)}")
|
|
500
|
+
# check to see if config is a string because airflow can pass a string instead of a dict
|
|
501
|
+
try:
|
|
502
|
+
config = json.loads(config.replace("'", '"')) if isinstance(config, str) else config
|
|
503
|
+
except json.JSONDecodeError:
|
|
504
|
+
return None
|
|
505
|
+
if isinstance(config, dict):
|
|
506
|
+
config = self._fetch_config_from_regscale(config=config)
|
|
507
|
+
if isinstance(config, str):
|
|
508
|
+
config = self._read_config_from_str(config)
|
|
509
|
+
return config
|
|
510
|
+
elif os.getenv("REGSCALE_TOKEN") and os.getenv("REGSCALE_DOMAIN"):
|
|
511
|
+
self.logger.debug("No config provided, fetching from RegScale via api.")
|
|
512
|
+
return self._fetch_config_from_regscale(
|
|
513
|
+
config={
|
|
514
|
+
"token": os.getenv("REGSCALE_TOKEN"),
|
|
515
|
+
"domain": os.getenv("REGSCALE_DOMAIN"),
|
|
516
|
+
}
|
|
517
|
+
)
|
|
518
|
+
return config or None
|
|
519
|
+
|
|
520
|
+
def _get_env(self) -> dict:
|
|
521
|
+
"""
|
|
522
|
+
return dict of RegScale keys from system
|
|
523
|
+
|
|
524
|
+
:return: Application config
|
|
525
|
+
:rtype: dict
|
|
526
|
+
"""
|
|
527
|
+
all_keys = self.template.keys()
|
|
528
|
+
sys_keys = [key for key in os.environ if key in all_keys]
|
|
529
|
+
# Update Template
|
|
530
|
+
dat = {}
|
|
531
|
+
try:
|
|
532
|
+
dat = self.template.copy()
|
|
533
|
+
for k in sys_keys:
|
|
534
|
+
dat[k] = os.environ[k]
|
|
535
|
+
except KeyError as ex:
|
|
536
|
+
self.logger.error("Key Error!!: %s", ex)
|
|
537
|
+
self.logger.debug("dat: %s", dat)
|
|
538
|
+
self.templated = dat == self.template
|
|
539
|
+
return dat
|
|
540
|
+
|
|
541
|
+
def _get_conf(self) -> dict:
|
|
542
|
+
"""
|
|
543
|
+
Get configuration from init.yaml if exists
|
|
544
|
+
|
|
545
|
+
:return: Application config
|
|
546
|
+
:rtype: dict
|
|
547
|
+
"""
|
|
548
|
+
config = None
|
|
549
|
+
# load the config from YAML
|
|
550
|
+
with self._config_lock: # Acquire the lock
|
|
551
|
+
try:
|
|
552
|
+
with open(self.config_file, encoding="utf-8") as stream:
|
|
553
|
+
self.logger.debug(f"Loading {self.config_file}")
|
|
554
|
+
config = yaml.safe_load(stream)
|
|
555
|
+
except FileNotFoundError as ex:
|
|
556
|
+
self.logger.debug(
|
|
557
|
+
"%s!\n This RegScale CLI application will create the %s file in the current working directory.",
|
|
558
|
+
ex,
|
|
559
|
+
)
|
|
560
|
+
finally:
|
|
561
|
+
self.logger.debug("_get_conf: %s, %s", config, type(config))
|
|
562
|
+
return config
|
|
563
|
+
|
|
564
|
+
def save_config(self, conf: dict) -> None:
|
|
565
|
+
"""
|
|
566
|
+
Save Configuration to init.yaml
|
|
567
|
+
|
|
568
|
+
:param dict conf: Application configuration
|
|
569
|
+
:rtype: None
|
|
570
|
+
"""
|
|
571
|
+
self.config = conf
|
|
572
|
+
if self.api_handler is not None:
|
|
573
|
+
self.api_handler.config = conf
|
|
574
|
+
self.api_handler.domain = conf.get("domain") or self.retrieve_domain()
|
|
575
|
+
if self.running_in_airflow:
|
|
576
|
+
self.logger.debug(
|
|
577
|
+
f"Updated config and not saving to {self.config_file} because CLI is running in an Airflow container."
|
|
578
|
+
)
|
|
579
|
+
return None
|
|
580
|
+
try:
|
|
581
|
+
self.logger.debug(f"Saving config to {self.config_file}.")
|
|
582
|
+
with self._config_lock:
|
|
583
|
+
with open(self.config_file, "w", encoding="utf-8") as file:
|
|
584
|
+
yaml.dump(conf, file)
|
|
585
|
+
except OSError:
|
|
586
|
+
self.logger.error(f"Could not save config to {self.config_file}.")
|
|
587
|
+
|
|
588
|
+
# Has to be Any class to prevent circular imports
|
|
589
|
+
def get_regscale_license(self, api: Any) -> Optional[Response]:
|
|
590
|
+
"""
|
|
591
|
+
Get RegScale license of provided application via provided API object
|
|
592
|
+
|
|
593
|
+
:param Any api: API object
|
|
594
|
+
:return: API response, if successful or None
|
|
595
|
+
:rtype: Optional[Response]
|
|
596
|
+
"""
|
|
597
|
+
config = self.config or api.config
|
|
598
|
+
if config is None and self.running_in_airflow:
|
|
599
|
+
config = self._get_airflow_config()
|
|
600
|
+
elif config is None:
|
|
601
|
+
config = self._gen_config()
|
|
602
|
+
domain = config.get("domain") or self.retrieve_domain()
|
|
603
|
+
if domain.endswith("/"):
|
|
604
|
+
domain = domain[:-1]
|
|
605
|
+
with contextlib.suppress(requests.RequestException):
|
|
606
|
+
return api.get(
|
|
607
|
+
url=f"{domain}/api/config/getLicense".lower(),
|
|
608
|
+
)
|
|
609
|
+
return None
|
|
610
|
+
|
|
611
|
+
def load_config(self) -> dict:
|
|
612
|
+
"""
|
|
613
|
+
Load Configuration file: init.yaml
|
|
614
|
+
|
|
615
|
+
:return: Dict of config
|
|
616
|
+
:rtype: dict
|
|
617
|
+
"""
|
|
618
|
+
try:
|
|
619
|
+
with self._config_lock:
|
|
620
|
+
with open(self.config_file, "r", encoding="utf-8") as stream:
|
|
621
|
+
return yaml.safe_load(stream)
|
|
622
|
+
except FileNotFoundError:
|
|
623
|
+
return {}
|
|
624
|
+
|
|
625
|
+
def retrieve_domain(self) -> str:
|
|
626
|
+
"""
|
|
627
|
+
Retrieve the domain from the OS environment if it exists
|
|
628
|
+
|
|
629
|
+
:return: The domain
|
|
630
|
+
:rtype: str
|
|
631
|
+
"""
|
|
632
|
+
self.logger.debug("Unable to determine domain, using retrieve_domain()...")
|
|
633
|
+
# REGSCALE_DOMAIN is the default host
|
|
634
|
+
for envar in ["REGSCALE_DOMAIN", "PLATFORM_HOST", "domain"]:
|
|
635
|
+
if host := os.environ.get(envar):
|
|
636
|
+
if host.startswith("http"):
|
|
637
|
+
self.logger.debug(f"Found {envar}={host} in environment.")
|
|
638
|
+
return host
|
|
639
|
+
return "https://regscale.yourcompany.com/"
|
|
640
|
+
|
|
641
|
+
def verify_config(self, template: dict, config: dict) -> dict:
|
|
642
|
+
"""
|
|
643
|
+
Verify keys and value types in init.yaml while retaining keys in config that are not present in template
|
|
644
|
+
|
|
645
|
+
:param dict template: Default template configuration
|
|
646
|
+
:param dict config: Dictionary to compare against template
|
|
647
|
+
:return: validated and/or updated config
|
|
648
|
+
:rtype: dict
|
|
649
|
+
"""
|
|
650
|
+
updated_config = config.copy() # Start with a copy of the original config
|
|
651
|
+
|
|
652
|
+
# Update or add template keys in config
|
|
653
|
+
for key, template_value in template.items():
|
|
654
|
+
config_value = config.get(key)
|
|
655
|
+
|
|
656
|
+
# If key missing or value type mismatch, use template value
|
|
657
|
+
if config_value is None or config_value == "" or not isinstance(config_value, type(template_value)):
|
|
658
|
+
updated_config[key] = template_value
|
|
659
|
+
# If value is a dict, recurse
|
|
660
|
+
elif isinstance(template_value, dict):
|
|
661
|
+
updated_config[key] = self.verify_config(template_value, config.get(key, {}))
|
|
662
|
+
# Else, retain the config value
|
|
663
|
+
else:
|
|
664
|
+
updated_config[key] = config_value
|
|
665
|
+
return updated_config
|