regscale-cli 6.21.2.0__py3-none-any.whl → 6.28.2.1__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.
- regscale/_version.py +1 -1
- regscale/airflow/hierarchy.py +2 -2
- regscale/core/app/api.py +5 -2
- regscale/core/app/application.py +36 -6
- regscale/core/app/internal/control_editor.py +73 -21
- regscale/core/app/internal/evidence.py +727 -204
- regscale/core/app/internal/login.py +4 -2
- regscale/core/app/internal/model_editor.py +219 -64
- regscale/core/app/utils/app_utils.py +86 -12
- regscale/core/app/utils/catalog_utils/common.py +1 -1
- regscale/core/login.py +21 -4
- regscale/core/utils/async_graphql_client.py +363 -0
- regscale/core/utils/date.py +77 -1
- regscale/dev/cli.py +26 -0
- regscale/dev/code_gen.py +109 -24
- regscale/dev/version.py +72 -0
- regscale/integrations/commercial/__init__.py +30 -2
- regscale/integrations/commercial/aws/audit_manager_compliance.py +3908 -0
- regscale/integrations/commercial/aws/cli.py +3107 -54
- regscale/integrations/commercial/aws/cloudtrail_control_mappings.py +333 -0
- regscale/integrations/commercial/aws/cloudtrail_evidence.py +501 -0
- regscale/integrations/commercial/aws/cloudwatch_control_mappings.py +357 -0
- regscale/integrations/commercial/aws/cloudwatch_evidence.py +490 -0
- regscale/integrations/commercial/{amazon → aws}/common.py +71 -19
- regscale/integrations/commercial/aws/config_compliance.py +914 -0
- regscale/integrations/commercial/aws/conformance_pack_mappings.py +198 -0
- regscale/integrations/commercial/aws/control_compliance_analyzer.py +439 -0
- regscale/integrations/commercial/aws/evidence_generator.py +283 -0
- regscale/integrations/commercial/aws/guardduty_control_mappings.py +340 -0
- regscale/integrations/commercial/aws/guardduty_evidence.py +1053 -0
- regscale/integrations/commercial/aws/iam_control_mappings.py +368 -0
- regscale/integrations/commercial/aws/iam_evidence.py +574 -0
- regscale/integrations/commercial/aws/inventory/__init__.py +338 -22
- regscale/integrations/commercial/aws/inventory/base.py +107 -5
- regscale/integrations/commercial/aws/inventory/resources/analytics.py +390 -0
- regscale/integrations/commercial/aws/inventory/resources/applications.py +234 -0
- regscale/integrations/commercial/aws/inventory/resources/audit_manager.py +513 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail.py +315 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudtrail_logs_metadata.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/cloudwatch.py +191 -0
- regscale/integrations/commercial/aws/inventory/resources/compute.py +328 -9
- regscale/integrations/commercial/aws/inventory/resources/config.py +464 -0
- regscale/integrations/commercial/aws/inventory/resources/containers.py +74 -9
- regscale/integrations/commercial/aws/inventory/resources/database.py +481 -31
- regscale/integrations/commercial/aws/inventory/resources/developer_tools.py +253 -0
- regscale/integrations/commercial/aws/inventory/resources/guardduty.py +286 -0
- regscale/integrations/commercial/aws/inventory/resources/iam.py +470 -0
- regscale/integrations/commercial/aws/inventory/resources/inspector.py +476 -0
- regscale/integrations/commercial/aws/inventory/resources/integration.py +175 -61
- regscale/integrations/commercial/aws/inventory/resources/kms.py +447 -0
- regscale/integrations/commercial/aws/inventory/resources/machine_learning.py +358 -0
- regscale/integrations/commercial/aws/inventory/resources/networking.py +390 -67
- regscale/integrations/commercial/aws/inventory/resources/s3.py +394 -0
- regscale/integrations/commercial/aws/inventory/resources/security.py +268 -72
- regscale/integrations/commercial/aws/inventory/resources/securityhub.py +473 -0
- regscale/integrations/commercial/aws/inventory/resources/storage.py +288 -29
- regscale/integrations/commercial/aws/inventory/resources/systems_manager.py +657 -0
- regscale/integrations/commercial/aws/inventory/resources/vpc.py +655 -0
- regscale/integrations/commercial/aws/kms_control_mappings.py +288 -0
- regscale/integrations/commercial/aws/kms_evidence.py +879 -0
- regscale/integrations/commercial/aws/ocsf/__init__.py +7 -0
- regscale/integrations/commercial/aws/ocsf/constants.py +115 -0
- regscale/integrations/commercial/aws/ocsf/mapper.py +435 -0
- regscale/integrations/commercial/aws/org_control_mappings.py +286 -0
- regscale/integrations/commercial/aws/org_evidence.py +666 -0
- regscale/integrations/commercial/aws/s3_control_mappings.py +356 -0
- regscale/integrations/commercial/aws/s3_evidence.py +632 -0
- regscale/integrations/commercial/aws/scanner.py +1072 -205
- regscale/integrations/commercial/aws/security_hub.py +319 -0
- regscale/integrations/commercial/aws/session_manager.py +282 -0
- regscale/integrations/commercial/aws/ssm_control_mappings.py +291 -0
- regscale/integrations/commercial/aws/ssm_evidence.py +492 -0
- regscale/integrations/commercial/jira.py +489 -153
- regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
- regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
- regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
- regscale/integrations/commercial/qualys/__init__.py +167 -68
- regscale/integrations/commercial/qualys/scanner.py +305 -39
- regscale/integrations/commercial/sarif/sairf_importer.py +432 -0
- regscale/integrations/commercial/sarif/sarif_converter.py +67 -0
- regscale/integrations/commercial/sicura/api.py +79 -42
- regscale/integrations/commercial/sicura/commands.py +8 -2
- regscale/integrations/commercial/sicura/scanner.py +83 -44
- regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
- regscale/integrations/commercial/synqly/assets.py +133 -16
- regscale/integrations/commercial/synqly/edr.py +2 -8
- regscale/integrations/commercial/synqly/query_builder.py +536 -0
- regscale/integrations/commercial/synqly/ticketing.py +27 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +165 -28
- regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
- regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
- regscale/integrations/commercial/tenablev2/commands.py +146 -5
- regscale/integrations/commercial/tenablev2/scanner.py +1 -3
- regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
- regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +191 -76
- regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
- regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
- regscale/integrations/commercial/wizv2/compliance_report.py +1592 -0
- regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
- regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +7 -3
- regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +92 -89
- regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
- regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +66 -9
- regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
- regscale/integrations/commercial/wizv2/issue.py +776 -28
- regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
- regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
- regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
- regscale/integrations/commercial/wizv2/reports.py +243 -0
- regscale/integrations/commercial/wizv2/sbom.py +1 -1
- regscale/integrations/commercial/wizv2/scanner.py +1031 -441
- regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
- regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
- regscale/integrations/commercial/wizv2/variables.py +89 -3
- regscale/integrations/compliance_integration.py +1036 -151
- regscale/integrations/control_matcher.py +432 -0
- regscale/integrations/due_date_handler.py +333 -0
- regscale/integrations/milestone_manager.py +291 -0
- regscale/integrations/public/__init__.py +14 -0
- regscale/integrations/public/cci_importer.py +834 -0
- regscale/integrations/public/csam/__init__.py +0 -0
- regscale/integrations/public/csam/csam.py +938 -0
- regscale/integrations/public/csam/csam_agency_defined.py +179 -0
- regscale/integrations/public/csam/csam_common.py +154 -0
- regscale/integrations/public/csam/csam_controls.py +432 -0
- regscale/integrations/public/csam/csam_poam.py +124 -0
- regscale/integrations/public/fedramp/click.py +77 -6
- regscale/integrations/public/fedramp/docx_parser.py +10 -1
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +675 -289
- regscale/integrations/public/fedramp/fedramp_five.py +1 -1
- regscale/integrations/public/fedramp/poam/scanner.py +75 -7
- regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
- regscale/integrations/scanner_integration.py +1961 -430
- regscale/models/integration_models/CCI_List.xml +1 -0
- regscale/models/integration_models/aqua.py +2 -2
- regscale/models/integration_models/cisa_kev_data.json +805 -11
- regscale/models/integration_models/flat_file_importer/__init__.py +5 -8
- regscale/models/integration_models/nexpose.py +36 -10
- regscale/models/integration_models/qualys.py +3 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +87 -18
- regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
- regscale/models/integration_models/synqly_models/ocsf_mapper.py +124 -25
- regscale/models/integration_models/synqly_models/synqly_model.py +89 -16
- regscale/models/locking.py +12 -8
- regscale/models/platform.py +4 -2
- regscale/models/regscale_models/__init__.py +7 -0
- regscale/models/regscale_models/assessment.py +2 -1
- regscale/models/regscale_models/catalog.py +1 -1
- regscale/models/regscale_models/compliance_settings.py +251 -1
- regscale/models/regscale_models/component.py +1 -0
- regscale/models/regscale_models/control_implementation.py +236 -41
- regscale/models/regscale_models/control_objective.py +74 -5
- regscale/models/regscale_models/file.py +2 -0
- regscale/models/regscale_models/form_field_value.py +5 -3
- regscale/models/regscale_models/inheritance.py +44 -0
- regscale/models/regscale_models/issue.py +301 -102
- regscale/models/regscale_models/milestone.py +33 -14
- regscale/models/regscale_models/organization.py +3 -0
- regscale/models/regscale_models/regscale_model.py +310 -73
- regscale/models/regscale_models/security_plan.py +4 -2
- regscale/models/regscale_models/vulnerability.py +3 -3
- regscale/regscale.py +25 -4
- regscale/templates/__init__.py +0 -0
- regscale/utils/threading/threadhandler.py +20 -15
- regscale/validation/record.py +23 -1
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/METADATA +17 -33
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/RECORD +310 -111
- tests/core/__init__.py +0 -0
- tests/core/utils/__init__.py +0 -0
- tests/core/utils/test_async_graphql_client.py +472 -0
- tests/fixtures/test_fixture.py +13 -8
- tests/regscale/core/test_login.py +171 -4
- tests/regscale/integrations/commercial/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/__init__.py +0 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_compliance.py +1304 -0
- tests/regscale/integrations/commercial/aws/test_audit_manager_evidence_aggregation.py +341 -0
- tests/regscale/integrations/commercial/aws/test_aws_analytics_collector.py +260 -0
- tests/regscale/integrations/commercial/aws/test_aws_applications_collector.py +242 -0
- tests/regscale/integrations/commercial/aws/test_aws_audit_manager_collector.py +1155 -0
- tests/regscale/integrations/commercial/aws/test_aws_cloudtrail_collector.py +534 -0
- tests/regscale/integrations/commercial/aws/test_aws_config_collector.py +400 -0
- tests/regscale/integrations/commercial/aws/test_aws_developer_tools_collector.py +203 -0
- tests/regscale/integrations/commercial/aws/test_aws_guardduty_collector.py +315 -0
- tests/regscale/integrations/commercial/aws/test_aws_iam_collector.py +458 -0
- tests/regscale/integrations/commercial/aws/test_aws_inspector_collector.py +353 -0
- tests/regscale/integrations/commercial/aws/test_aws_inventory_integration.py +530 -0
- tests/regscale/integrations/commercial/aws/test_aws_kms_collector.py +919 -0
- tests/regscale/integrations/commercial/aws/test_aws_machine_learning_collector.py +237 -0
- tests/regscale/integrations/commercial/aws/test_aws_s3_collector.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_scanner_integration.py +722 -0
- tests/regscale/integrations/commercial/aws/test_aws_securityhub_collector.py +792 -0
- tests/regscale/integrations/commercial/aws/test_aws_systems_manager_collector.py +918 -0
- tests/regscale/integrations/commercial/aws/test_aws_vpc_collector.py +996 -0
- tests/regscale/integrations/commercial/aws/test_cli_evidence.py +431 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_control_mappings.py +452 -0
- tests/regscale/integrations/commercial/aws/test_cloudtrail_evidence.py +788 -0
- tests/regscale/integrations/commercial/aws/test_config_compliance.py +298 -0
- tests/regscale/integrations/commercial/aws/test_conformance_pack_mappings.py +200 -0
- tests/regscale/integrations/commercial/aws/test_control_compliance_analyzer.py +375 -0
- tests/regscale/integrations/commercial/aws/test_datetime_parsing.py +223 -0
- tests/regscale/integrations/commercial/aws/test_evidence_generator.py +386 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_control_mappings.py +564 -0
- tests/regscale/integrations/commercial/aws/test_guardduty_evidence.py +1041 -0
- tests/regscale/integrations/commercial/aws/test_iam_control_mappings.py +718 -0
- tests/regscale/integrations/commercial/aws/test_iam_evidence.py +1375 -0
- tests/regscale/integrations/commercial/aws/test_kms_control_mappings.py +656 -0
- tests/regscale/integrations/commercial/aws/test_kms_evidence.py +1163 -0
- tests/regscale/integrations/commercial/aws/test_ocsf_mapper.py +370 -0
- tests/regscale/integrations/commercial/aws/test_org_control_mappings.py +546 -0
- tests/regscale/integrations/commercial/aws/test_org_evidence.py +1240 -0
- tests/regscale/integrations/commercial/aws/test_s3_control_mappings.py +672 -0
- tests/regscale/integrations/commercial/aws/test_s3_evidence.py +987 -0
- tests/regscale/integrations/commercial/aws/test_scanner_evidence.py +373 -0
- tests/regscale/integrations/commercial/aws/test_security_hub_config_filtering.py +539 -0
- tests/regscale/integrations/commercial/aws/test_session_manager.py +516 -0
- tests/regscale/integrations/commercial/aws/test_ssm_control_mappings.py +588 -0
- tests/regscale/integrations/commercial/aws/test_ssm_evidence.py +735 -0
- tests/regscale/integrations/commercial/conftest.py +28 -0
- tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
- tests/regscale/integrations/commercial/test_aws.py +3742 -0
- tests/regscale/integrations/commercial/test_burp.py +48 -0
- tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
- tests/regscale/integrations/commercial/test_dependabot.py +341 -0
- tests/regscale/integrations/commercial/test_gcp.py +1543 -0
- tests/regscale/integrations/commercial/test_gitlab.py +549 -0
- tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
- tests/regscale/integrations/commercial/test_jira.py +2204 -0
- tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
- tests/regscale/integrations/commercial/test_okta.py +1228 -0
- tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
- tests/regscale/integrations/commercial/test_sicura.py +349 -0
- tests/regscale/integrations/commercial/test_snow.py +423 -0
- tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
- tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
- tests/regscale/integrations/commercial/test_stig.py +33 -0
- tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
- tests/regscale/integrations/commercial/test_stigv2.py +406 -0
- tests/regscale/integrations/commercial/test_wiz.py +1365 -0
- tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
- tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
- tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
- tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
- tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
- tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
- tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
- tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
- tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
- tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
- tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
- tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
- tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
- tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
- tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
- tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1218 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
- tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
- tests/regscale/integrations/public/__init__.py +0 -0
- tests/regscale/integrations/public/fedramp/__init__.py +1 -0
- tests/regscale/integrations/public/fedramp/test_gen_asset_list.py +150 -0
- tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
- tests/regscale/integrations/public/test_alienvault.py +220 -0
- tests/regscale/integrations/public/test_cci.py +1053 -0
- tests/regscale/integrations/public/test_cisa.py +1021 -0
- tests/regscale/integrations/public/test_emass.py +518 -0
- tests/regscale/integrations/public/test_fedramp.py +1152 -0
- tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
- tests/regscale/integrations/public/test_file_uploads.py +506 -0
- tests/regscale/integrations/public/test_oscal.py +453 -0
- tests/regscale/integrations/test_compliance_status_mapping.py +406 -0
- tests/regscale/integrations/test_control_matcher.py +1421 -0
- tests/regscale/integrations/test_control_matching.py +155 -0
- tests/regscale/integrations/test_milestone_manager.py +408 -0
- tests/regscale/models/test_control_implementation.py +118 -3
- tests/regscale/models/test_form_field_value_integration.py +304 -0
- tests/regscale/models/test_issue.py +378 -1
- tests/regscale/models/test_module_integration.py +582 -0
- tests/regscale/models/test_tenable_integrations.py +811 -105
- regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3057
- regscale/integrations/public/fedramp/mappings/fedramp_r4_parts.json +0 -7388
- regscale/integrations/public/fedramp/mappings/fedramp_r5_parts.json +0 -9605
- regscale/integrations/public/fedramp/parts_mapper.py +0 -107
- /regscale/integrations/commercial/{amazon → sarif}/__init__.py +0 -0
- /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.21.2.0.dist-info → regscale_cli-6.28.2.1.dist-info}/top_level.txt +0 -0
|
@@ -5,7 +5,11 @@ from typing import Any, Union, TYPE_CHECKING, Optional
|
|
|
5
5
|
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
7
|
from regscale.models.integration_models.synqly_models.connectors import Edr, Ticketing, Vulnerabilities
|
|
8
|
-
from synqly.engine.resources.ocsf.resources.
|
|
8
|
+
from synqly.engine.resources.ocsf.resources.v_1_3_0.resources.securityfinding import (
|
|
9
|
+
Finding,
|
|
10
|
+
ResourceDetails,
|
|
11
|
+
Vulnerability,
|
|
12
|
+
)
|
|
9
13
|
|
|
10
14
|
from synqly import engine
|
|
11
15
|
from synqly.engine import CreateTicketRequest
|
|
@@ -78,7 +82,7 @@ class Mapper:
|
|
|
78
82
|
"""
|
|
79
83
|
due_date = convert_datetime_to_regscale_string(ticket.due_date)
|
|
80
84
|
if not due_date:
|
|
81
|
-
due_date = self._determine_due_date(ticket.priority)
|
|
85
|
+
due_date = self._determine_due_date(ticket.priority, connector)
|
|
82
86
|
ticket_dict = ticket.dict()
|
|
83
87
|
key_val_desc = "All fields:\n"
|
|
84
88
|
for k, v in ticket_dict.items():
|
|
@@ -102,9 +106,8 @@ class Mapper:
|
|
|
102
106
|
regscale_issue.manualDetectionId = ticket.id
|
|
103
107
|
return regscale_issue
|
|
104
108
|
|
|
105
|
-
@staticmethod
|
|
106
109
|
def _ocsf_asset_to_regscale(
|
|
107
|
-
connector: Union["Edr", "Vulnerabilities"], asset: Union[InventoryInfo, OCSFAsset, SoftwareInfo], **kwargs
|
|
110
|
+
self, connector: Union["Edr", "Vulnerabilities"], asset: Union[InventoryInfo, OCSFAsset, SoftwareInfo], **kwargs
|
|
108
111
|
) -> IntegrationAsset:
|
|
109
112
|
"""
|
|
110
113
|
Convert OCSF Asset to RegScale Asset
|
|
@@ -130,6 +133,9 @@ class Mapper:
|
|
|
130
133
|
else:
|
|
131
134
|
name = device_data.name or device_data.hostname or f"{connector.provider} Asset: {device_data.uid}"
|
|
132
135
|
category = AssetCategory.Software
|
|
136
|
+
ip_v4s, ip_v6s = self._determine_ip_addresses(
|
|
137
|
+
device_data.ip_addresses if device_data.ip_addresses else [device_data.ip]
|
|
138
|
+
)
|
|
133
139
|
return IntegrationAsset(
|
|
134
140
|
name=name,
|
|
135
141
|
identifier=device_data.uid,
|
|
@@ -139,7 +145,8 @@ class Mapper:
|
|
|
139
145
|
parent_module=SecurityPlan.get_module_string(),
|
|
140
146
|
mac_address=device_data.mac,
|
|
141
147
|
fqdn=device_data.hostname,
|
|
142
|
-
ip_address=", ".join(
|
|
148
|
+
ip_address=", ".join(ip_v4s),
|
|
149
|
+
ipv6_address=", ".join(ip_v6s),
|
|
143
150
|
location=device_data.location or device_data.zone,
|
|
144
151
|
vlan_id=device_data.vlan_uid,
|
|
145
152
|
other_tracking_number=device_data.uid,
|
|
@@ -152,7 +159,64 @@ class Mapper:
|
|
|
152
159
|
)
|
|
153
160
|
|
|
154
161
|
@staticmethod
|
|
155
|
-
def
|
|
162
|
+
def _determine_ip_addresses(ips: list[str]) -> tuple[list[str], list[str]]:
|
|
163
|
+
"""
|
|
164
|
+
Parse the list of ips and return a tuple of two lists: list of IP v4 and IP v6 addresses
|
|
165
|
+
|
|
166
|
+
:param list[str] ips: List of IP addresses
|
|
167
|
+
:return: Tuple containing two lists, list of IP v4 addresses and list of IP v6 addresses
|
|
168
|
+
:rtype: tuple[list[str], list[str]]
|
|
169
|
+
"""
|
|
170
|
+
import ipaddress
|
|
171
|
+
|
|
172
|
+
ip_v4s = []
|
|
173
|
+
ip_v6s = []
|
|
174
|
+
for ip in ips:
|
|
175
|
+
try:
|
|
176
|
+
ipaddress.IPv4Address(ip)
|
|
177
|
+
ip_v4s.append(ip)
|
|
178
|
+
except ipaddress.AddressValueError:
|
|
179
|
+
try:
|
|
180
|
+
ipaddress.IPv6Address(ip)
|
|
181
|
+
ip_v6s.append(ip)
|
|
182
|
+
except ipaddress.AddressValueError:
|
|
183
|
+
continue
|
|
184
|
+
return ip_v4s, ip_v6s
|
|
185
|
+
|
|
186
|
+
@staticmethod
|
|
187
|
+
def _determine_date(
|
|
188
|
+
attribute: str, finding: Optional["Finding"] = None, vuln: Optional["Vulnerability"] = None
|
|
189
|
+
) -> datetime:
|
|
190
|
+
"""
|
|
191
|
+
Determine the date based on the provided vulnerability or finding
|
|
192
|
+
|
|
193
|
+
:param str attribute: The attribute to determine the date for
|
|
194
|
+
:param Optional[SecurityFinding] finding: The finding to determine the date for
|
|
195
|
+
:param Optional[Vulnerability] vuln: The vulnerability to determine the date for
|
|
196
|
+
:return: The date
|
|
197
|
+
:rtype: datetime
|
|
198
|
+
"""
|
|
199
|
+
from regscale.core.utils.date import normalize_timestamp
|
|
200
|
+
|
|
201
|
+
vuln_date = getattr(vuln, attribute) if vuln else None
|
|
202
|
+
fallback_vuln_date = getattr(vuln, attribute.replace("_dt", ""), None) if vuln else None
|
|
203
|
+
finding_date = getattr(finding, attribute) if finding else None
|
|
204
|
+
fallback_finding_date = getattr(finding, attribute.replace("_dt", ""), None) if finding else None
|
|
205
|
+
if vuln_date:
|
|
206
|
+
return vuln_date
|
|
207
|
+
elif finding_date:
|
|
208
|
+
return finding_date
|
|
209
|
+
# No datetime objects, lets try the epoch integers
|
|
210
|
+
if fallback_vuln_date:
|
|
211
|
+
fallback_vuln_date = normalize_timestamp(fallback_vuln_date)
|
|
212
|
+
return datetime.fromtimestamp(fallback_vuln_date)
|
|
213
|
+
elif fallback_finding_date:
|
|
214
|
+
fallback_finding_date = normalize_timestamp(fallback_finding_date)
|
|
215
|
+
return datetime.fromtimestamp(fallback_finding_date)
|
|
216
|
+
else:
|
|
217
|
+
return datetime.now()
|
|
218
|
+
|
|
219
|
+
def _parse_finding_data(self, finding: "SecurityFinding", vuln: Optional["Vulnerability"] = None) -> dict:
|
|
156
220
|
"""
|
|
157
221
|
Parse the data from the SecurityFinding object
|
|
158
222
|
|
|
@@ -160,20 +224,25 @@ class Mapper:
|
|
|
160
224
|
:return: A dictionary of the parsed data
|
|
161
225
|
:rtype: dict
|
|
162
226
|
"""
|
|
163
|
-
from synqly.engine.resources.ocsf.resources.
|
|
227
|
+
from synqly.engine.resources.ocsf.resources.v_1_3_0.resources.securityfinding import Remediation
|
|
164
228
|
|
|
165
229
|
finding_data = {
|
|
166
230
|
"cve": None,
|
|
167
|
-
"first_seen":
|
|
168
|
-
"last_seen":
|
|
231
|
+
"first_seen": self._determine_date("first_seen_time_dt", getattr(finding, "finding", None), vuln),
|
|
232
|
+
"last_seen": self._determine_date("last_seen_time_dt", getattr(finding, "finding", None), vuln),
|
|
169
233
|
"plugin_id": vuln.cve.uid if vuln else finding.finding.product_uid,
|
|
170
|
-
"severity":
|
|
234
|
+
"severity": vuln.severity if vuln and getattr(vuln, "severity") else finding.severity_id,
|
|
171
235
|
"remediation": getattr(vuln, "remediation") if vuln else finding.finding.remediation.desc,
|
|
236
|
+
"title": getattr(vuln, "title") or finding.finding.title,
|
|
172
237
|
}
|
|
173
238
|
if vuln:
|
|
174
239
|
finding_data["cve"] = vuln.cve.uid
|
|
175
240
|
else:
|
|
176
241
|
finding_data["cve"] = finding.finding.title if "cve" in finding.finding.title.lower() else None
|
|
242
|
+
try:
|
|
243
|
+
finding_data["severity"] = int(finding_data["severity"])
|
|
244
|
+
except ValueError:
|
|
245
|
+
finding_data["severity"] = 0
|
|
177
246
|
if isinstance(finding_data["remediation"], Remediation):
|
|
178
247
|
finding_data["remediation"] = finding_data["remediation"].desc
|
|
179
248
|
elif isinstance(finding_data["remediation"], dict):
|
|
@@ -183,8 +252,26 @@ class Mapper:
|
|
|
183
252
|
finding_data["remediation"] = remediation_string
|
|
184
253
|
elif isinstance(finding_data["remediation"], list):
|
|
185
254
|
finding_data["remediation"] = "\n".join(finding_data["remediation"])
|
|
255
|
+
finding_data["title"] = self._determine_title(finding_data["title"], finding_data["cve"])
|
|
186
256
|
return finding_data
|
|
187
257
|
|
|
258
|
+
@staticmethod
|
|
259
|
+
def _determine_title(title: Optional[str], cve: Optional[str]) -> str:
|
|
260
|
+
"""
|
|
261
|
+
Determine the title based on the provided title and cve
|
|
262
|
+
|
|
263
|
+
:param Optional[str] title: The title to determine the title for, defaults to None
|
|
264
|
+
:param Optional[str] cve: The cve to determine the title for, defaults to None
|
|
265
|
+
:return: The title
|
|
266
|
+
:rtype: str
|
|
267
|
+
"""
|
|
268
|
+
if title and cve:
|
|
269
|
+
return f"{title} - {cve}"
|
|
270
|
+
elif title is None and cve:
|
|
271
|
+
return cve
|
|
272
|
+
else:
|
|
273
|
+
return title
|
|
274
|
+
|
|
188
275
|
@staticmethod
|
|
189
276
|
def _populate_cvs_scores(finding_obj: IntegrationFinding, vuln: Optional["Vulnerability"] = None) -> None:
|
|
190
277
|
"""
|
|
@@ -203,6 +290,17 @@ class Mapper:
|
|
|
203
290
|
else:
|
|
204
291
|
finding_obj.cvss_score = cvs.base_score
|
|
205
292
|
|
|
293
|
+
# validate that only one of the CVSS scores is populated
|
|
294
|
+
if finding_obj.cvss_v3_score:
|
|
295
|
+
finding_obj.cvss_v2_score = None
|
|
296
|
+
finding_obj.cvss_score = None
|
|
297
|
+
elif finding_obj.cvss_v2_score:
|
|
298
|
+
finding_obj.cvss_v3_score = None
|
|
299
|
+
finding_obj.cvss_score = None
|
|
300
|
+
elif finding_obj.cvss_score:
|
|
301
|
+
finding_obj.cvss_v3_score = None
|
|
302
|
+
finding_obj.cvss_v2_score = None
|
|
303
|
+
|
|
206
304
|
def _security_finding_to_regscale(
|
|
207
305
|
self, connector: "Vulnerabilities", finding: SecurityFinding, **_
|
|
208
306
|
) -> list[IntegrationFinding]:
|
|
@@ -227,28 +325,29 @@ class Mapper:
|
|
|
227
325
|
"""
|
|
228
326
|
base = vuln if vuln else finding.finding
|
|
229
327
|
finding_data = self._parse_finding_data(finding, vuln)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
328
|
+
resource_data = getattr(resource, "data", {})
|
|
329
|
+
dns = resource_data.get("hostname") or resource.uid if vuln else None
|
|
330
|
+
ip_v4s, ip_v6s = self._determine_ip_addresses(
|
|
331
|
+
resource_data["ipAddresses"] if resource_data.get("ipAddresses") else [resource_data.get("ip")]
|
|
332
|
+
)
|
|
333
|
+
ips = ip_v4s + ip_v6s
|
|
236
334
|
|
|
237
335
|
finding_obj = IntegrationFinding(
|
|
238
336
|
control_labels=[],
|
|
239
337
|
category=f"{connector.integration_name} Vulnerability",
|
|
240
|
-
title=
|
|
338
|
+
title=finding_data["title"],
|
|
241
339
|
plugin_name=connector.integration_name,
|
|
242
340
|
severity=Issue.assign_severity(finding.severity), # type: ignore
|
|
243
341
|
description=base.desc,
|
|
244
342
|
status=finding.status or "Open",
|
|
245
343
|
first_seen=self._datetime_to_str(finding_data["first_seen"]),
|
|
246
344
|
last_seen=self._datetime_to_str(finding_data["last_seen"]),
|
|
247
|
-
ip_address=
|
|
345
|
+
ip_address=", ".join(ips),
|
|
248
346
|
plugin_id=finding_data["plugin_id"],
|
|
347
|
+
due_date=self._determine_due_date(finding_data["severity"], connector),
|
|
249
348
|
dns=dns,
|
|
250
349
|
severity_int=finding_data["severity"],
|
|
251
|
-
issue_title=
|
|
350
|
+
issue_title=finding_data["title"],
|
|
252
351
|
cve=finding_data["cve"],
|
|
253
352
|
evidence=finding.evidence,
|
|
254
353
|
impact=finding.impact,
|
|
@@ -330,22 +429,22 @@ class Mapper:
|
|
|
330
429
|
else:
|
|
331
430
|
return Priority.LOW
|
|
332
431
|
|
|
333
|
-
def _determine_due_date(self, severity: str) -> str:
|
|
432
|
+
def _determine_due_date(self, severity: str, connector: Union["Ticketing", "Vulnerabilities"]) -> str:
|
|
334
433
|
"""
|
|
335
434
|
Determine the due date based on the provided severity
|
|
336
435
|
|
|
337
436
|
:param str severity: RegScale severity
|
|
437
|
+
:param Union["Ticketing", "Vulnerabilities"] connector: Ticketing or Vulnerabilities connector class object
|
|
338
438
|
:return: Due date for the issue
|
|
339
439
|
:rtype: str
|
|
340
440
|
"""
|
|
341
|
-
from datetime import timedelta
|
|
342
|
-
|
|
343
441
|
if "high" in severity.lower():
|
|
344
|
-
|
|
442
|
+
default_days = 30
|
|
345
443
|
elif "medium" in severity.lower():
|
|
346
|
-
|
|
444
|
+
default_days = 90
|
|
347
445
|
else:
|
|
348
|
-
|
|
446
|
+
default_days = 180
|
|
447
|
+
return Issue.get_due_date(severity, connector.app.config, connector.integration, default_days=default_days)
|
|
349
448
|
|
|
350
449
|
def _regscale_issue_to_ticket(self, regscale_issue: Issue, **kwargs) -> CreateTicketRequest:
|
|
351
450
|
"""
|
|
@@ -20,6 +20,7 @@ from regscale.core.app.api import Api
|
|
|
20
20
|
from regscale.core.app.application import Application
|
|
21
21
|
from regscale.core.app.utils.app_utils import create_progress_object, error_and_exit
|
|
22
22
|
from regscale.models.integration_models.synqly_models.connector_types import ConnectorType
|
|
23
|
+
from regscale.models.integration_models.synqly_models.filter_parser import FilterParser
|
|
23
24
|
from regscale.models.integration_models.synqly_models.ocsf_mapper import Mapper
|
|
24
25
|
from regscale.models.integration_models.synqly_models.param import Param
|
|
25
26
|
from regscale.models.integration_models.synqly_models.tenants import Tenant
|
|
@@ -37,7 +38,7 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
37
38
|
client: Optional[Any] = None
|
|
38
39
|
connectors: dict = Field(default_factory=dict)
|
|
39
40
|
# defined using the openApi spec on 7/16/2024, this is updated via _get_integrations_and_secrets()
|
|
40
|
-
connector_types: set = Field(default_factory=lambda:
|
|
41
|
+
connector_types: set = Field(default_factory=lambda: {connector.__str__() for connector in ConnectorType})
|
|
41
42
|
terminated: Optional[bool] = False
|
|
42
43
|
app: Application = Field(default_factory=Application, alias="app")
|
|
43
44
|
api: Api = Field(default_factory=Api, alias="api")
|
|
@@ -60,6 +61,7 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
60
61
|
created_regscale_objects: list = Field(default_factory=list)
|
|
61
62
|
updated_regscale_objects: list = Field(default_factory=list)
|
|
62
63
|
regscale_objects_to_update: list = Field(default_factory=list)
|
|
64
|
+
filter_parser: Optional[FilterParser] = None
|
|
63
65
|
|
|
64
66
|
def __init__(self: S, connector_type: Optional[str] = None, integration: Optional[str] = None, **kwargs):
|
|
65
67
|
try:
|
|
@@ -241,7 +243,14 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
241
243
|
config[f"{self._connector_type}_{self.integration}_{key}"] = attribute[key].default
|
|
242
244
|
elif not skip_prompts:
|
|
243
245
|
self.logger.info(f"Enter the {key} for {self.integration}. Description: {attribute[key].description}")
|
|
244
|
-
|
|
246
|
+
if key.lower() in ["secret", "password"] or "token" in key.lower():
|
|
247
|
+
from getpass import getpass
|
|
248
|
+
|
|
249
|
+
print(f"{attribute[key].description} (input will be hidden)")
|
|
250
|
+
provided_secret = getpass(f"{key}: ")
|
|
251
|
+
else:
|
|
252
|
+
print(f"{attribute[key].description} (input will be visible)")
|
|
253
|
+
provided_secret = input(f"{key}: ")
|
|
245
254
|
kwargs[key] = provided_secret
|
|
246
255
|
config[f"{self._connector_type}_{self.integration}_{key}"] = provided_secret
|
|
247
256
|
else:
|
|
@@ -278,6 +287,8 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
278
287
|
:rtype: dict
|
|
279
288
|
"""
|
|
280
289
|
raw_data = self._load_from_package()
|
|
290
|
+
# Initialize FilterParser with the loaded capabilities data
|
|
291
|
+
self.filter_parser = FilterParser(capabilities_data=raw_data)
|
|
281
292
|
return self._parse_api_spec_data(raw_data, return_params)
|
|
282
293
|
|
|
283
294
|
def _parse_api_spec_data(self, data: dict, return_params: bool = False) -> dict:
|
|
@@ -441,12 +452,12 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
441
452
|
|
|
442
453
|
operations = schema.get("operations", [])
|
|
443
454
|
capabilities = [item["name"] for item in operations if item.get("supported")]
|
|
444
|
-
capabilities_params =
|
|
455
|
+
capabilities_params = [
|
|
445
456
|
field
|
|
446
457
|
for item in operations
|
|
447
458
|
if item.get("supported") and "required_fields" in item.keys()
|
|
448
459
|
for field in item.get("required_fields", [])
|
|
449
|
-
|
|
460
|
+
]
|
|
450
461
|
if self.integration.lower() in key.lower():
|
|
451
462
|
self.capabilities = capabilities
|
|
452
463
|
schema = schema["provider_config"]
|
|
@@ -636,6 +647,42 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
636
647
|
"""
|
|
637
648
|
pass
|
|
638
649
|
|
|
650
|
+
def validate_filters(self, filters: Union[tuple, list, str]) -> list[str]:
|
|
651
|
+
"""
|
|
652
|
+
Validate filter strings against provider capabilities.
|
|
653
|
+
|
|
654
|
+
:param Union[tuple, list, str] filters: Filter(s) to validate
|
|
655
|
+
:return: Validated filter list
|
|
656
|
+
:rtype: list[str]
|
|
657
|
+
:raises: SystemExit if validation fails
|
|
658
|
+
"""
|
|
659
|
+
if not self.filter_parser:
|
|
660
|
+
self.logger.warning("FilterParser not available for filter validation")
|
|
661
|
+
if isinstance(filters, list):
|
|
662
|
+
return filters
|
|
663
|
+
elif filters:
|
|
664
|
+
return [filters]
|
|
665
|
+
else:
|
|
666
|
+
return []
|
|
667
|
+
|
|
668
|
+
provider_id = f"{self._connector_type}_{self.integration}"
|
|
669
|
+
validated_filters = []
|
|
670
|
+
|
|
671
|
+
# Normalize to list for processing
|
|
672
|
+
if isinstance(filters, str):
|
|
673
|
+
filters = [filters]
|
|
674
|
+
elif filters is None:
|
|
675
|
+
return []
|
|
676
|
+
|
|
677
|
+
for filter_string in filters:
|
|
678
|
+
is_valid, error_message = self.filter_parser.validate_filter(provider_id, filter_string)
|
|
679
|
+
if not is_valid:
|
|
680
|
+
error_and_exit(f"Filter validation failed: {error_message}")
|
|
681
|
+
validated_filters.append(filter_string)
|
|
682
|
+
self.logger.debug(f"Filter '{filter_string}' validated successfully")
|
|
683
|
+
|
|
684
|
+
return validated_filters
|
|
685
|
+
|
|
639
686
|
def fetch_integration_data(
|
|
640
687
|
self, func: Callable, **kwargs
|
|
641
688
|
) -> list[Union["InventoryAsset", "SecurityFinding", "Ticket"]]:
|
|
@@ -648,11 +695,12 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
648
695
|
"""
|
|
649
696
|
query_filter = kwargs.get("filter")
|
|
650
697
|
limit = kwargs.get("limit", 200)
|
|
698
|
+
|
|
699
|
+
# Validate filters if provided
|
|
700
|
+
if query_filter:
|
|
701
|
+
query_filter = self.validate_filters(query_filter)
|
|
651
702
|
integration_data: list = []
|
|
652
|
-
fetch_res = func
|
|
653
|
-
filter=query_filter,
|
|
654
|
-
limit=limit,
|
|
655
|
-
)
|
|
703
|
+
fetch_res = self._fetch_data_with_retries(func=func, limit=limit, filter=query_filter)
|
|
656
704
|
self.logger.info(f"Received {len(fetch_res.result)} record(s) from {self.integration_name}.")
|
|
657
705
|
integration_data.extend(fetch_res.result)
|
|
658
706
|
# check and handle pagination
|
|
@@ -660,24 +708,49 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
660
708
|
try:
|
|
661
709
|
# fetch.cursor can be an int as a string, or a continuation token
|
|
662
710
|
while int(fetch_res.cursor) == len(integration_data):
|
|
663
|
-
fetch_res =
|
|
664
|
-
filter=query_filter
|
|
665
|
-
limit=limit,
|
|
666
|
-
cursor=fetch_res.cursor,
|
|
711
|
+
fetch_res = self._fetch_data_with_retries(
|
|
712
|
+
func=func, limit=limit, cursor=fetch_res.cursor, filter=query_filter
|
|
667
713
|
)
|
|
668
714
|
integration_data.extend(fetch_res.result)
|
|
669
715
|
except ValueError:
|
|
670
716
|
while fetch_res.cursor:
|
|
671
|
-
fetch_res =
|
|
672
|
-
filter=query_filter
|
|
673
|
-
limit=limit,
|
|
674
|
-
cursor=fetch_res.cursor,
|
|
717
|
+
fetch_res = self._fetch_data_with_retries(
|
|
718
|
+
func=func, limit=limit, cursor=fetch_res.cursor, filter=query_filter
|
|
675
719
|
)
|
|
676
720
|
integration_data.extend(fetch_res.result)
|
|
677
721
|
self.logger.info(f"Received {len(integration_data)} record(s) from {self.integration_name}...")
|
|
678
722
|
self.logger.info(f"Fetched {len(integration_data)} total record(s) from {self.integration_name}...")
|
|
679
723
|
return integration_data
|
|
680
724
|
|
|
725
|
+
def _fetch_data_with_retries(
|
|
726
|
+
self, func: Callable, limit: int, cursor: Union[str, int, None] = None, filter: Optional[str] = None
|
|
727
|
+
) -> Any:
|
|
728
|
+
"""
|
|
729
|
+
Fetches data from the integration using the provided function and handles pagination
|
|
730
|
+
|
|
731
|
+
:param Callable func: The function to fetch data from the integration
|
|
732
|
+
:param int limit: The limit of records to fetch
|
|
733
|
+
:param Optional[str] filter: The filter to apply to the data
|
|
734
|
+
:return: The data from the integration
|
|
735
|
+
:rtype: Any
|
|
736
|
+
"""
|
|
737
|
+
import time
|
|
738
|
+
|
|
739
|
+
retries = 0
|
|
740
|
+
sleep_timers = [10, 20, 40]
|
|
741
|
+
while retries < 3:
|
|
742
|
+
try:
|
|
743
|
+
if cursor:
|
|
744
|
+
return func(limit=limit, filter=filter, cursor=cursor)
|
|
745
|
+
else:
|
|
746
|
+
return func(limit=limit, filter=filter)
|
|
747
|
+
except Exception as e:
|
|
748
|
+
self.logger.error(f"Error fetching data with retries: {e}")
|
|
749
|
+
self.logger.error(f"Retrying in {sleep_timers[retries]} seconds...")
|
|
750
|
+
time.sleep(sleep_timers[retries])
|
|
751
|
+
retries += 1
|
|
752
|
+
error_and_exit("Failed to fetch data after 3 retries")
|
|
753
|
+
|
|
681
754
|
def run_integration_sync(self, *args, **kwargs) -> None:
|
|
682
755
|
"""
|
|
683
756
|
Runs the sync process for the integration
|
regscale/models/locking.py
CHANGED
|
@@ -90,11 +90,15 @@ class FileLock:
|
|
|
90
90
|
|
|
91
91
|
:rtype: None
|
|
92
92
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
try:
|
|
94
|
+
if os.path.isfile(self.lock_file):
|
|
95
|
+
with open(self.lock_file, "r") as lockfile:
|
|
96
|
+
scope = str(lockfile.read().strip())
|
|
97
|
+
|
|
98
|
+
if scope == str(self.lock_scope):
|
|
99
|
+
os.remove(self.lock_file) # Lock released
|
|
100
|
+
else:
|
|
101
|
+
pass # Lock can only be released by {self.lock_scope}
|
|
102
|
+
except FileNotFoundError:
|
|
103
|
+
# Lock file was already removed by another process, this is acceptable in parallel execution
|
|
104
|
+
pass
|
regscale/models/platform.py
CHANGED
|
@@ -5,10 +5,9 @@ from typing import Optional, Union
|
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, SecretStr
|
|
7
7
|
|
|
8
|
+
from regscale.core.app.api import Api
|
|
8
9
|
from regscale.core.login import get_regscale_token
|
|
9
10
|
from regscale.core.utils.urls import generate_regscale_domain_url
|
|
10
|
-
from regscale.core.app.api import Api
|
|
11
|
-
from regscale.core.app.application import Application
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class RegScaleAuth(BaseModel):
|
|
@@ -45,6 +44,7 @@ class RegScaleAuth(BaseModel):
|
|
|
45
44
|
password: Optional[Union[str, SecretStr]] = None,
|
|
46
45
|
domain: Optional[str] = None,
|
|
47
46
|
mfa_token: Optional[str] = None,
|
|
47
|
+
app_id: Optional[int] = 1,
|
|
48
48
|
) -> "RegScaleAuth":
|
|
49
49
|
"""
|
|
50
50
|
Authenticate with RegScale and return a token and a user_id
|
|
@@ -53,6 +53,7 @@ class RegScaleAuth(BaseModel):
|
|
|
53
53
|
:param Optional[Union[str, SecretStr]] password: Password to log in with, defaults to None
|
|
54
54
|
:param Optional[str] domain: Domain to log into, defaults to None
|
|
55
55
|
:param Optional[str] mfa_token: mfa_token to verify, defaults to None
|
|
56
|
+
:param Optional[int] app_id: The app ID to login with
|
|
56
57
|
:return: RegScaleAuth object
|
|
57
58
|
:rtype: RegScaleAuth
|
|
58
59
|
"""
|
|
@@ -89,6 +90,7 @@ class RegScaleAuth(BaseModel):
|
|
|
89
90
|
password=password.get_secret_value(),
|
|
90
91
|
domain=domain,
|
|
91
92
|
mfa_token=mfa_token,
|
|
93
|
+
app_id=app_id,
|
|
92
94
|
)
|
|
93
95
|
return cls(
|
|
94
96
|
user_id=uid,
|
|
@@ -41,6 +41,7 @@ from .implementation_objective import *
|
|
|
41
41
|
from .implementation_option import *
|
|
42
42
|
from .implementation_role import *
|
|
43
43
|
from .incident import *
|
|
44
|
+
from .inheritance import *
|
|
44
45
|
from .inherited_control import *
|
|
45
46
|
from .interconnection import *
|
|
46
47
|
from .issue import *
|
|
@@ -50,7 +51,10 @@ from .link import *
|
|
|
50
51
|
from .master_assessment import *
|
|
51
52
|
from .milestone import *
|
|
52
53
|
from .meta_data import *
|
|
54
|
+
from .module import *
|
|
55
|
+
from .modules import *
|
|
53
56
|
from .objective import *
|
|
57
|
+
from .organization import *
|
|
54
58
|
from .parameter import *
|
|
55
59
|
from .policy import *
|
|
56
60
|
from .ports_protocol import *
|
|
@@ -79,10 +83,13 @@ from .stig import *
|
|
|
79
83
|
from .supply_chain import *
|
|
80
84
|
from .system_role import *
|
|
81
85
|
from .system_role_external_assignment import *
|
|
86
|
+
from .tag import *
|
|
87
|
+
from .tag_mapping import *
|
|
82
88
|
from .task import *
|
|
83
89
|
from .threat import *
|
|
84
90
|
from .team import *
|
|
85
91
|
from .user import *
|
|
92
|
+
from .user_group import *
|
|
86
93
|
from .vulnerability import *
|
|
87
94
|
from .vulnerability_mapping import *
|
|
88
95
|
from .workflow import *
|
|
@@ -5,6 +5,7 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from typing import Any, List, Optional, Union
|
|
7
7
|
|
|
8
|
+
from pydantic import Field
|
|
8
9
|
from requests import JSONDecodeError
|
|
9
10
|
|
|
10
11
|
from regscale.core.app.api import Api
|
|
@@ -60,7 +61,7 @@ class Assessment(RegScaleModel):
|
|
|
60
61
|
_module_slug = "assessments"
|
|
61
62
|
|
|
62
63
|
id: int = 0
|
|
63
|
-
leadAssessorId: str =
|
|
64
|
+
leadAssessorId: str = Field(default_factory=RegScaleModel.get_user_id)
|
|
64
65
|
title: Optional[str] = None
|
|
65
66
|
assessmentType: Optional[Union[AssessmentType, str]] = None
|
|
66
67
|
plannedStart: Optional[str] = None
|