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,373 @@
|
|
|
1
|
+
"""Helper functions for the CLI API Wrapper"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from contextlib import redirect_stdout, suppress
|
|
5
|
+
from io import StringIO
|
|
6
|
+
from typing import Callable, Dict, Optional, Tuple, Union, List
|
|
7
|
+
from urllib.parse import urljoin
|
|
8
|
+
|
|
9
|
+
from click import Argument, Command, Context, Option
|
|
10
|
+
from flask import url_for
|
|
11
|
+
from flask_restx import Api, Model, Resource, fields
|
|
12
|
+
from flask_restx.reqparse import RequestParser
|
|
13
|
+
from requests.exceptions import JSONDecodeError
|
|
14
|
+
from requests.models import ReadTimeoutError
|
|
15
|
+
|
|
16
|
+
from regscale.core.app.api import Api as CliApi
|
|
17
|
+
from regscale.core.app.application import Application
|
|
18
|
+
from regscale.utils.string import remove_ansi_escape_sequences
|
|
19
|
+
|
|
20
|
+
GET_OPTIONS = "GET, OPTIONS"
|
|
21
|
+
POST_OPTIONS = "POST, OPTIONS"
|
|
22
|
+
ALLOWED_METHODS = "Allowed methods"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def execute_click_command_in_context(command: Command, params: dict = None) -> str:
|
|
26
|
+
"""Execute a click command
|
|
27
|
+
|
|
28
|
+
:param Command command: a click.Command for performing the logic
|
|
29
|
+
:param dict params: an optional dictionary of parameter key values to pass, defaults to None
|
|
30
|
+
:return: a string of the command output
|
|
31
|
+
:rtype: str
|
|
32
|
+
"""
|
|
33
|
+
with Context(command) as ctx:
|
|
34
|
+
# redirect stdout to a string to capture command output
|
|
35
|
+
output_stream = StringIO()
|
|
36
|
+
with redirect_stdout(output_stream):
|
|
37
|
+
with suppress(SystemExit):
|
|
38
|
+
if params:
|
|
39
|
+
# run command, params are key values of the request JSON
|
|
40
|
+
ctx.invoke(command, **params)
|
|
41
|
+
else:
|
|
42
|
+
# no params, so don't use them
|
|
43
|
+
ctx.invoke(command)
|
|
44
|
+
# retrieve the output, remove ansi escape sequences to not color
|
|
45
|
+
# also remove trailing returns
|
|
46
|
+
output = remove_ansi_escape_sequences(text=output_stream.getvalue().strip())
|
|
47
|
+
return output
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# pylint: disable=duplicate-code
|
|
51
|
+
def create_view_func(command: Command) -> Union[Callable, Resource]:
|
|
52
|
+
"""Create a factory function for returning a CommandResource object for use in an API.
|
|
53
|
+
|
|
54
|
+
:param Command command: a click.Command for performing the logic
|
|
55
|
+
:return: a Resource instance
|
|
56
|
+
:rtype: Union[Callable, Resource]
|
|
57
|
+
"""
|
|
58
|
+
parser = RequestParser()
|
|
59
|
+
for param in command.params:
|
|
60
|
+
parser.add_argument(param.human_readable_name, type=param.type, location="json")
|
|
61
|
+
|
|
62
|
+
# define a CommandResource class to return (get or post depending upon if params are expected)
|
|
63
|
+
if command.params and all([param.required for param in command.params]):
|
|
64
|
+
|
|
65
|
+
class CommandResource(Resource):
|
|
66
|
+
"""Allow for the use of this view function using flask_restx."""
|
|
67
|
+
|
|
68
|
+
def options(self) -> dict:
|
|
69
|
+
"""Return the allowed methods and any parameters expected"""
|
|
70
|
+
methods = GET_OPTIONS if not command.params else POST_OPTIONS
|
|
71
|
+
return {
|
|
72
|
+
ALLOWED_METHODS: methods,
|
|
73
|
+
"Parameters": [param.human_readable_name for param in command.params],
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
def post(self) -> Tuple[Dict[str, str], int]:
|
|
77
|
+
"""Return output with params in a POST request"""
|
|
78
|
+
if not command.params:
|
|
79
|
+
return {
|
|
80
|
+
"message": "Invalid method. This endpoint does not accept parameters, use GET method instead"
|
|
81
|
+
}, 405
|
|
82
|
+
args = parser.parse_args()
|
|
83
|
+
params = {k: v for k, v in args.items() if v is not None}
|
|
84
|
+
output = execute_click_command_in_context(command, params)
|
|
85
|
+
return {"input": f"regscale {command.name}", "output": output}, 200
|
|
86
|
+
|
|
87
|
+
elif command.params:
|
|
88
|
+
|
|
89
|
+
class CommandResource(Resource):
|
|
90
|
+
"""Allow for the use of this view function using flask_restx."""
|
|
91
|
+
|
|
92
|
+
def options(self) -> dict:
|
|
93
|
+
"""Return the allowed methods and any parameters expected"""
|
|
94
|
+
methods = GET_OPTIONS if not command.params else POST_OPTIONS
|
|
95
|
+
return {
|
|
96
|
+
ALLOWED_METHODS: methods,
|
|
97
|
+
"Parameters": [param.human_readable_name for param in command.params],
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
def post(self) -> Tuple[Dict[str, str], int]:
|
|
101
|
+
"""Return output with params in a POST request"""
|
|
102
|
+
if not command.params:
|
|
103
|
+
return {
|
|
104
|
+
"message": "Invalid method. This endpoint does not accept parameters, use GET method instead"
|
|
105
|
+
}, 405
|
|
106
|
+
args = parser.parse_args()
|
|
107
|
+
params = {k: v for k, v in args.items() if v is not None}
|
|
108
|
+
output = execute_click_command_in_context(command, params)
|
|
109
|
+
return {"input": f"regscale {command.name}", "output": output}, 200
|
|
110
|
+
|
|
111
|
+
def get(self) -> Tuple[Dict[str, str], int]:
|
|
112
|
+
"""Return output if get command is invoked."""
|
|
113
|
+
if command.params:
|
|
114
|
+
return {
|
|
115
|
+
"message": "Invalid method. This endpoint expects parameters "
|
|
116
|
+
"use POST method for this endpoint with the following parameters: "
|
|
117
|
+
f"{','.join([p.human_readable_name for p in command.params])}",
|
|
118
|
+
}, 405
|
|
119
|
+
output = execute_click_command_in_context(command)
|
|
120
|
+
return {"input": f"regscale {command.name}", "output": output}, 200
|
|
121
|
+
|
|
122
|
+
else:
|
|
123
|
+
|
|
124
|
+
class CommandResource(Resource):
|
|
125
|
+
"""Allow for the use of this view function using flask_restx."""
|
|
126
|
+
|
|
127
|
+
def options(self) -> dict:
|
|
128
|
+
"""Return the allowed methods and any parameters expected"""
|
|
129
|
+
methods = GET_OPTIONS if not command.params else POST_OPTIONS
|
|
130
|
+
return {
|
|
131
|
+
ALLOWED_METHODS: methods,
|
|
132
|
+
"Parameters": [param.human_readable_name for param in command.params],
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
def get(self) -> Tuple[Dict[str, str], int]:
|
|
136
|
+
"""Return output if get command is invoked."""
|
|
137
|
+
if command.params:
|
|
138
|
+
return {
|
|
139
|
+
"message": "Invalid method. This endpoint expects parameters "
|
|
140
|
+
"use POST method for this endpoint with the following parameters: "
|
|
141
|
+
f"{','.join([p.human_readable_name for p in command.params])}",
|
|
142
|
+
}, 405
|
|
143
|
+
output = execute_click_command_in_context(command)
|
|
144
|
+
return {"input": f"regscale {command.name}", "output": output}, 200
|
|
145
|
+
|
|
146
|
+
return CommandResource
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# pylint: enable=duplicate-code
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _get_field_type(param: Union[Option, Argument]) -> fields.Raw:
|
|
153
|
+
"""Retrieve the field type from a click.Option or click.Argument
|
|
154
|
+
|
|
155
|
+
:param Union[Option, Argument] param: a click.Option or click.Argument
|
|
156
|
+
:return: a flask_restx.fields type
|
|
157
|
+
:rtype: fields.Raw
|
|
158
|
+
"""
|
|
159
|
+
if param.type == int:
|
|
160
|
+
return fields.Integer
|
|
161
|
+
if param.type == float:
|
|
162
|
+
return fields.Float
|
|
163
|
+
if param.type == bool:
|
|
164
|
+
return fields.Boolean
|
|
165
|
+
return fields.String
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def generate_parameters_model(api_instance: Api, command: Command) -> Model:
|
|
169
|
+
"""Generate a Flask_restx parameter model
|
|
170
|
+
|
|
171
|
+
:param Api api_instance: the flask_restx.Api instance
|
|
172
|
+
:param Command command: a click.Command to retrieve a Model from
|
|
173
|
+
:return: a flask_restx.Model of the parameters
|
|
174
|
+
:rtype: Model
|
|
175
|
+
"""
|
|
176
|
+
parameters = {}
|
|
177
|
+
for param in command.params:
|
|
178
|
+
field_type = _get_field_type(param)
|
|
179
|
+
parameters[param.name] = field_type(required=param.required, description=param.help)
|
|
180
|
+
return api_instance.model(f"{command.name.title().replace('_', '')}Parameters", parameters)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_site_info() -> Dict:
|
|
184
|
+
"""Get site info
|
|
185
|
+
|
|
186
|
+
:return: Dictionary of site information to pass into pages
|
|
187
|
+
:rtype: Dict
|
|
188
|
+
"""
|
|
189
|
+
app = Application()
|
|
190
|
+
site_info = {"domain": os.getenv("REGSCALE_DOMAIN", app.config["domain"])}
|
|
191
|
+
|
|
192
|
+
return site_info
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def get_catgalogues() -> List[Tuple[int, str]]:
|
|
196
|
+
"""Get catalogues information
|
|
197
|
+
|
|
198
|
+
:return: List of tuples of catalogs in RegScale instance
|
|
199
|
+
:rtype: List[Tuple[int, str]]
|
|
200
|
+
"""
|
|
201
|
+
cli_app = Application()
|
|
202
|
+
api = CliApi(cli_app)
|
|
203
|
+
try:
|
|
204
|
+
response = api.get(f"{cli_app.config['domain']}/api/catalogues/getlist")
|
|
205
|
+
catalog_data = response.json()
|
|
206
|
+
regscale_catalogues = [
|
|
207
|
+
(catalog["id"], f'{catalog["title"][:45]} (#{catalog["id"]})') for catalog in catalog_data
|
|
208
|
+
]
|
|
209
|
+
except (JSONDecodeError, KeyError, AttributeError, TypeError):
|
|
210
|
+
regscale_catalogues = [
|
|
211
|
+
(1, "Please Login to RegScale to retrieve Catalogues."),
|
|
212
|
+
]
|
|
213
|
+
return sorted(regscale_catalogues, key=lambda x: x[1])
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def get_ssps() -> List[Tuple[int, str]]:
|
|
217
|
+
"""Get Security Plans from RegScale instance
|
|
218
|
+
|
|
219
|
+
:return: List of tuples of catalogs in RegScale instance
|
|
220
|
+
:rtype: List[Tuple[int, str]]
|
|
221
|
+
"""
|
|
222
|
+
cli_app = Application()
|
|
223
|
+
api = CliApi(cli_app)
|
|
224
|
+
body = """
|
|
225
|
+
query {
|
|
226
|
+
securityPlans(take: 50, skip: 0) {
|
|
227
|
+
items {
|
|
228
|
+
id
|
|
229
|
+
systemName
|
|
230
|
+
},
|
|
231
|
+
pageInfo {
|
|
232
|
+
hasNextPage
|
|
233
|
+
}
|
|
234
|
+
,totalCount}
|
|
235
|
+
}
|
|
236
|
+
"""
|
|
237
|
+
try:
|
|
238
|
+
response = api.graph(query=body)
|
|
239
|
+
ssp_data = response["securityPlans"]["items"]
|
|
240
|
+
regscale_ssps = [(ssp["id"], f'{ssp["systemName"][:45]} (#{ssp["id"]})') for ssp in ssp_data]
|
|
241
|
+
except (JSONDecodeError, KeyError, AttributeError, TypeError):
|
|
242
|
+
regscale_ssps = [
|
|
243
|
+
(1, "Please Login to RegScale to retrieve SSPs."),
|
|
244
|
+
]
|
|
245
|
+
return regscale_ssps
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def get_ssp_name(ssp_id: Union[str, int]) -> str:
|
|
249
|
+
"""Get Security Plans from RegScale instance
|
|
250
|
+
|
|
251
|
+
:param Union[str, int] ssp_id: ID of SSP to retrieve name for
|
|
252
|
+
:return: Name of the SSP from RegScale
|
|
253
|
+
:rtype: str
|
|
254
|
+
"""
|
|
255
|
+
cli_app = Application()
|
|
256
|
+
api = CliApi(cli_app)
|
|
257
|
+
body = f"""
|
|
258
|
+
query {{
|
|
259
|
+
securityPlans(take: 50, skip: 0, where: {{id: {{eq: {ssp_id}}}}}) {{
|
|
260
|
+
items {{
|
|
261
|
+
id
|
|
262
|
+
systemName
|
|
263
|
+
}},
|
|
264
|
+
pageInfo {{
|
|
265
|
+
hasNextPage
|
|
266
|
+
}}
|
|
267
|
+
,totalCount}}
|
|
268
|
+
}}
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
response = api.graph(query=body)
|
|
272
|
+
ssp_name = response["securityPlans"]["items"][0]["systemName"]
|
|
273
|
+
except (JSONDecodeError, KeyError, AttributeError, TypeError, ReadTimeoutError):
|
|
274
|
+
ssp_name = f"RegScale SSP# {ssp_id}"
|
|
275
|
+
return ssp_name
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def delete_all_items_in_directory(dir_path: str, keep_ext: Optional[str] = None) -> None:
|
|
279
|
+
"""
|
|
280
|
+
Recursively delete all files and directories in the given directory
|
|
281
|
+
|
|
282
|
+
:param str dir_path: The directory whose contents are to be deleted
|
|
283
|
+
:param Optional[str] keep_ext: The file extension to keep (g.g., '.ckl') If None, all files will be deleted
|
|
284
|
+
:rtype: None
|
|
285
|
+
"""
|
|
286
|
+
# Check if the directory exists
|
|
287
|
+
if not os.path.exists(dir_path):
|
|
288
|
+
print(f"The directory {dir_path} does not exist.")
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
# Iterate through all the items in the directory
|
|
292
|
+
for item in os.listdir(dir_path):
|
|
293
|
+
item_path = os.path.join(dir_path, item)
|
|
294
|
+
|
|
295
|
+
# If item is a directory, recursively call this function
|
|
296
|
+
if os.path.isdir(item_path):
|
|
297
|
+
delete_all_items_in_directory(item_path, keep_ext)
|
|
298
|
+
|
|
299
|
+
# Remove the directory only if it's empty
|
|
300
|
+
if not os.listdir(item_path):
|
|
301
|
+
os.rmdir(item_path)
|
|
302
|
+
|
|
303
|
+
else:
|
|
304
|
+
check_ext(keep_ext, item_path)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def check_ext(keep_ext: Optional[str], item_path: str) -> None:
|
|
308
|
+
"""
|
|
309
|
+
Check if the file extension should be kept or removed
|
|
310
|
+
|
|
311
|
+
:param Optional[str] keep_ext: The file extension to keep (g.g., '.ckl') If None, all files will be deleted
|
|
312
|
+
:param str item_path: The path of the file to check
|
|
313
|
+
"""
|
|
314
|
+
# If keep_ext is None, remove all files
|
|
315
|
+
if keep_ext is None:
|
|
316
|
+
os.remove(item_path)
|
|
317
|
+
else:
|
|
318
|
+
# Otherwise, get the file extension and check if it should be kept
|
|
319
|
+
_, ext = os.path.splitext(item_path)
|
|
320
|
+
if ext != keep_ext:
|
|
321
|
+
os.remove(item_path)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def get_profiles() -> List[Tuple[int, str]]:
|
|
325
|
+
"""Get catalogues information
|
|
326
|
+
|
|
327
|
+
:return: List of tuples of catalogs in RegScale instance
|
|
328
|
+
:rtype: List[Tuple[int, str]]
|
|
329
|
+
"""
|
|
330
|
+
cli_app = Application()
|
|
331
|
+
api = CliApi(cli_app)
|
|
332
|
+
response = api.get(urljoin(cli_app.config["domain"], "/api/profiles/getlist"))
|
|
333
|
+
try:
|
|
334
|
+
profiles = response.json()
|
|
335
|
+
regscale_profiles = [(profile["id"], f'{profile["name"][:45]} (#{profile["id"]})') for profile in profiles]
|
|
336
|
+
except (JSONDecodeError, KeyError, AttributeError, TypeError, ReadTimeoutError):
|
|
337
|
+
regscale_profiles = [
|
|
338
|
+
(1, "Please Login to RegScale to retrieve Profiles."),
|
|
339
|
+
]
|
|
340
|
+
return sorted(regscale_profiles, key=lambda x: x[1])
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def generate(app, file_path, filename, catalogue_id):
|
|
344
|
+
"""
|
|
345
|
+
Generate the HTML content for processing the SSP
|
|
346
|
+
"""
|
|
347
|
+
# Push/flush content to browser
|
|
348
|
+
yield "<html><head><title>Processing SSP</title></head><body>"
|
|
349
|
+
yield "<h1>Processing Security Plan</h1>"
|
|
350
|
+
|
|
351
|
+
with app.test_request_context() as context:
|
|
352
|
+
from regscale.integrations.public.fedramp.import_fedramp_r4_ssp import parse_and_load_xml_rev4
|
|
353
|
+
|
|
354
|
+
# Call the existing function and get the CSV path and results
|
|
355
|
+
parse_and_load_gen = parse_and_load_xml_rev4(
|
|
356
|
+
context, file_path=file_path, filename=filename, catalogue_id=catalogue_id
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
csv_path, result_output, implementation_results = None, None, None
|
|
360
|
+
# Yield from the generator returned by parse_and_load_xml_rev4
|
|
361
|
+
for content in parse_and_load_gen:
|
|
362
|
+
if isinstance(content, tuple):
|
|
363
|
+
csv_path, result_output, implementation_results = content
|
|
364
|
+
else:
|
|
365
|
+
yield content
|
|
366
|
+
|
|
367
|
+
if csv_path and result_output and implementation_results:
|
|
368
|
+
final_results_url = url_for(
|
|
369
|
+
"final_results", csv_path=csv_path, result_output=result_output, filename=filename
|
|
370
|
+
)
|
|
371
|
+
# yield a javascript re-direct to the browser to clear progress content and load result page
|
|
372
|
+
yield f'<script>window.location.href = "{final_results_url}";</script>'
|
|
373
|
+
yield "</body></html>"
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""Generate a REST API from the click model."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Union
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from contextlib import redirect_stdout
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from flask import Flask, request
|
|
9
|
+
except ImportError:
|
|
10
|
+
raise ImportError("regscale must be installed with the [server] extra.")
|
|
11
|
+
|
|
12
|
+
from regscale.models.click_models import ClickGroup, ClickCommand, ClickOption
|
|
13
|
+
from regscale.core.utils.click_utils import REGSCALE_CLI
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
app = Flask(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def generate_routes(group: ClickGroup, path: str = ""):
|
|
20
|
+
"""Generate routes recursively
|
|
21
|
+
:param ClickGroup group: a ClickGroup BaseModel
|
|
22
|
+
:param str path: the endpoint path
|
|
23
|
+
"""
|
|
24
|
+
for command in group.commands:
|
|
25
|
+
if isinstance(command, ClickCommand):
|
|
26
|
+
endpoint_path = f"{path}/{command.name}"
|
|
27
|
+
|
|
28
|
+
# create the view function
|
|
29
|
+
def create_view_func(command_: ClickCommand):
|
|
30
|
+
def view_func():
|
|
31
|
+
params = {param.name: request.json.get(param.name) for param in command_.params}
|
|
32
|
+
output_stream = StringIO()
|
|
33
|
+
with redirect_stdout(output_stream):
|
|
34
|
+
command_.callback(**params)
|
|
35
|
+
output = output_stream.getvalue()
|
|
36
|
+
return {"input": f"regscale {command_.name}", "output": output}
|
|
37
|
+
|
|
38
|
+
return view_func
|
|
39
|
+
|
|
40
|
+
# replace / with _ to create a unique command name
|
|
41
|
+
unique_command_name = endpoint_path.replace("/", "__")
|
|
42
|
+
# determine methods
|
|
43
|
+
methods = ["POST"] if command.params else ["GET"]
|
|
44
|
+
|
|
45
|
+
# add the route to the Flask app
|
|
46
|
+
app.add_url_rule(
|
|
47
|
+
rule=endpoint_path,
|
|
48
|
+
endpoint=unique_command_name,
|
|
49
|
+
view_func=create_view_func(command),
|
|
50
|
+
methods=methods,
|
|
51
|
+
)
|
|
52
|
+
elif isinstance(command, ClickGroup):
|
|
53
|
+
generate_routes(command, f"{path}/{command.name}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
generate_routes(REGSCALE_CLI)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def run_app(port: int = 5555, debug: bool = False):
|
|
60
|
+
"""Run the CLI as a flask app
|
|
61
|
+
:param int port: the port to serve flask on
|
|
62
|
+
:param bool debug: should it be run in debug mode
|
|
63
|
+
"""
|
|
64
|
+
app.run(port=port, debug=debug)
|