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,105 @@
|
|
|
1
|
+
"""Functions for creating, starting, and fetching workflows from RegScale."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict
|
|
4
|
+
|
|
5
|
+
from regscale.core.app.api import Api
|
|
6
|
+
from regscale.core.app.application import Application
|
|
7
|
+
from regscale.core.app.utils.app_utils import (
|
|
8
|
+
error_and_exit,
|
|
9
|
+
get_current_datetime,
|
|
10
|
+
)
|
|
11
|
+
from regscale.models.regscale_models.workflow import Workflow
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_regscale_workflow_from_template(new_assessment_id: int, template_id: int) -> int:
|
|
15
|
+
"""Create a RegScale workflow from a template
|
|
16
|
+
|
|
17
|
+
:param int new_assessment_id: RegScale assessment ID
|
|
18
|
+
:param int template_id: RegScale template ID
|
|
19
|
+
:return: RegScale workflow ID
|
|
20
|
+
:rtype: int
|
|
21
|
+
"""
|
|
22
|
+
app = Application()
|
|
23
|
+
workflow = Workflow(
|
|
24
|
+
name="Placeholder", # just a placeholder its overriden below by the template
|
|
25
|
+
status="Active",
|
|
26
|
+
startDate=get_current_datetime(),
|
|
27
|
+
endDate=None,
|
|
28
|
+
currentStep=0,
|
|
29
|
+
comments="",
|
|
30
|
+
createdById=app.config["userId"],
|
|
31
|
+
dateCreated=get_current_datetime(),
|
|
32
|
+
lastUpdatedById=app.config["userId"],
|
|
33
|
+
isPublic=True,
|
|
34
|
+
dateLastUpdated=get_current_datetime(),
|
|
35
|
+
ownerId=app.config["userId"],
|
|
36
|
+
atlasModule="assessments",
|
|
37
|
+
parentId=new_assessment_id,
|
|
38
|
+
workflowInstanceSteps=[],
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if template := get_workflow_template(app, template_id):
|
|
42
|
+
app.logger.debug(template)
|
|
43
|
+
workflow.name = template.get("name")
|
|
44
|
+
workflow.workflowInstanceSteps = template["workflowTemplateSteps"]
|
|
45
|
+
else:
|
|
46
|
+
error_and_exit(f"Failed to get workflow template with id #{template_id}.")
|
|
47
|
+
|
|
48
|
+
if new_workflow_id := workflow.insert_workflow(app, template_id).id:
|
|
49
|
+
app.logger.info(f"Workflow created with id {new_workflow_id}")
|
|
50
|
+
if submit_workflow_instance(app, workflow.dict(), new_workflow_id):
|
|
51
|
+
return new_workflow_id
|
|
52
|
+
error_and_exit(f"Failed to create RegScale workflow from template with id #{template_id}.")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def create_workflow(app: Application, workflow_data: dict, template_id: int) -> Optional[Dict]:
|
|
56
|
+
"""Create a RegScale workflow from a template
|
|
57
|
+
|
|
58
|
+
:param Application app: Regscale application
|
|
59
|
+
:param dict workflow_data: Workflow data
|
|
60
|
+
:param int template_id: RegScale template ID
|
|
61
|
+
:return: RegScale workflow or None
|
|
62
|
+
:rtype: Optional[Dict]
|
|
63
|
+
"""
|
|
64
|
+
api = Api()
|
|
65
|
+
create_workflow_instance_from_template_url = (
|
|
66
|
+
f"{app.config['domain']}/api/workflowInstances/createFromTemplate/{template_id}"
|
|
67
|
+
)
|
|
68
|
+
workflow_res = api.post(url=create_workflow_instance_from_template_url, json=workflow_data)
|
|
69
|
+
app.logger.debug(workflow_res.status_code)
|
|
70
|
+
if not workflow_res.ok:
|
|
71
|
+
app.logger.error(f"Failed to create RegScale workflow from template with id {template_id}.")
|
|
72
|
+
return None
|
|
73
|
+
return workflow_res.json()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_workflow_template(app: Application, template_id: int) -> Optional[Dict]:
|
|
77
|
+
"""Get a RegScale workflow template
|
|
78
|
+
|
|
79
|
+
:param Application app: Regscale application
|
|
80
|
+
:param int template_id: RegScale template ID
|
|
81
|
+
:return: RegScale workflow template
|
|
82
|
+
:rtype: Optional[Dict]
|
|
83
|
+
"""
|
|
84
|
+
api = Api()
|
|
85
|
+
workflow_template_url = f"{app.config['domain']}/api/workflowTemplates/{template_id}"
|
|
86
|
+
get_template_rep = api.get(url=workflow_template_url)
|
|
87
|
+
return get_template_rep.json() if get_template_rep.ok else None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def submit_workflow_instance(app: Application, workflow_data: dict, workflow_id: int) -> bool:
|
|
91
|
+
"""Submit a RegScale workflow instance
|
|
92
|
+
|
|
93
|
+
:param Application app: Regscale application
|
|
94
|
+
:param dict workflow_data: Workflow data
|
|
95
|
+
:param int workflow_id: RegScale workflow ID
|
|
96
|
+
:return: True if successful else False
|
|
97
|
+
:rtype: bool
|
|
98
|
+
"""
|
|
99
|
+
api = Api()
|
|
100
|
+
submit_workflow_url = f"{app.config['domain']}/api/workflowInstances/submit/{app.config['userId']}"
|
|
101
|
+
submit_resp = api.put(url=submit_workflow_url, json=workflow_data)
|
|
102
|
+
if submit_resp.ok:
|
|
103
|
+
api.logger.info(f"Workflow with id: {workflow_id} started.")
|
|
104
|
+
return True
|
|
105
|
+
return False
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Rich Logging"""
|
|
4
|
+
|
|
5
|
+
# standard python imports
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import tempfile
|
|
9
|
+
from logging.handlers import TimedRotatingFileHandler
|
|
10
|
+
from typing import Any, Optional
|
|
11
|
+
|
|
12
|
+
import click
|
|
13
|
+
from rich.logging import RichHandler
|
|
14
|
+
from rich.traceback import install
|
|
15
|
+
from rich.console import Console
|
|
16
|
+
|
|
17
|
+
from regscale import exceptions
|
|
18
|
+
|
|
19
|
+
if not os.getenv("REGSCALE_DEBUG", False):
|
|
20
|
+
install(suppress=[click, exceptions])
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def create_logger(propagate: Optional[bool] = None, custom_handler: Optional[Any] = None) -> logging.Logger:
|
|
24
|
+
"""
|
|
25
|
+
Create a logger for use in all cases
|
|
26
|
+
|
|
27
|
+
:param Optional[bool] propagate: Whether to propagate the logger, defaults to None
|
|
28
|
+
:param Optional[Any] custom_handler: Custom handler to add to the logger, defaults to None
|
|
29
|
+
:return: logger object
|
|
30
|
+
:rtype: logging.Logger
|
|
31
|
+
"""
|
|
32
|
+
loglevel = os.environ.get("LOGLEVEL", "INFO").upper()
|
|
33
|
+
# Try to get the log width from the environment variable, default to 160 if not set
|
|
34
|
+
try:
|
|
35
|
+
width = int(os.environ.get("REGSCALE_LOG_WIDTH")) # Default to 160 if not set
|
|
36
|
+
rich_handler = RichHandler(rich_tracebacks=False, markup=True, show_time=False, console=Console(width=width))
|
|
37
|
+
except (TypeError, ValueError):
|
|
38
|
+
# If the value is not an integer, set it to None (goes to default to use the full width of the terminal)
|
|
39
|
+
# Without this except block, the logs do NOT print
|
|
40
|
+
rich_handler = RichHandler(rich_tracebacks=False, markup=True, show_time=False)
|
|
41
|
+
|
|
42
|
+
rich_handler.setLevel(loglevel)
|
|
43
|
+
|
|
44
|
+
# Only create file handler if not in container
|
|
45
|
+
handlers: list[logging.Handler] = [rich_handler]
|
|
46
|
+
if os.getenv("REGSCALE_ECS", False) != "True":
|
|
47
|
+
file_handler = TimedRotatingFileHandler(
|
|
48
|
+
filename=f"{tempfile.gettempdir()}{os.sep}RegScale.log",
|
|
49
|
+
when="D",
|
|
50
|
+
interval=3,
|
|
51
|
+
backupCount=10,
|
|
52
|
+
)
|
|
53
|
+
file_handler.setLevel(loglevel)
|
|
54
|
+
handlers.append(file_handler)
|
|
55
|
+
|
|
56
|
+
if custom_handler:
|
|
57
|
+
handlers.append(custom_handler)
|
|
58
|
+
|
|
59
|
+
logging.getLogger("botocore").setLevel(logging.CRITICAL)
|
|
60
|
+
logging.basicConfig(
|
|
61
|
+
level=loglevel,
|
|
62
|
+
format="%(asctime)s [%(levelname)-5.5s] %(message)s",
|
|
63
|
+
datefmt="[%Y/%m/%d %H:%M;%S]",
|
|
64
|
+
handlers=handlers,
|
|
65
|
+
force=os.getenv("REGSCALE_ECS", False) == "True",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
logger = logging.getLogger("regscale")
|
|
69
|
+
logger.handlers = handlers
|
|
70
|
+
logger.setLevel(loglevel)
|
|
71
|
+
logger.parent.handlers = []
|
|
72
|
+
if propagate is not None:
|
|
73
|
+
logger.propagate = propagate
|
|
74
|
+
return logger
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# Author: Luke Bechtel (Regscale)
|
|
2
|
+
import typing
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
from lxml import etree
|
|
7
|
+
from pydantic import BaseModel, ConfigDict
|
|
8
|
+
from typing_extensions import TypedDict
|
|
9
|
+
|
|
10
|
+
from regscale.integrations.public.fedramp.reporting import log_error, log_event
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class LogEventArgs(TypedDict):
|
|
14
|
+
"""
|
|
15
|
+
Args for the log_event function.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
record_type: str
|
|
19
|
+
event_msg: str
|
|
20
|
+
model_layer: str
|
|
21
|
+
level: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LogErrorArgs(TypedDict):
|
|
25
|
+
"""
|
|
26
|
+
Args for the log_error function.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
record_type: str
|
|
30
|
+
missing_element: str
|
|
31
|
+
model_layer: str
|
|
32
|
+
level: str
|
|
33
|
+
event_msg: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class XMLIRTraversal(BaseModel):
|
|
37
|
+
"""
|
|
38
|
+
A traversal through an XML tree to produce an XMLIR object.
|
|
39
|
+
:param xpathToThis: The XPath to the current element
|
|
40
|
+
:param el: The current element
|
|
41
|
+
:param root: The root element
|
|
42
|
+
:param add_error: A function to add an error
|
|
43
|
+
:param add_log: A function to add a log
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
47
|
+
|
|
48
|
+
namespaces: dict
|
|
49
|
+
xpathToThis: str
|
|
50
|
+
el: etree._Element
|
|
51
|
+
root: etree._Element
|
|
52
|
+
|
|
53
|
+
events: List[LogEventArgs] = []
|
|
54
|
+
errors: List[LogErrorArgs] = []
|
|
55
|
+
|
|
56
|
+
def el_xpath(self, xpath: str):
|
|
57
|
+
"""
|
|
58
|
+
Helper function to get an element by xpath from the current element, using the correct namespaces.
|
|
59
|
+
"""
|
|
60
|
+
return self.el.xpath(xpath, namespaces=self.namespaces)
|
|
61
|
+
|
|
62
|
+
def root_xpath(self, xpath: str) -> etree._Element:
|
|
63
|
+
"""
|
|
64
|
+
Helper function to get an element by xpath from the root element, using the correct namespaces.
|
|
65
|
+
|
|
66
|
+
:param str xpath: The xpath to the element
|
|
67
|
+
:return: The element at the provided xpath
|
|
68
|
+
:rtype: etree._Element
|
|
69
|
+
"""
|
|
70
|
+
return self.root.xpath(xpath, namespaces=self.namespaces)
|
|
71
|
+
|
|
72
|
+
def add_event(self, args: LogEventArgs):
|
|
73
|
+
"""
|
|
74
|
+
Add an event to this traversal.
|
|
75
|
+
|
|
76
|
+
:param LogEventArgs args: The event arguments
|
|
77
|
+
"""
|
|
78
|
+
self.events.append(
|
|
79
|
+
log_event(
|
|
80
|
+
record_type=args["record_type"],
|
|
81
|
+
event_msg=args["event_msg"],
|
|
82
|
+
model_layer=args["model_layer"],
|
|
83
|
+
level=args["level"],
|
|
84
|
+
)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def add_error(self, args: LogErrorArgs):
|
|
88
|
+
"""
|
|
89
|
+
Add an error to this traversal.
|
|
90
|
+
|
|
91
|
+
:param LogErrorArgs args: The event arguments
|
|
92
|
+
"""
|
|
93
|
+
self.errors.append(
|
|
94
|
+
log_error(
|
|
95
|
+
record_type=args["record_type"],
|
|
96
|
+
missing_element=args["missing_element"],
|
|
97
|
+
model_layer=args["model_layer"],
|
|
98
|
+
level=args["level"],
|
|
99
|
+
event_msg=args["event_msg"],
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class XMLIR:
|
|
105
|
+
"""
|
|
106
|
+
A class that makes creating intermediate representations of XML objects easier.
|
|
107
|
+
Calling parse() on an XMLIR-inheriting object will run through all its decorated functions
|
|
108
|
+
and return a dictionary of the results.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
__decorated_functions: {} = {}
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def from_el(cls, xpath):
|
|
115
|
+
"""
|
|
116
|
+
A decorator used to decorate functions that will be run on an XML element.
|
|
117
|
+
The return value of the decorated function will be added to the object returned by 'XMLIR.parse()'
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
def wrapper(func):
|
|
121
|
+
qualname = func.__qualname__
|
|
122
|
+
clsname = qualname.rsplit(".", 1)[0] if "." in qualname else None
|
|
123
|
+
|
|
124
|
+
@wraps(func)
|
|
125
|
+
def inner(self, *args, **kwargs):
|
|
126
|
+
return func(self, *args, **kwargs)
|
|
127
|
+
|
|
128
|
+
if clsname not in cls.__decorated_functions:
|
|
129
|
+
cls.__decorated_functions[clsname] = {}
|
|
130
|
+
|
|
131
|
+
if func.__name__ not in cls.__decorated_functions[clsname]:
|
|
132
|
+
cls.__decorated_functions[clsname][func.__name__] = {}
|
|
133
|
+
|
|
134
|
+
cls.__decorated_functions[clsname][func.__name__]["func"] = func
|
|
135
|
+
cls.__decorated_functions[clsname][func.__name__]["xpath"] = xpath
|
|
136
|
+
return inner
|
|
137
|
+
|
|
138
|
+
return wrapper
|
|
139
|
+
|
|
140
|
+
def decorated_functions_for_class(self):
|
|
141
|
+
"""
|
|
142
|
+
Returns the decorated functions for this class.
|
|
143
|
+
"""
|
|
144
|
+
return self.__decorated_functions[self.__class__.__name__]
|
|
145
|
+
|
|
146
|
+
def parse(self, traversal: XMLIRTraversal):
|
|
147
|
+
"""
|
|
148
|
+
Will traverse an XML element,
|
|
149
|
+
and run any functions decorated with @XMLIR.from_el
|
|
150
|
+
"""
|
|
151
|
+
ret = {}
|
|
152
|
+
|
|
153
|
+
# Run through all decorated functions and run them.
|
|
154
|
+
for func_name, f_desc in self.decorated_functions_for_class().items():
|
|
155
|
+
xpath = f_desc["xpath"]
|
|
156
|
+
func = f_desc["func"]
|
|
157
|
+
|
|
158
|
+
# Get the relevant element from the xpath
|
|
159
|
+
xml_els = traversal.el.xpath(xpath, namespaces=traversal.namespaces)
|
|
160
|
+
|
|
161
|
+
# Pass the xml_els to the function
|
|
162
|
+
# And get the result
|
|
163
|
+
processed_xml_els = func(self, xml_els, traversal)
|
|
164
|
+
|
|
165
|
+
# Add the result to the return object
|
|
166
|
+
ret[func_name] = processed_xml_els
|
|
167
|
+
|
|
168
|
+
return ret
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class XMLIR2:
|
|
172
|
+
def __init__(self, traversal: XMLIRTraversal) -> None:
|
|
173
|
+
# Validate that all custom attributes have a getter
|
|
174
|
+
self.check_get_methods()
|
|
175
|
+
|
|
176
|
+
# Get all custom attributes.
|
|
177
|
+
attributes_to_parse = self.get_custom_attrs()
|
|
178
|
+
|
|
179
|
+
# Run through all getters and add the result to the object
|
|
180
|
+
for attr_name in attributes_to_parse:
|
|
181
|
+
getter = self.getter_for_attr(attr_name)
|
|
182
|
+
setattr(self, attr_name, getter(traversal))
|
|
183
|
+
|
|
184
|
+
def __repr__(self) -> str:
|
|
185
|
+
"""
|
|
186
|
+
Print out all normal attributes of the object.
|
|
187
|
+
"""
|
|
188
|
+
attrs = self.custom_attr_dict()
|
|
189
|
+
|
|
190
|
+
return f"{self.__class__.__name__} ({str(attrs)})"
|
|
191
|
+
|
|
192
|
+
def custom_attr_dict(self) -> dict:
|
|
193
|
+
"""
|
|
194
|
+
Return a list of all custom attributes and their values.
|
|
195
|
+
"""
|
|
196
|
+
custom_attrs = self.get_custom_attrs()
|
|
197
|
+
return {attr_name: getattr(self, attr_name) for attr_name in custom_attrs}
|
|
198
|
+
|
|
199
|
+
def items(self):
|
|
200
|
+
return self.custom_attr_dict().items()
|
|
201
|
+
|
|
202
|
+
def __iter__(self):
|
|
203
|
+
return iter(self.items())
|
|
204
|
+
|
|
205
|
+
def getter_for_attr(self, attr_name: str):
|
|
206
|
+
return getattr(self, f"get_{attr_name}")
|
|
207
|
+
|
|
208
|
+
def check_get_methods(self) -> None:
|
|
209
|
+
attributes = self.get_custom_attrs()
|
|
210
|
+
|
|
211
|
+
for attr in attributes:
|
|
212
|
+
getter_name = f"get_{attr}"
|
|
213
|
+
if not hasattr(self, getter_name):
|
|
214
|
+
raise AttributeError(
|
|
215
|
+
f"No 'get_{attr}' method found. "
|
|
216
|
+
"All non-callable, non-'__'-prefixed custom attributes must have a getter."
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Get the getter method
|
|
220
|
+
getter_method = getattr(self.__class__, getter_name)
|
|
221
|
+
|
|
222
|
+
# Check if the getter has a return type hint
|
|
223
|
+
getter_hints = typing.get_type_hints(getter_method)
|
|
224
|
+
if "return" not in getter_hints:
|
|
225
|
+
raise TypeError(f"Getter '{getter_name}' does not have a return type hint.")
|
|
226
|
+
|
|
227
|
+
# Check if the attribute has a type hint
|
|
228
|
+
attr_hints = typing.get_type_hints(self.__class__)
|
|
229
|
+
if attr not in attr_hints:
|
|
230
|
+
raise TypeError(f"Attribute '{attr}' does not have a type hint.")
|
|
231
|
+
|
|
232
|
+
# Compare the type hint of the getter and the attribute
|
|
233
|
+
if getter_hints["return"] != attr_hints[attr]:
|
|
234
|
+
raise TypeError(
|
|
235
|
+
f"Type hint of the attribute '{attr}' does not match the return type hint of its getter method."
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
getters = self.get_custom_attr_getters()
|
|
239
|
+
for getter in getters:
|
|
240
|
+
attr_name = getter.replace("get_", "")
|
|
241
|
+
if not hasattr(self, attr_name):
|
|
242
|
+
raise AttributeError(f"Getter '{getter}' found, but no attribute '{attr_name}' found.")
|
|
243
|
+
|
|
244
|
+
def get_custom_attr_getters(self) -> List[str]:
|
|
245
|
+
attributes: List[str] = [
|
|
246
|
+
attr_name
|
|
247
|
+
for attr_name, attr_value in self.__class__.__dict__.items()
|
|
248
|
+
if callable(attr_value) and attr_name.startswith("get_")
|
|
249
|
+
]
|
|
250
|
+
return attributes
|
|
251
|
+
|
|
252
|
+
def get_custom_attrs(self) -> List[str]:
|
|
253
|
+
attributes: List[str] = [
|
|
254
|
+
attr_name
|
|
255
|
+
for attr_name, attr_value in self.__class__.__dict__.items()
|
|
256
|
+
if not callable(attr_value) and not attr_name.startswith("__")
|
|
257
|
+
]
|
|
258
|
+
return attributes
|
|
File without changes
|