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,93 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Dataclass for a Microsoft Defender recommendations or alerts"""
|
|
4
|
+
|
|
5
|
+
# standard python imports
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Optional
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel, Field, field_validator, ConfigDict
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class DefenderData(BaseModel):
|
|
14
|
+
"""DefenderData Model"""
|
|
15
|
+
|
|
16
|
+
_model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
17
|
+
|
|
18
|
+
id: str # Required
|
|
19
|
+
data: dict # Required
|
|
20
|
+
system: str # Required
|
|
21
|
+
object: str # Required
|
|
22
|
+
analyzed: Optional[str] = Field(default=False) # type: ignore
|
|
23
|
+
created: Optional[str] = Field(default=False) # type: ignore
|
|
24
|
+
integration_field: Optional[str] = None
|
|
25
|
+
init_key: Optional[str] = None
|
|
26
|
+
|
|
27
|
+
def __init__(self, *args, **data):
|
|
28
|
+
super().__init__(*args, **data)
|
|
29
|
+
self.integration_field = self.get_integration_field(self.system, self.object)
|
|
30
|
+
self.init_key = self.get_init_key()
|
|
31
|
+
|
|
32
|
+
@field_validator("system")
|
|
33
|
+
def validate_system(cls, v: str) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Validates the riskAdjustment field.
|
|
36
|
+
|
|
37
|
+
:param str v: The value to validate
|
|
38
|
+
:raise ValueError: If the value is not valid
|
|
39
|
+
|
|
40
|
+
:return: The validated values
|
|
41
|
+
:rtype: str
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
allowed_values = ["365", "cloud"]
|
|
45
|
+
if v not in allowed_values:
|
|
46
|
+
raise ValueError(f"system must be one of {allowed_values}")
|
|
47
|
+
return v
|
|
48
|
+
|
|
49
|
+
@field_validator("object")
|
|
50
|
+
def validate_object(cls, v: str) -> str:
|
|
51
|
+
"""
|
|
52
|
+
Validates the riskAdjustment field.
|
|
53
|
+
|
|
54
|
+
:param str v: The value to validate
|
|
55
|
+
:raise ValueError: If the value is not valid
|
|
56
|
+
|
|
57
|
+
:return: The validated values
|
|
58
|
+
:rtype: str
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
allowed_values = ["alerts", "recommendations"]
|
|
62
|
+
if v not in allowed_values:
|
|
63
|
+
raise ValueError(f"object must be one of {allowed_values}")
|
|
64
|
+
return v
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def get_integration_field(system: str, object: str) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Get the integration field for the provided system and object
|
|
70
|
+
|
|
71
|
+
:return: The integration field for the provided system and object
|
|
72
|
+
:rtype: str
|
|
73
|
+
"""
|
|
74
|
+
issue_integration_field = {
|
|
75
|
+
"365_alerts": "defenderAlertId",
|
|
76
|
+
"365_recommendations": "defenderId",
|
|
77
|
+
"cloud_alerts": "defenderCloudId",
|
|
78
|
+
"cloud_recommendations": "manualDetectionId",
|
|
79
|
+
}
|
|
80
|
+
return issue_integration_field.get(f"{system}_{object}", "pluginId")
|
|
81
|
+
|
|
82
|
+
def get_init_key(self) -> str:
|
|
83
|
+
"""
|
|
84
|
+
Get the init.yaml key for the system and object
|
|
85
|
+
|
|
86
|
+
:return: The init.yaml key for the system and object
|
|
87
|
+
:rtype: str
|
|
88
|
+
"""
|
|
89
|
+
init_mapping = {
|
|
90
|
+
"365": "defender365",
|
|
91
|
+
"cloud": "defenderCloud",
|
|
92
|
+
}
|
|
93
|
+
return init_mapping.get(self.system, "defender")
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration model to import data from Defender .csv export
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from regscale.core.app.application import Application
|
|
9
|
+
from regscale.core.app.logz import create_logger
|
|
10
|
+
from regscale.core.app.utils.app_utils import get_current_datetime, is_valid_fqdn
|
|
11
|
+
from regscale.core.utils.date import datetime_obj, datetime_str
|
|
12
|
+
from regscale.models import Asset, ImportValidater, Vulnerability
|
|
13
|
+
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DefenderImport(FlatFileImporter):
|
|
17
|
+
def __init__(self, **kwargs):
|
|
18
|
+
self.name = kwargs.get("name")
|
|
19
|
+
self.vuln_title = "SUBASSESSMENTNAME"
|
|
20
|
+
self.vuln_id = "SUBASSESSMENTID"
|
|
21
|
+
logger = create_logger()
|
|
22
|
+
self.fmt = "%Y-%m-%d"
|
|
23
|
+
self.dt_format = "%Y-%m-%d %H:%M:%S"
|
|
24
|
+
self.required_headers = [
|
|
25
|
+
"SEVERITY",
|
|
26
|
+
self.vuln_title,
|
|
27
|
+
self.vuln_id,
|
|
28
|
+
]
|
|
29
|
+
self.mapping_file = kwargs.get("mappings_path")
|
|
30
|
+
self.disable_mapping = kwargs.get("disable_mapping")
|
|
31
|
+
self.validater = ImportValidater(
|
|
32
|
+
self.required_headers, kwargs.get("file_path"), self.mapping_file, self.disable_mapping
|
|
33
|
+
)
|
|
34
|
+
self.headers = self.validater.parsed_headers
|
|
35
|
+
self.mapping = self.validater.mapping
|
|
36
|
+
|
|
37
|
+
super().__init__(
|
|
38
|
+
logger=logger,
|
|
39
|
+
app=Application(),
|
|
40
|
+
headers=self.headers,
|
|
41
|
+
asset_func=self.create_asset,
|
|
42
|
+
vuln_func=self.create_vuln,
|
|
43
|
+
extra_headers_allowed=True,
|
|
44
|
+
**kwargs,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def determine_first_seen(self, dat: dict) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Determine the first seen date of the vulnerability
|
|
50
|
+
|
|
51
|
+
:param dict dat: Data row from CSV file
|
|
52
|
+
:return: The first seen date as a string
|
|
53
|
+
:rtype: str
|
|
54
|
+
"""
|
|
55
|
+
# Remove the 'Z' at the end
|
|
56
|
+
iso_string = self.mapping.get_value(dat, "TIMEGENERATED", "").rstrip("Z")
|
|
57
|
+
|
|
58
|
+
# Convert to datetime object
|
|
59
|
+
dt_object = datetime_obj(iso_string)
|
|
60
|
+
|
|
61
|
+
return datetime_str(dt_object, self.dt_format)
|
|
62
|
+
|
|
63
|
+
def create_asset(self, dat: Optional[dict] = None) -> Asset:
|
|
64
|
+
"""
|
|
65
|
+
Create an asset from a row in the Snyk file
|
|
66
|
+
|
|
67
|
+
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
68
|
+
:return: RegScale Asset object
|
|
69
|
+
:rtype: Asset
|
|
70
|
+
"""
|
|
71
|
+
additional_data = json.loads(self.mapping.get_value(dat, "ADDITIONALDATA", {}))
|
|
72
|
+
os = Asset.find_os(additional_data.get("imageDetails", {}).get("osDetails", ""))
|
|
73
|
+
name = additional_data.get("repositoryName", "")
|
|
74
|
+
valid_name = is_valid_fqdn(name)
|
|
75
|
+
return Asset(
|
|
76
|
+
**{
|
|
77
|
+
"id": 0,
|
|
78
|
+
"name": name,
|
|
79
|
+
"ipAddress": "0.0.0.0",
|
|
80
|
+
"isPublic": True,
|
|
81
|
+
"status": "Active (On Network)",
|
|
82
|
+
"assetCategory": "Software",
|
|
83
|
+
"bLatestScan": True,
|
|
84
|
+
"bAuthenticatedScan": True,
|
|
85
|
+
"scanningTool": self.name,
|
|
86
|
+
"assetOwnerId": self.config["userId"],
|
|
87
|
+
"assetType": "Other",
|
|
88
|
+
"fqdn": name if valid_name else None,
|
|
89
|
+
"systemAdministratorId": self.config["userId"],
|
|
90
|
+
"parentId": self.attributes.parent_id,
|
|
91
|
+
"parentModule": self.attributes.parent_module,
|
|
92
|
+
"operatingSystem": os,
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def create_vuln(self, dat: Optional[dict] = None, **kwargs: dict) -> Optional[Vulnerability]:
|
|
97
|
+
"""
|
|
98
|
+
Create a vulnerability from a row in the Snyk csv file
|
|
99
|
+
|
|
100
|
+
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
101
|
+
:param dict **kwargs: Additional keyword arguments
|
|
102
|
+
:return: RegScale Vulnerability object or None
|
|
103
|
+
:rtype: Optional[Vulnerability]
|
|
104
|
+
"""
|
|
105
|
+
regscale_vuln = None
|
|
106
|
+
severity = self.mapping.get_value(dat, "SEVERITY", "").lower()
|
|
107
|
+
additional_data = json.loads(self.mapping.get_value(dat, "ADDITIONALDATA", {}))
|
|
108
|
+
hostname = additional_data.get("repositoryName", "")
|
|
109
|
+
description = self.mapping.get_value(dat, self.vuln_title)
|
|
110
|
+
solution = self.mapping.get_value(dat, self.vuln_id)
|
|
111
|
+
config = self.attributes.app.config
|
|
112
|
+
asset_match = [asset for asset in self.data["assets"] if asset.name == hostname]
|
|
113
|
+
asset = asset_match[0] if asset_match else None
|
|
114
|
+
cves = [cve.get("title", "") for cve in additional_data.get("cve", [])]
|
|
115
|
+
cvss_v3_score = float(additional_data.get("cvssV30Score", 0))
|
|
116
|
+
if dat and asset_match:
|
|
117
|
+
regscale_vuln = Vulnerability(
|
|
118
|
+
id=0,
|
|
119
|
+
scanId=0, # set later
|
|
120
|
+
parentId=asset.id,
|
|
121
|
+
parentModule="assets",
|
|
122
|
+
ipAddress="0.0.0.0", # No ip address available
|
|
123
|
+
lastSeen=get_current_datetime(),
|
|
124
|
+
firstSeen=self.determine_first_seen(dat),
|
|
125
|
+
daysOpen=None,
|
|
126
|
+
dns=hostname,
|
|
127
|
+
mitigated=None,
|
|
128
|
+
operatingSystem=None,
|
|
129
|
+
severity=severity,
|
|
130
|
+
plugInName=description,
|
|
131
|
+
cve=", ".join(cves) if cves else self.mapping.get_value(dat, self.vuln_title),
|
|
132
|
+
vprScore=None,
|
|
133
|
+
cvsSv3BaseScore=cvss_v3_score,
|
|
134
|
+
tenantsId=0,
|
|
135
|
+
title=f"{description} on asset {asset.name}",
|
|
136
|
+
description=description,
|
|
137
|
+
plugInText=self.mapping.get_value(dat, self.vuln_title),
|
|
138
|
+
createdById=config["userId"],
|
|
139
|
+
lastUpdatedById=config["userId"],
|
|
140
|
+
dateCreated=get_current_datetime(),
|
|
141
|
+
extra_data={"solution": solution},
|
|
142
|
+
)
|
|
143
|
+
return regscale_vuln
|
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""A class to import DRF forms"""
|
|
4
|
+
import re
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any, Optional, Union
|
|
7
|
+
|
|
8
|
+
from openpyxl import Workbook, load_workbook, worksheet
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from rich.progress import Progress, track
|
|
11
|
+
|
|
12
|
+
from regscale.core.app.logz import create_logger
|
|
13
|
+
from regscale.core.app.utils.app_utils import create_progress_object
|
|
14
|
+
from regscale.integrations.integration.issue import IntegrationIssue
|
|
15
|
+
from regscale.models.regscale_models.deviation import Deviation
|
|
16
|
+
from regscale.models.regscale_models.issue import Issue
|
|
17
|
+
from regscale.models.regscale_models.property import Property
|
|
18
|
+
|
|
19
|
+
DR_SUBMISSION_DATE = "DR Submission Date"
|
|
20
|
+
CVSS_BASE_SCORE = "CVSS Base Score"
|
|
21
|
+
ADJUSTED_CVSS_SCORE = "Adjusted CVSS Score"
|
|
22
|
+
DR_NUMBER = "DR Number"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class DRF(IntegrationIssue):
|
|
26
|
+
"""
|
|
27
|
+
Deviation Request Form class
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, **kwargs):
|
|
31
|
+
super().__init__(**kwargs)
|
|
32
|
+
self.properties = []
|
|
33
|
+
logger = create_logger()
|
|
34
|
+
self.logger = logger
|
|
35
|
+
file_path: Union[str, None] = kwargs.get("file_path", None)
|
|
36
|
+
if not file_path:
|
|
37
|
+
raise ValueError("File path is required")
|
|
38
|
+
self.file_path = Path(file_path)
|
|
39
|
+
self.id_other_identifier_map = self.get_id_map()
|
|
40
|
+
self.module = kwargs.get("module", "securityplans")
|
|
41
|
+
self.module_id = kwargs.get("module_id", 0)
|
|
42
|
+
|
|
43
|
+
self.metadata = {}
|
|
44
|
+
self.report = []
|
|
45
|
+
self.errors = []
|
|
46
|
+
self.existing_deviations = Deviation.get_existing_deviations_by_ssp(
|
|
47
|
+
self.module_id,
|
|
48
|
+
issue_ids=[poam.get("id") for poam in self.id_other_identifier_map.values()],
|
|
49
|
+
poam_map=self.id_other_identifier_map,
|
|
50
|
+
)
|
|
51
|
+
self.drf_data = []
|
|
52
|
+
self.missed_drf_data = []
|
|
53
|
+
self.import_drf()
|
|
54
|
+
self.logger.info("Saving DR Identifiers to Issue properties.. ")
|
|
55
|
+
create_properties = [
|
|
56
|
+
prop
|
|
57
|
+
for prop in self.properties
|
|
58
|
+
if prop.value.upper()
|
|
59
|
+
not in {
|
|
60
|
+
val.get("dr_number").upper() for val in self.id_other_identifier_map.values() if val.get("dr_number")
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
update_properties = self.get_update_properties()
|
|
64
|
+
if create_properties:
|
|
65
|
+
Property.batch_create(create_properties)
|
|
66
|
+
if update_properties:
|
|
67
|
+
Property.batch_update(update_properties)
|
|
68
|
+
self.export_report()
|
|
69
|
+
|
|
70
|
+
def pull(self) -> tuple[Workbook, list[str]]:
|
|
71
|
+
"""
|
|
72
|
+
Pull data from Excel Workbook
|
|
73
|
+
|
|
74
|
+
:return: The workbook and the sheets
|
|
75
|
+
:rtype: tuple[Workbook, list[str]]
|
|
76
|
+
"""
|
|
77
|
+
workbook = load_workbook(filename=self.file_path, data_only=True, read_only=True)
|
|
78
|
+
sheets = workbook.sheetnames
|
|
79
|
+
pattern = "DR Sheet"
|
|
80
|
+
|
|
81
|
+
return workbook, [item for item in sheets if re.search(pattern, item)]
|
|
82
|
+
|
|
83
|
+
def gen_metadata(self) -> dict:
|
|
84
|
+
"""
|
|
85
|
+
Generate metadata from the DRF sheet including the system information and POC information, example:
|
|
86
|
+
{
|
|
87
|
+
"CSP Name": "CSP Name",
|
|
88
|
+
"System Name": "System Name",
|
|
89
|
+
"Impact Level": "Impact Level",
|
|
90
|
+
"DR Submission Date": "DR Submission Date",
|
|
91
|
+
"Name": "Name",
|
|
92
|
+
"Title": "Title",
|
|
93
|
+
"Phone": "Phone",
|
|
94
|
+
"Email": "Email"
|
|
95
|
+
}
|
|
96
|
+
:return: The metadata as a dictionary example:
|
|
97
|
+
:rtype: dict
|
|
98
|
+
"""
|
|
99
|
+
import pandas as pd # Optimize import performance
|
|
100
|
+
import copy
|
|
101
|
+
|
|
102
|
+
instructions_df = pd.read_excel(str(self.file_path), sheet_name="DR Sheet", nrows=20)
|
|
103
|
+
plan_df = copy.deepcopy(instructions_df)
|
|
104
|
+
csp_df = copy.deepcopy(instructions_df)
|
|
105
|
+
|
|
106
|
+
found_items = 0
|
|
107
|
+
attempts = 0
|
|
108
|
+
for index in range(len(instructions_df)):
|
|
109
|
+
attempts += 1
|
|
110
|
+
row_headers = [str(val).strip().lower() for val in instructions_df.iloc[index].values]
|
|
111
|
+
if found_items == 2 or attempts > 10:
|
|
112
|
+
break
|
|
113
|
+
elif "csp name" in row_headers:
|
|
114
|
+
plan_df.columns = plan_df.iloc[index]
|
|
115
|
+
plan_df = plan_df[index + 1 :]
|
|
116
|
+
plan_df = plan_df[["CSP Name", "System Name", "Impact Level", "DR Submission Date"]]
|
|
117
|
+
found_items += 1
|
|
118
|
+
elif "name" in row_headers:
|
|
119
|
+
csp_df.columns = csp_df.iloc[index]
|
|
120
|
+
csp_df = csp_df[index + 1 :]
|
|
121
|
+
csp_df = csp_df[["Name", "Title", "Phone", "Email"]]
|
|
122
|
+
found_items += 1
|
|
123
|
+
try:
|
|
124
|
+
data = plan_df.to_dict(orient="records")[0] | csp_df.to_dict(orient="records")[0]
|
|
125
|
+
except (IndexError, TypeError, AttributeError, KeyError):
|
|
126
|
+
data = {}
|
|
127
|
+
return data
|
|
128
|
+
|
|
129
|
+
def import_drf(self) -> Workbook:
|
|
130
|
+
"""
|
|
131
|
+
Import the DRF
|
|
132
|
+
|
|
133
|
+
:rtype: Workbook
|
|
134
|
+
:return: The workbook
|
|
135
|
+
"""
|
|
136
|
+
workbook, drf_sheets = self.pull()
|
|
137
|
+
with create_progress_object() as progress:
|
|
138
|
+
for sheet in drf_sheets:
|
|
139
|
+
ws = workbook[sheet]
|
|
140
|
+
self.metadata = self.gen_metadata()
|
|
141
|
+
self.parse_sheet_and_generate_deviations(ws, sheet, progress)
|
|
142
|
+
self.save_deviations(progress)
|
|
143
|
+
return workbook
|
|
144
|
+
|
|
145
|
+
def parse_sheet_and_generate_deviations(self, ws: worksheet, sheet: str, progress: Progress):
|
|
146
|
+
"""
|
|
147
|
+
Parse the sheet and generate deviations
|
|
148
|
+
|
|
149
|
+
:param worksheet ws: The worksheet
|
|
150
|
+
:param str sheet: The sheet name
|
|
151
|
+
:param Progress progress: The progress object
|
|
152
|
+
"""
|
|
153
|
+
parsing_drfs = progress.add_task(f"[#ef5d23]Parsing '{sheet}' sheet for DRFs...", total=ws.max_row)
|
|
154
|
+
columns = []
|
|
155
|
+
found_columns = False
|
|
156
|
+
row_number = 0
|
|
157
|
+
min_row = 0
|
|
158
|
+
for row_number in range(ws.max_row + 1):
|
|
159
|
+
try:
|
|
160
|
+
columns = [(cell.value.replace("\n", " ")).strip() for cell in ws[row_number] if cell.value]
|
|
161
|
+
except (AttributeError, IndexError, TypeError):
|
|
162
|
+
continue
|
|
163
|
+
if DR_NUMBER in columns:
|
|
164
|
+
found_columns = True
|
|
165
|
+
break
|
|
166
|
+
if not found_columns:
|
|
167
|
+
self.logger.error("Unable to find DR Number column in sheet %s... Skipping sheet.", sheet)
|
|
168
|
+
return
|
|
169
|
+
for index, row in enumerate(ws.iter_rows(min_row=row_number + 1, max_row=ws.max_row, values_only=True)):
|
|
170
|
+
if row[0] and isinstance(row[0], str) and row[0].upper().startswith("DR-"):
|
|
171
|
+
drf = self.gen_drf_from_row(columns=columns, row=row, index=index + min_row, sheet=sheet)
|
|
172
|
+
if drf:
|
|
173
|
+
self.drf_data.append(drf)
|
|
174
|
+
else:
|
|
175
|
+
self.missed_drf_data.append(row)
|
|
176
|
+
progress.update(parsing_drfs, advance=1)
|
|
177
|
+
self.logger.info("Found %s Deviations ready to create or update", len(self.drf_data))
|
|
178
|
+
|
|
179
|
+
def save_deviations(self, progress: Progress) -> None:
|
|
180
|
+
"""
|
|
181
|
+
Save the deviations to RegScale
|
|
182
|
+
|
|
183
|
+
:param Progress progress: The progress object
|
|
184
|
+
:rtype: None
|
|
185
|
+
"""
|
|
186
|
+
saving_drfs = progress.add_task(
|
|
187
|
+
"[#D9F837]Creating or Updating Deviations in RegScale...", total=len(self.drf_data)
|
|
188
|
+
)
|
|
189
|
+
for dev in self.drf_data:
|
|
190
|
+
if dev.extra_data["dr_number"] in {ex.extra_data["dr_number"] for ex in self.existing_deviations}:
|
|
191
|
+
dev.id = [
|
|
192
|
+
item.id
|
|
193
|
+
for item in self.existing_deviations
|
|
194
|
+
if item.extra_data["dr_number"].upper() == dev.extra_data["dr_number"].upper()
|
|
195
|
+
].pop()
|
|
196
|
+
dev.save()
|
|
197
|
+
else:
|
|
198
|
+
dev.create()
|
|
199
|
+
progress.update(saving_drfs, advance=1)
|
|
200
|
+
|
|
201
|
+
def get_property(self, dr_number: str, matching_poam_id: int):
|
|
202
|
+
"""
|
|
203
|
+
Get the property and update the list
|
|
204
|
+
"""
|
|
205
|
+
prop = Property(
|
|
206
|
+
name=DR_NUMBER,
|
|
207
|
+
key="dr_number",
|
|
208
|
+
value=dr_number,
|
|
209
|
+
parentId=matching_poam_id,
|
|
210
|
+
parentModule="issues",
|
|
211
|
+
label="Deviation Request Number",
|
|
212
|
+
isPublic=True,
|
|
213
|
+
)
|
|
214
|
+
self.properties.append(prop)
|
|
215
|
+
|
|
216
|
+
def parse_cvs_score(self, score: Optional[str] = None) -> float:
|
|
217
|
+
"""
|
|
218
|
+
Function to parse the CVSS Base Score from a string from an excel workbook
|
|
219
|
+
|
|
220
|
+
:param Optional[str] score: The score to parse
|
|
221
|
+
:return: The parsed score or 0.0
|
|
222
|
+
:rtype: float
|
|
223
|
+
"""
|
|
224
|
+
import math
|
|
225
|
+
|
|
226
|
+
if not score or (isinstance(score, float) and math.isnan(score)):
|
|
227
|
+
return 0.0
|
|
228
|
+
if score.isnumeric():
|
|
229
|
+
return float(score)
|
|
230
|
+
if score.isdigit():
|
|
231
|
+
return float(score)
|
|
232
|
+
if score and isinstance(score, str):
|
|
233
|
+
try:
|
|
234
|
+
return float(score)
|
|
235
|
+
except ValueError:
|
|
236
|
+
self.logger.error("Unable to parse base score: %s", score)
|
|
237
|
+
return 0.0
|
|
238
|
+
return 0.0
|
|
239
|
+
|
|
240
|
+
def gen_drf_from_row(self, columns: list[str], row: tuple, index: int, sheet: str) -> Optional[Deviation]:
|
|
241
|
+
"""
|
|
242
|
+
Generate a Deviation from a row
|
|
243
|
+
|
|
244
|
+
:param list[str] columns: The columns
|
|
245
|
+
:param tuple row: The row
|
|
246
|
+
:param int index: The index
|
|
247
|
+
:param str sheet: The sheet
|
|
248
|
+
|
|
249
|
+
:return: The Deviation or None
|
|
250
|
+
:rtype: Optional[Deviation]
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
def get_val(index_str: str, default_val: Optional[Any] = None) -> Optional[Any]:
|
|
254
|
+
"""
|
|
255
|
+
Get the value from the row
|
|
256
|
+
|
|
257
|
+
:param str index_str: The index string
|
|
258
|
+
:param Optional[Any] default_val: The default value to use if nothing is found
|
|
259
|
+
:return: The value or None
|
|
260
|
+
:rtype: Optional[Any]
|
|
261
|
+
"""
|
|
262
|
+
index_str = (str(index_str)).strip() if index_str and isinstance(index_str, str) else ""
|
|
263
|
+
try:
|
|
264
|
+
if (dat := row[columns.index(index_str)]) is not None:
|
|
265
|
+
return str(dat)
|
|
266
|
+
except ValueError:
|
|
267
|
+
self.logger.error("Unable to find column %s in sheet %s", index_str, sheet)
|
|
268
|
+
except TypeError:
|
|
269
|
+
self.logger.error("Type Error: %s, %s", index, sheet)
|
|
270
|
+
return str(default_val) if default_val else None
|
|
271
|
+
|
|
272
|
+
# Unique Ident Coalfire
|
|
273
|
+
dr_number = get_val(DR_NUMBER).upper().strip() if get_val(DR_NUMBER) else ""
|
|
274
|
+
poam_id = get_val("POA&M ID").upper().strip() if get_val("POA&M ID") else ""
|
|
275
|
+
|
|
276
|
+
matching_poam = self.id_other_identifier_map.get(poam_id)
|
|
277
|
+
if not matching_poam:
|
|
278
|
+
self.report.append({"dr_number": dr_number, "poam_id": poam_id, "status": "Unmatched"})
|
|
279
|
+
return
|
|
280
|
+
matching_poam_id = matching_poam.get("id")
|
|
281
|
+
|
|
282
|
+
self.get_property(dr_number=dr_number, matching_poam_id=matching_poam_id)
|
|
283
|
+
|
|
284
|
+
deviation_type = Deviation.mapping().get(get_val("Type of DR"))
|
|
285
|
+
justification = get_val("Justification")
|
|
286
|
+
if deviation_type == "Risk Adjustment (RA)" and not justification:
|
|
287
|
+
justification = "Unknown Justification"
|
|
288
|
+
requested_risk_rating = (
|
|
289
|
+
get_val("Requested Risk Rating/Impact") if get_val("Requested Risk Rating/Impact") else "Low"
|
|
290
|
+
)
|
|
291
|
+
self.report.append({"dr_number": dr_number, "poam_id": poam_id, "status": "Matched"})
|
|
292
|
+
if requested_risk_rating and requested_risk_rating.lower() not in ["low", "moderate", "high"]:
|
|
293
|
+
self.logger.error("A valid Requested Risk Rating is required for %s", dr_number)
|
|
294
|
+
self.errors.append(
|
|
295
|
+
{"dr_number": dr_number, "error": f"The Requested Risk Rating {requested_risk_rating} is invalid"}
|
|
296
|
+
)
|
|
297
|
+
return
|
|
298
|
+
if not deviation_type:
|
|
299
|
+
self.logger.error("Unable to find deviation type for %s", dr_number)
|
|
300
|
+
self.errors.append({"dr_number": dr_number, "error": "Deviation Type not found"})
|
|
301
|
+
return None
|
|
302
|
+
if deviation_type == "Risk Adjustment (RA)" and not justification:
|
|
303
|
+
self.logger.error("Justification is required for RA Deviation %s", dr_number)
|
|
304
|
+
self.errors.append({"dr_number": dr_number, "error": f"Justification is required for {deviation_type}"})
|
|
305
|
+
return None
|
|
306
|
+
from regscale.core.utils.date import datetime_str
|
|
307
|
+
|
|
308
|
+
return Deviation(
|
|
309
|
+
id=0,
|
|
310
|
+
otherIdentifier=poam_id,
|
|
311
|
+
extra_data={"dr_number": dr_number},
|
|
312
|
+
baseScore=self.parse_cvs_score(get_val(CVSS_BASE_SCORE)),
|
|
313
|
+
environmentalScore=(
|
|
314
|
+
float(get_val(ADJUSTED_CVSS_SCORE))
|
|
315
|
+
if get_val(ADJUSTED_CVSS_SCORE) and str(get_val(ADJUSTED_CVSS_SCORE)).isnumeric()
|
|
316
|
+
else None
|
|
317
|
+
),
|
|
318
|
+
parentIssueId=matching_poam_id,
|
|
319
|
+
isPublic=True,
|
|
320
|
+
deviationType=deviation_type,
|
|
321
|
+
requestedImpactRating=requested_risk_rating,
|
|
322
|
+
dateSubmitted=(
|
|
323
|
+
get_val(DR_SUBMISSION_DATE).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"
|
|
324
|
+
if isinstance(get_val(DR_SUBMISSION_DATE), datetime)
|
|
325
|
+
else datetime_str(get_val(DR_SUBMISSION_DATE))
|
|
326
|
+
), # Format must be 2024-08-15T19:00:26.372Z
|
|
327
|
+
rationale=get_val("DR Rationale", ""),
|
|
328
|
+
evidenceDescription=(get_val("Evidence Description")),
|
|
329
|
+
operationalImpacts=(
|
|
330
|
+
get_val("Operational Impact Statement") if get_val("Operational Impact Statement") else "N/A"
|
|
331
|
+
),
|
|
332
|
+
riskJustification=justification,
|
|
333
|
+
tmpExploitCodeMaturity=None,
|
|
334
|
+
tmpRemediationLevel=get_val("Remediation Level"),
|
|
335
|
+
tmpReportConfidence=None,
|
|
336
|
+
envConfidentiality=get_val("Impact Metrics: Confidentiality"),
|
|
337
|
+
envIntegrity=get_val("Impact Metrics: Integrity"),
|
|
338
|
+
envAvailability=get_val("Impact Metrics: Availability"),
|
|
339
|
+
envAttackVector=get_val("Attack Vector"),
|
|
340
|
+
envAttackComplexity=get_val("Attack Complexity"),
|
|
341
|
+
envPrivilegesRequired=get_val("Privileges Required"),
|
|
342
|
+
envUserInteraction=get_val("User Interaction"),
|
|
343
|
+
envScope=None,
|
|
344
|
+
envModConfidentiality=None,
|
|
345
|
+
envModIntegrity=None,
|
|
346
|
+
envModAvailability=None,
|
|
347
|
+
vulnerabilityId=get_val("Vulnerability Name"),
|
|
348
|
+
envAttackVectorExplanation=get_val("Attack Vector Explanation"),
|
|
349
|
+
envAttackComplexityExplanation=get_val("Attack Complexity Explanation"),
|
|
350
|
+
envPrivilegesRequiredExplanation=get_val("Privileges Required Explanation"),
|
|
351
|
+
envUserInteractionExplanation=get_val("User Interaction Explanation"),
|
|
352
|
+
envConfidentialityExplanation=get_val("Impact Metrics: Confidentiality Explanation"),
|
|
353
|
+
envIntegrityExplanation=get_val("Impact Metrics: Integrity Explanation"),
|
|
354
|
+
envAvailabilityExplanation=get_val("Impact Metrics: Availability Explanation"),
|
|
355
|
+
tmpExploitCodeMaturityExplanation=None,
|
|
356
|
+
tmpRemediationLevelExplanation=get_val("Remediation Level Explanation"),
|
|
357
|
+
tmpReportConfidenceExplanation=None,
|
|
358
|
+
baseSeverity=get_val("Initial Risk Rating"),
|
|
359
|
+
temporalSeverity=None,
|
|
360
|
+
environmentalSeverity=None,
|
|
361
|
+
finalVectorString=None,
|
|
362
|
+
overallRiskReductionExplanation=get_val("List of Risk Reduction"),
|
|
363
|
+
evidenceAttachments=get_val("List of Evidence Attachments")
|
|
364
|
+
or get_val("List of Operational Requirement Attachments"),
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def get_id_map(self) -> dict:
|
|
368
|
+
"""
|
|
369
|
+
Get the ID map
|
|
370
|
+
|
|
371
|
+
:return: The ID map
|
|
372
|
+
:rtype: dict
|
|
373
|
+
"""
|
|
374
|
+
self.logger.info(f"Fetching all issues for {self.module} #{self.module_id}...")
|
|
375
|
+
id_map = {}
|
|
376
|
+
all_issues = Issue.get_all_by_parent(parent_id=self.module_id, parent_module=self.module)
|
|
377
|
+
self.logger.info("Fetched %s issue(s) from RegScale.", len(all_issues))
|
|
378
|
+
|
|
379
|
+
for issue in track(all_issues, description="Building id-otherIdentifier lookup..."):
|
|
380
|
+
if issue.otherIdentifier:
|
|
381
|
+
ident = issue.otherIdentifier.upper()
|
|
382
|
+
id_map[ident] = {"id": issue.id, "dr_number": None, "prop_id": None}
|
|
383
|
+
|
|
384
|
+
properties = Property.get_all_by_parent(parent_id=issue.id, parent_module="issues")
|
|
385
|
+
dr_number_property = next((prop for prop in properties if prop.key == "dr_number"), None)
|
|
386
|
+
if dr_number_property:
|
|
387
|
+
id_map[ident]["dr_number"] = dr_number_property.value.upper()
|
|
388
|
+
id_map[ident]["prop_id"] = dr_number_property.id
|
|
389
|
+
self.logger.info(
|
|
390
|
+
"Constructed a map of %s issues with POAM id (otherIdentifier) and a nested dictionary of dr_number, issue_key, and property_key",
|
|
391
|
+
len(id_map),
|
|
392
|
+
)
|
|
393
|
+
return id_map
|
|
394
|
+
|
|
395
|
+
def export_report(self):
|
|
396
|
+
"""Save a Report of missing POAMs to a file"""
|
|
397
|
+
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
|
398
|
+
report_file = self.file_path.parent / f"DRF-Import-Report_{timestamp}.csv"
|
|
399
|
+
with open(report_file, "w") as file:
|
|
400
|
+
file.write("DR Number,POAM ID,Status,Errors\n")
|
|
401
|
+
for item in self.report:
|
|
402
|
+
item["errors"] = []
|
|
403
|
+
errors = [er for er in self.errors if er["dr_number"] == item["dr_number"]]
|
|
404
|
+
if errors:
|
|
405
|
+
for error in errors:
|
|
406
|
+
item["status"] = "Error"
|
|
407
|
+
item["errors"].append(error)
|
|
408
|
+
if item["status"] in ["Unmatched", "Error"]:
|
|
409
|
+
file.write(f"{item['dr_number']},{item['poam_id']},{item['status']},{item['errors']}\n")
|
|
410
|
+
self.logger.info("Mismatched POAM Report saved to %s", report_file)
|
|
411
|
+
|
|
412
|
+
def get_update_properties(self) -> list[Property]:
|
|
413
|
+
"""
|
|
414
|
+
Get the properties to update
|
|
415
|
+
|
|
416
|
+
:return: The properties to update
|
|
417
|
+
:rtype: list[Property]
|
|
418
|
+
"""
|
|
419
|
+
# Filter properties that have a corresponding 'dr_number' and 'prop_id' in 'id_other_identifier_map'
|
|
420
|
+
props = [
|
|
421
|
+
prop
|
|
422
|
+
for prop in self.properties
|
|
423
|
+
if prop.value
|
|
424
|
+
in {
|
|
425
|
+
val.get("dr_number")
|
|
426
|
+
for val in self.id_other_identifier_map.values()
|
|
427
|
+
if val.get("dr_number") and val.get("prop_id")
|
|
428
|
+
}
|
|
429
|
+
]
|
|
430
|
+
|
|
431
|
+
# Iterate over the filtered properties
|
|
432
|
+
for prop in props:
|
|
433
|
+
# Find the matching value in 'id_other_identifier_map' where 'dr_number' equals the property value
|
|
434
|
+
match = next(
|
|
435
|
+
(val for val in self.id_other_identifier_map.values() if val.get("dr_number") == prop.value), None
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
# If a match is found, update the property id
|
|
439
|
+
if match:
|
|
440
|
+
prop.id = match.get("prop_id")
|
|
441
|
+
|
|
442
|
+
# Return the list of properties that have an id
|
|
443
|
+
return [prop for prop in props if prop.id]
|
|
File without changes
|