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,583 @@
|
|
|
1
|
+
"""Ticketing Connector Model"""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
from typing import Optional, TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from synqly.engine.resources.ticketing.types.ticket import Ticket
|
|
10
|
+
|
|
11
|
+
import rich.progress
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from pydantic import ConfigDict
|
|
14
|
+
|
|
15
|
+
from regscale.core.app.utils.app_utils import (
|
|
16
|
+
check_file_path,
|
|
17
|
+
compute_hashes_in_directory,
|
|
18
|
+
get_current_datetime,
|
|
19
|
+
)
|
|
20
|
+
from regscale.models.integration_models.synqly_models.synqly_model import SynqlyModel
|
|
21
|
+
from regscale.models.regscale_models import File, Issue
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Ticketing(SynqlyModel):
|
|
25
|
+
"""Ticketing Connector Model"""
|
|
26
|
+
|
|
27
|
+
model_config = ConfigDict(populate_by_name=True, use_enum_values=True, arbitrary_types_allowed=True)
|
|
28
|
+
|
|
29
|
+
integration_id: str = ""
|
|
30
|
+
integration_id_field: str = ""
|
|
31
|
+
has_integration_field: bool = False
|
|
32
|
+
manage_attachments: bool = False
|
|
33
|
+
|
|
34
|
+
def __init__(self, integration: str, **kwargs: dict):
|
|
35
|
+
super().__init__(connector_type=self.__class__.__name__, integration=integration, **kwargs)
|
|
36
|
+
self.integration_id = f"{self._connector_type.lower()}_{self.integration.lower()}"
|
|
37
|
+
self.integration_id_field = f"{self.integration[0:1].lower()}{self.integration[1:]}Id"
|
|
38
|
+
self.has_integration_field = hasattr(Issue(), self.integration_id_field)
|
|
39
|
+
self.manage_attachments = (
|
|
40
|
+
"create_attachment" in self.capabilities and "download_attachment" in self.capabilities
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def integration_sync(self, regscale_id: int, regscale_module: str, sync_attachments: bool = True, **kwargs) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Runs the integration sync process
|
|
46
|
+
|
|
47
|
+
:param int regscale_id: RegScale record ID Number to sync issues to
|
|
48
|
+
:param str regscale_module: RegScale module to sync issues to
|
|
49
|
+
:param bool sync_attachments: Whether to sync attachments or not, defaults to True
|
|
50
|
+
:rtype: None
|
|
51
|
+
"""
|
|
52
|
+
sync_attachments = sync_attachments and self.manage_attachments
|
|
53
|
+
self.logger.info(f"Fetching tickets from {self.integration}...")
|
|
54
|
+
if project := kwargs.get("project"):
|
|
55
|
+
query_filter = f"project[eq]{project}"
|
|
56
|
+
elif default_project := kwargs.get("default_project"):
|
|
57
|
+
query_filter = f"project[eq]{default_project}"
|
|
58
|
+
else:
|
|
59
|
+
query_filter = None
|
|
60
|
+
kwargs["filter"] = query_filter
|
|
61
|
+
integration_issues = self.fetch_integration_data(
|
|
62
|
+
func=self.tenant.engine_client.ticketing.query_tickets, **kwargs
|
|
63
|
+
)
|
|
64
|
+
self.logger.info(f"Found {len(integration_issues)} ticket(s) in {self.integration}")
|
|
65
|
+
self.logger.info("Fetching issues from RegScale...")
|
|
66
|
+
(
|
|
67
|
+
regscale_issues,
|
|
68
|
+
regscale_attachments,
|
|
69
|
+
) = Issue.fetch_issues_and_attachments_by_parent(
|
|
70
|
+
parent_id=regscale_id,
|
|
71
|
+
parent_module=regscale_module,
|
|
72
|
+
fetch_attachments=sync_attachments,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
self.process_regscale_issues(
|
|
76
|
+
regscale_issues=regscale_issues,
|
|
77
|
+
regscale_attachments=regscale_attachments,
|
|
78
|
+
sync_attachments=sync_attachments,
|
|
79
|
+
**kwargs,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
self.process_integration_issues(
|
|
83
|
+
integration_issues=integration_issues,
|
|
84
|
+
regscale_issues=regscale_issues,
|
|
85
|
+
regscale_id=regscale_id,
|
|
86
|
+
regscale_module=regscale_module,
|
|
87
|
+
sync_attachments=sync_attachments,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
def run_sync(self, *args, **kwargs) -> None:
|
|
91
|
+
"""
|
|
92
|
+
Syncs RegScale issues with Ticketing connector using Synqly
|
|
93
|
+
|
|
94
|
+
:rtype: None
|
|
95
|
+
"""
|
|
96
|
+
self.run_integration_sync(
|
|
97
|
+
*args,
|
|
98
|
+
**kwargs,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def download_issue_attachments_to_directory(
|
|
102
|
+
self,
|
|
103
|
+
directory: str,
|
|
104
|
+
integration_issue: "Ticket",
|
|
105
|
+
regscale_issue: Issue,
|
|
106
|
+
) -> tuple[str, str]:
|
|
107
|
+
"""
|
|
108
|
+
Function to download attachments from the integration via Synqly and RegScale issues to a directory
|
|
109
|
+
|
|
110
|
+
:param str directory: Directory to store the files in
|
|
111
|
+
:param Ticket integration_issue: Issue to download the attachments for
|
|
112
|
+
:param Issue regscale_issue: RegScale issue to download the attachments for
|
|
113
|
+
:return: Tuple of strings containing the Integration and RegScale directories
|
|
114
|
+
:rtype: tuple[str, str]
|
|
115
|
+
"""
|
|
116
|
+
# determine which attachments need to be uploaded to prevent duplicates by checking hashes
|
|
117
|
+
synqly_dir = os.path.join(directory, self.integration)
|
|
118
|
+
check_file_path(synqly_dir, False)
|
|
119
|
+
# download all attachments from Integration via Synqly to the synqly directory in temp_dir
|
|
120
|
+
self.download_attachments(ticket_id=integration_issue.id, download_dir=synqly_dir)
|
|
121
|
+
# get the regscale issue attachments
|
|
122
|
+
regscale_issue_attachments = File.get_files_for_parent_from_regscale(
|
|
123
|
+
api=self.api,
|
|
124
|
+
parent_id=regscale_issue.id,
|
|
125
|
+
parent_module="issues",
|
|
126
|
+
)
|
|
127
|
+
# create a directory for the regscale attachments
|
|
128
|
+
regscale_dir = os.path.join(directory, "regscale")
|
|
129
|
+
check_file_path(regscale_dir, False)
|
|
130
|
+
# download regscale attachments to the directory
|
|
131
|
+
for attachment in regscale_issue_attachments:
|
|
132
|
+
with open(os.path.join(regscale_dir, attachment.trustedDisplayName), "wb") as file:
|
|
133
|
+
file.write(
|
|
134
|
+
File.download_file_from_regscale_to_memory(
|
|
135
|
+
api=self.api,
|
|
136
|
+
record_id=regscale_issue.id,
|
|
137
|
+
module="issues",
|
|
138
|
+
stored_name=attachment.trustedStorageName,
|
|
139
|
+
file_hash=(attachment.fileHash if attachment.fileHash else attachment.shaHash),
|
|
140
|
+
)
|
|
141
|
+
)
|
|
142
|
+
return synqly_dir, regscale_dir
|
|
143
|
+
|
|
144
|
+
def download_attachments(self, ticket_id: str, download_dir: str) -> int:
|
|
145
|
+
"""
|
|
146
|
+
Downloads attachments from a ticket via Synqly
|
|
147
|
+
|
|
148
|
+
:param str ticket_id: Ticket ID to download attachments from
|
|
149
|
+
:param str download_dir: Directory to download attachments to
|
|
150
|
+
:return: # of Synqly attachments downloaded
|
|
151
|
+
:rtype: int
|
|
152
|
+
"""
|
|
153
|
+
attachments = self.client.ticketing.list_attachments_metadata(ticket_id)
|
|
154
|
+
self.logger.debug("Found %i attachments for ticket %s", len(attachments.result), ticket_id)
|
|
155
|
+
for attachment in attachments.result:
|
|
156
|
+
download_response = self.client.ticketing.download_attachment(
|
|
157
|
+
ticket_id=ticket_id, attachment_id=attachment.id
|
|
158
|
+
)
|
|
159
|
+
output_path = os.path.join(download_dir, attachment.file_name)
|
|
160
|
+
with open(output_path, "wb") as f:
|
|
161
|
+
f.write(base64.b64decode(download_response.result.content))
|
|
162
|
+
self.logger.debug(
|
|
163
|
+
"Downloaded attachment: %s and wrote its contents to %s",
|
|
164
|
+
download_response.result.file_name,
|
|
165
|
+
attachment.file_name,
|
|
166
|
+
)
|
|
167
|
+
return len(attachments.result)
|
|
168
|
+
|
|
169
|
+
def upload_synqly_attachments(self, ticket_id: str, file_path: Path) -> None:
|
|
170
|
+
"""
|
|
171
|
+
Uploads an attachment to a ticket via Synqly
|
|
172
|
+
|
|
173
|
+
:param str ticket_id: Ticket ID to attach the file to
|
|
174
|
+
:param Path file_path: Path to the file to attach
|
|
175
|
+
:rtype: None
|
|
176
|
+
"""
|
|
177
|
+
from synqly import engine
|
|
178
|
+
|
|
179
|
+
with open(file_path.absolute(), "rb") as file:
|
|
180
|
+
content = base64.b64encode(file.read()) # type: ignore
|
|
181
|
+
self.logger.debug("Creating attachment for ticket %s", ticket_id)
|
|
182
|
+
self.client.ticketing.create_attachment(
|
|
183
|
+
ticket_id=ticket_id,
|
|
184
|
+
request=engine.CreateAttachmentRequest(
|
|
185
|
+
file_name=file_path.name,
|
|
186
|
+
content=content, # type: ignore
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
self.logger.info("Added an attachment to %s ticket %s", self.integration, ticket_id)
|
|
190
|
+
|
|
191
|
+
def compare_files_for_dupes_and_upload(self, connector_issue: "Ticket", regscale_issue: Issue) -> None:
|
|
192
|
+
"""
|
|
193
|
+
Compare attachments for provided Integration and RegScale issues via hash to prevent duplicates
|
|
194
|
+
|
|
195
|
+
:param Ticket connector_issue: Connector issue object to compare attachments from
|
|
196
|
+
:param Issue regscale_issue: RegScale issue object to compare attachments from
|
|
197
|
+
:rtype: None
|
|
198
|
+
"""
|
|
199
|
+
connector_uploaded_attachments = []
|
|
200
|
+
regscale_uploaded_attachments = []
|
|
201
|
+
# create a temporary directory to store the downloaded attachments from desired connector and RegScale
|
|
202
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
203
|
+
# write attachments to the temporary directory
|
|
204
|
+
connector_dir, regscale_dir = self.download_issue_attachments_to_directory(
|
|
205
|
+
directory=temp_dir,
|
|
206
|
+
integration_issue=connector_issue,
|
|
207
|
+
regscale_issue=regscale_issue,
|
|
208
|
+
)
|
|
209
|
+
# get the hashes for the attachments in the RegScale and connector directories
|
|
210
|
+
# iterate all files in the connector directory and compute their hashes
|
|
211
|
+
ticket_attachment_hashes = compute_hashes_in_directory(connector_dir)
|
|
212
|
+
regscale_attachment_hashes = compute_hashes_in_directory(regscale_dir)
|
|
213
|
+
|
|
214
|
+
# check where the files need to be uploaded to before uploading
|
|
215
|
+
for file_hash, file in regscale_attachment_hashes.items():
|
|
216
|
+
if file_hash not in ticket_attachment_hashes:
|
|
217
|
+
try:
|
|
218
|
+
self.upload_synqly_attachments(
|
|
219
|
+
ticket_id=connector_issue.id,
|
|
220
|
+
file_path=Path(file),
|
|
221
|
+
)
|
|
222
|
+
connector_uploaded_attachments.append(file)
|
|
223
|
+
except TypeError as ex:
|
|
224
|
+
self.logger.error(
|
|
225
|
+
"Unable to upload %s to %s issue %s.\nError: %s",
|
|
226
|
+
Path(file).name,
|
|
227
|
+
self.integration,
|
|
228
|
+
connector_issue.id,
|
|
229
|
+
ex,
|
|
230
|
+
)
|
|
231
|
+
for file_hash, file in ticket_attachment_hashes.items():
|
|
232
|
+
if file_hash not in regscale_attachment_hashes:
|
|
233
|
+
with open(file, "rb") as in_file:
|
|
234
|
+
if File.upload_file_to_regscale(
|
|
235
|
+
file_name=f"{self.integration}_attachment_{Path(file).name}",
|
|
236
|
+
parent_id=regscale_issue.id,
|
|
237
|
+
parent_module="issues",
|
|
238
|
+
api=self.api,
|
|
239
|
+
file_data=in_file.read(),
|
|
240
|
+
):
|
|
241
|
+
regscale_uploaded_attachments.append(file)
|
|
242
|
+
self.logger.debug(
|
|
243
|
+
"Uploaded %s to RegScale issue #%i.",
|
|
244
|
+
Path(file).name,
|
|
245
|
+
regscale_issue.id,
|
|
246
|
+
)
|
|
247
|
+
else:
|
|
248
|
+
self.logger.warning(
|
|
249
|
+
"Unable to upload %s to RegScale issue #%i.",
|
|
250
|
+
Path(file).name,
|
|
251
|
+
regscale_issue.id,
|
|
252
|
+
)
|
|
253
|
+
self.log_upload_outcome(
|
|
254
|
+
regscale_uploads=regscale_uploaded_attachments,
|
|
255
|
+
connector_uploads=connector_uploaded_attachments,
|
|
256
|
+
regscale_issue=regscale_issue,
|
|
257
|
+
connector_issue=connector_issue,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def log_upload_outcome(
|
|
261
|
+
self, regscale_uploads: list, connector_uploads: list, regscale_issue: Issue, connector_issue: "Ticket"
|
|
262
|
+
) -> None:
|
|
263
|
+
"""
|
|
264
|
+
Log the outcome of the attachment uploads
|
|
265
|
+
|
|
266
|
+
:param list regscale_uploads: List of RegScale attachments uploaded
|
|
267
|
+
:param list connector_uploads: List of Connector attachments uploaded
|
|
268
|
+
:param Issue regscale_issue: RegScale issue object
|
|
269
|
+
:param Ticket connector_issue: Connector issue object
|
|
270
|
+
:rtype: None
|
|
271
|
+
"""
|
|
272
|
+
if regscale_uploads and connector_uploads:
|
|
273
|
+
self.logger.info(
|
|
274
|
+
"%i file(s) uploaded to RegScale issue #%i and %i file(s) uploaded to %s ticket %s.",
|
|
275
|
+
len(regscale_uploads),
|
|
276
|
+
regscale_issue.id,
|
|
277
|
+
len(connector_uploads),
|
|
278
|
+
self.integration,
|
|
279
|
+
connector_issue.id,
|
|
280
|
+
)
|
|
281
|
+
elif connector_uploads:
|
|
282
|
+
self.logger.info(
|
|
283
|
+
"%i file(s) uploaded to %s ticket %s.",
|
|
284
|
+
len(connector_uploads),
|
|
285
|
+
self.integration,
|
|
286
|
+
connector_issue.id,
|
|
287
|
+
)
|
|
288
|
+
elif regscale_uploads:
|
|
289
|
+
self.logger.info(
|
|
290
|
+
"%i file(s) uploaded to RegScale issue #%i.",
|
|
291
|
+
len(regscale_uploads),
|
|
292
|
+
regscale_issue.id,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
def process_regscale_issues(
|
|
296
|
+
self,
|
|
297
|
+
regscale_issues: list[Issue],
|
|
298
|
+
regscale_attachments: Optional[dict] = None,
|
|
299
|
+
sync_attachments: bool = True,
|
|
300
|
+
**kwargs,
|
|
301
|
+
) -> None:
|
|
302
|
+
"""
|
|
303
|
+
Process RegScale issues and sync them to the Integration
|
|
304
|
+
|
|
305
|
+
:param list[Issue] regscale_issues: List of RegScale issues to process
|
|
306
|
+
:param dict regscale_attachments: Dictionary of RegScale attachments
|
|
307
|
+
:param bool sync_attachments: Flag to determine if attachments should be synced, defaults to True
|
|
308
|
+
:rtype: None
|
|
309
|
+
"""
|
|
310
|
+
if regscale_issues:
|
|
311
|
+
# sync RegScale issues to Integration
|
|
312
|
+
self.sync_regscale_to_integration(
|
|
313
|
+
regscale_issues=regscale_issues,
|
|
314
|
+
sync_attachments=sync_attachments,
|
|
315
|
+
attachments=regscale_attachments,
|
|
316
|
+
**kwargs,
|
|
317
|
+
)
|
|
318
|
+
if self.regscale_objects_to_update:
|
|
319
|
+
upd_count = len(self.regscale_objects_to_update)
|
|
320
|
+
with self.job_progress as job_progress:
|
|
321
|
+
# create task to update RegScale issues
|
|
322
|
+
updating_issues = job_progress.add_task(
|
|
323
|
+
f"[#f8b737]Updating {upd_count} RegScale issue(s) from {self.integration}...",
|
|
324
|
+
total=upd_count,
|
|
325
|
+
)
|
|
326
|
+
# create threads to analyze Jira issues and RegScale issues
|
|
327
|
+
self.app.thread_manager.submit_tasks_from_list(
|
|
328
|
+
self.update_regscale_issues,
|
|
329
|
+
self.regscale_objects_to_update,
|
|
330
|
+
(updating_issues), # noqa
|
|
331
|
+
)
|
|
332
|
+
self.app.thread_manager.execute_and_verify()
|
|
333
|
+
self.logger.info(
|
|
334
|
+
"%i/%i issue(s) updated in RegScale.",
|
|
335
|
+
len(self.updated_regscale_objects),
|
|
336
|
+
upd_count,
|
|
337
|
+
)
|
|
338
|
+
else:
|
|
339
|
+
self.logger.info("No issues need to be updated in RegScale.")
|
|
340
|
+
|
|
341
|
+
def process_integration_issues(
|
|
342
|
+
self,
|
|
343
|
+
integration_issues: list["Ticket"],
|
|
344
|
+
regscale_issues: list[Issue],
|
|
345
|
+
regscale_id: int,
|
|
346
|
+
regscale_module: str,
|
|
347
|
+
sync_attachments: bool = True,
|
|
348
|
+
) -> None:
|
|
349
|
+
"""
|
|
350
|
+
Process Integration issues and sync them to RegScale
|
|
351
|
+
|
|
352
|
+
:param list[Ticket] integration_issues: List of Integration issues to process
|
|
353
|
+
:param list[Issue] regscale_issues: List of RegScale issues to process
|
|
354
|
+
:param int regscale_id: RegScale record ID Number to sync issues to
|
|
355
|
+
:param str regscale_module: RegScale module to sync issues to
|
|
356
|
+
:param bool sync_attachments: Flag to determine if attachments should be synced, defaults to True
|
|
357
|
+
:rtype: None
|
|
358
|
+
"""
|
|
359
|
+
if integration_issues:
|
|
360
|
+
# sync integration issues to RegScale
|
|
361
|
+
with self.job_progress as job_progress:
|
|
362
|
+
# create task to create RegScale issues
|
|
363
|
+
creating_issues = job_progress.add_task(
|
|
364
|
+
f"[#f8b737]Analyzing {len(integration_issues)} {self.integration} ticket(s)"
|
|
365
|
+
f" and {len(regscale_issues)} RegScale issue(s)...",
|
|
366
|
+
total=len(integration_issues),
|
|
367
|
+
)
|
|
368
|
+
# create threads to analyze Jira issues and RegScale issues
|
|
369
|
+
self.app.thread_manager.submit_tasks_from_list(
|
|
370
|
+
self.create_and_update_regscale_issues,
|
|
371
|
+
integration_issues,
|
|
372
|
+
(
|
|
373
|
+
regscale_issues,
|
|
374
|
+
sync_attachments,
|
|
375
|
+
regscale_id,
|
|
376
|
+
regscale_module,
|
|
377
|
+
creating_issues,
|
|
378
|
+
),
|
|
379
|
+
)
|
|
380
|
+
self.app.thread_manager.execute_and_verify()
|
|
381
|
+
self.logger.info(
|
|
382
|
+
"Analyzed %i %s ticket(s), created %i issue(s) and updated %i issue(s) in RegScale.",
|
|
383
|
+
len(integration_issues),
|
|
384
|
+
self.integration,
|
|
385
|
+
len(self.created_regscale_objects),
|
|
386
|
+
len(self.updated_regscale_objects),
|
|
387
|
+
)
|
|
388
|
+
else:
|
|
389
|
+
self.logger.info(f"No tickets need to be analyzed from {self.integration}.")
|
|
390
|
+
|
|
391
|
+
def create_and_update_regscale_issues(self, *args, **kwargs) -> None:
|
|
392
|
+
"""
|
|
393
|
+
Function to create or update issues in RegScale from Jira
|
|
394
|
+
|
|
395
|
+
:rtype: None
|
|
396
|
+
"""
|
|
397
|
+
# set up local variables from the passed args
|
|
398
|
+
integration_issue: "Ticket" = args[0] # type: ignore
|
|
399
|
+
(
|
|
400
|
+
regscale_issues,
|
|
401
|
+
add_attachments,
|
|
402
|
+
parent_id,
|
|
403
|
+
parent_module,
|
|
404
|
+
task,
|
|
405
|
+
) = args[
|
|
406
|
+
1 # type: ignore
|
|
407
|
+
]
|
|
408
|
+
if self.has_integration_field:
|
|
409
|
+
regscale_issue: Optional[Issue] = next(
|
|
410
|
+
(
|
|
411
|
+
issue
|
|
412
|
+
for issue in regscale_issues
|
|
413
|
+
if getattr(issue, self.integration_id_field) == integration_issue.id
|
|
414
|
+
),
|
|
415
|
+
None,
|
|
416
|
+
)
|
|
417
|
+
else:
|
|
418
|
+
# use the manualDetectionSource and manualDetectionId fields
|
|
419
|
+
regscale_issue: Optional[Issue] = next(
|
|
420
|
+
(
|
|
421
|
+
issue
|
|
422
|
+
for issue in regscale_issues
|
|
423
|
+
if issue.manualDetectionSource == self.integration
|
|
424
|
+
and issue.manualDetectionId == integration_issue.id
|
|
425
|
+
),
|
|
426
|
+
None,
|
|
427
|
+
)
|
|
428
|
+
# see if the Jira issue needs to be created in RegScale
|
|
429
|
+
if integration_issue.status.lower() == "done" and regscale_issue:
|
|
430
|
+
# update the status and date completed of the RegScale issue
|
|
431
|
+
regscale_issue.status = "Closed"
|
|
432
|
+
regscale_issue.dateCompleted = get_current_datetime()
|
|
433
|
+
# update the issue in RegScale
|
|
434
|
+
self.updated_regscale_objects.append(regscale_issue.save())
|
|
435
|
+
elif regscale_issue:
|
|
436
|
+
# update the issue in RegScale
|
|
437
|
+
self.updated_regscale_objects.append(regscale_issue.save())
|
|
438
|
+
else:
|
|
439
|
+
# map the jira issue to a RegScale issue object
|
|
440
|
+
issue = self.mapper.to_regscale(
|
|
441
|
+
ocsf_object=integration_issue,
|
|
442
|
+
connector=self,
|
|
443
|
+
config=self.app.config,
|
|
444
|
+
parent_id=parent_id,
|
|
445
|
+
parent_module=parent_module,
|
|
446
|
+
**kwargs,
|
|
447
|
+
)
|
|
448
|
+
# create the issue in RegScale
|
|
449
|
+
if regscale_issue := issue.create():
|
|
450
|
+
self.logger.debug(
|
|
451
|
+
"Created issue #%i-%s in RegScale.",
|
|
452
|
+
regscale_issue.id,
|
|
453
|
+
regscale_issue.title,
|
|
454
|
+
)
|
|
455
|
+
self.created_regscale_objects.append(regscale_issue)
|
|
456
|
+
else:
|
|
457
|
+
self.logger.warning("Unable to create issue in RegScale.\nIssue: %s", issue.dict())
|
|
458
|
+
if add_attachments and regscale_issue:
|
|
459
|
+
# check if the integration issue has attachments
|
|
460
|
+
attachment_count = self.get_integration_attachment_count(integration_issue.id)
|
|
461
|
+
if attachment_count > 0:
|
|
462
|
+
# determine which attachments need to be uploaded to prevent duplicates by
|
|
463
|
+
# getting the hashes of all Jira & RegScale attachments
|
|
464
|
+
self.compare_files_for_dupes_and_upload(
|
|
465
|
+
connector_issue=integration_issue,
|
|
466
|
+
regscale_issue=regscale_issue,
|
|
467
|
+
)
|
|
468
|
+
# update progress bar
|
|
469
|
+
self.job_progress.update(task, advance=1)
|
|
470
|
+
|
|
471
|
+
def get_integration_attachment_count(self, ticket_id: str) -> int:
|
|
472
|
+
"""
|
|
473
|
+
Get the number of attachments for a ticket in Synqly
|
|
474
|
+
|
|
475
|
+
:param str ticket_id: Ticket ID to get the attachments for
|
|
476
|
+
:return: Number of attachments for the ticket
|
|
477
|
+
:rtype: int
|
|
478
|
+
"""
|
|
479
|
+
try:
|
|
480
|
+
attachments = self.client.ticketing.list_attachments_metadata(ticket_id)
|
|
481
|
+
return len(attachments.result)
|
|
482
|
+
except Exception as ex:
|
|
483
|
+
self.logger.error(f"Unable to get attachments for ticket {ticket_id}.\nError: {ex}")
|
|
484
|
+
return 0
|
|
485
|
+
|
|
486
|
+
def update_regscale_issues(self, *args) -> None:
|
|
487
|
+
"""
|
|
488
|
+
Function to compare Integration issues and RegScale issues
|
|
489
|
+
|
|
490
|
+
:rtype: None
|
|
491
|
+
"""
|
|
492
|
+
# set up local variables from the passed args
|
|
493
|
+
regscale_issue: Issue = args[0] # type: ignore
|
|
494
|
+
task: rich.progress.TaskID = args[1] # type: ignore
|
|
495
|
+
# update the issue in RegScale
|
|
496
|
+
regscale_issue.save()
|
|
497
|
+
self.logger.info(
|
|
498
|
+
"RegScale Issue %i was updated with the %s link.",
|
|
499
|
+
regscale_issue.id,
|
|
500
|
+
self.integration.title(),
|
|
501
|
+
)
|
|
502
|
+
self.updated_regscale_objects.append(regscale_issue)
|
|
503
|
+
# update progress bar
|
|
504
|
+
self.job_progress.update(task, advance=1)
|
|
505
|
+
|
|
506
|
+
def create_issue_in_integration(
|
|
507
|
+
self,
|
|
508
|
+
issue: Issue,
|
|
509
|
+
add_attachments: Optional[bool] = False,
|
|
510
|
+
attachments: list[File] = None,
|
|
511
|
+
**kwargs,
|
|
512
|
+
) -> Optional["Ticket"]:
|
|
513
|
+
"""
|
|
514
|
+
Create a new issue in the integration
|
|
515
|
+
|
|
516
|
+
:param Issue issue: RegScale issue object
|
|
517
|
+
:param Optional[bool] add_attachments: Flag to determine if attachments should be added to the issue
|
|
518
|
+
:param list[File] attachments: List of attachments to add to the issue
|
|
519
|
+
:return: Newly created issue in Jira
|
|
520
|
+
:rtype: Optional[Ticket]
|
|
521
|
+
"""
|
|
522
|
+
try:
|
|
523
|
+
new_issue = self.mapper.to_ocsf(issue, **kwargs)
|
|
524
|
+
create_response = self.client.ticketing.create_ticket(request=new_issue)
|
|
525
|
+
except Exception as ex:
|
|
526
|
+
self.logger.error(f"Unable to create {self.integration} ticket.\nError: {ex}")
|
|
527
|
+
return None
|
|
528
|
+
if add_attachments and attachments:
|
|
529
|
+
self.compare_files_for_dupes_and_upload(
|
|
530
|
+
connector_issue=create_response.result,
|
|
531
|
+
regscale_issue=issue,
|
|
532
|
+
)
|
|
533
|
+
self.logger.info("Created ticket: {}".format(create_response.result.name))
|
|
534
|
+
return create_response.result
|
|
535
|
+
|
|
536
|
+
def sync_regscale_to_integration(
|
|
537
|
+
self,
|
|
538
|
+
regscale_issues: list[Issue],
|
|
539
|
+
sync_attachments: bool = True,
|
|
540
|
+
attachments: Optional[dict] = None,
|
|
541
|
+
**kwargs,
|
|
542
|
+
) -> list[Issue]:
|
|
543
|
+
"""
|
|
544
|
+
Sync issues from RegScale to Jira
|
|
545
|
+
|
|
546
|
+
:param list[Issue] regscale_issues: list of RegScale issues to sync to Jira
|
|
547
|
+
:param bool sync_attachments: Flag to determine if attachments should be synced, defaults to True
|
|
548
|
+
:param Optional[dict] attachments: Dictionary of attachments to sync, defaults to None
|
|
549
|
+
:return: list of RegScale issues that need to be updated
|
|
550
|
+
:rtype: list[Issue]
|
|
551
|
+
"""
|
|
552
|
+
for issue in regscale_issues:
|
|
553
|
+
# see if integration field is an option
|
|
554
|
+
if self.has_integration_field and not getattr(issue, self.integration_id_field, None):
|
|
555
|
+
if new_issue := self.create_issue_in_integration(
|
|
556
|
+
issue=issue,
|
|
557
|
+
add_attachments=sync_attachments,
|
|
558
|
+
attachments=attachments,
|
|
559
|
+
**kwargs,
|
|
560
|
+
):
|
|
561
|
+
self.created_integration_objects.append(new_issue)
|
|
562
|
+
setattr(issue, self.integration_id_field, new_issue.id)
|
|
563
|
+
self.regscale_objects_to_update.append(issue)
|
|
564
|
+
elif (
|
|
565
|
+
not self.has_integration_field
|
|
566
|
+
and issue.manualDetectionSource != self.integration
|
|
567
|
+
and not issue.manualDetectionId
|
|
568
|
+
):
|
|
569
|
+
if new_issue := self.create_issue_in_integration(
|
|
570
|
+
issue=issue,
|
|
571
|
+
add_attachments=sync_attachments,
|
|
572
|
+
attachments=attachments,
|
|
573
|
+
**kwargs,
|
|
574
|
+
):
|
|
575
|
+
self.created_integration_objects.append(new_issue)
|
|
576
|
+
# use the manualDetectionSource and manualDetectionId fields
|
|
577
|
+
issue.manualDetectionSource = self.integration
|
|
578
|
+
issue.manualDetectionId = new_issue.id
|
|
579
|
+
self.regscale_objects_to_update.append(issue)
|
|
580
|
+
# output the final result
|
|
581
|
+
if self.created_integration_objects:
|
|
582
|
+
self.logger.info("%i new ticket(s) opened in %s.", len(self.created_integration_objects), self.integration)
|
|
583
|
+
return self.regscale_objects_to_update
|