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,334 @@
|
|
|
1
|
+
import csv
|
|
2
|
+
import datetime
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import Dict, List, Any, Optional
|
|
5
|
+
|
|
6
|
+
from regscale.core.app import create_logger
|
|
7
|
+
from regscale.core.app.utils.catalog_utils.common import parentheses_to_dot
|
|
8
|
+
from regscale.models import regscale_models as rm, ControlImplementationStatus
|
|
9
|
+
|
|
10
|
+
logger = create_logger()
|
|
11
|
+
# Define a list to hold log entries for CSV output
|
|
12
|
+
log_entries: List[Dict[str, str]] = []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _log_change(action: str, model: str, message: str, changes: Optional[Dict[str, Any]] = None):
|
|
16
|
+
"""
|
|
17
|
+
Log changes to both the logger and a list for later CSV output.
|
|
18
|
+
:param str action: The type of action (e.g., 'Add', 'Update', 'Remove')
|
|
19
|
+
:param str model: The model being changed (e.g., 'Control', 'Objective', 'Parameter')
|
|
20
|
+
:param str message: The message to log
|
|
21
|
+
:param Optional[Dict[str, Any]] changes: Optional changes to log
|
|
22
|
+
"""
|
|
23
|
+
logger.info(message)
|
|
24
|
+
log_entries.append({"action": action, "model": model, "message": message, "changes": str(changes)})
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _write_log_to_csv(filename: str):
|
|
28
|
+
"""
|
|
29
|
+
Write all log entries to a CSV file.
|
|
30
|
+
|
|
31
|
+
:param str filename: The filename for the CSV output
|
|
32
|
+
"""
|
|
33
|
+
entry_count = len(log_entries)
|
|
34
|
+
if entry_count > 0:
|
|
35
|
+
logger.info(f"Writing {entry_count} changes to {filename}")
|
|
36
|
+
with open(filename, "w", newline="") as csvfile:
|
|
37
|
+
fieldnames = ["action", "model", "message", "changes"]
|
|
38
|
+
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
39
|
+
writer.writeheader()
|
|
40
|
+
for entry in log_entries:
|
|
41
|
+
writer.writerow(entry)
|
|
42
|
+
logger.info(f"Successfully wrote all entries to {filename}")
|
|
43
|
+
else:
|
|
44
|
+
logger.info("No changes to write to CSV")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _log_summary():
|
|
48
|
+
"""
|
|
49
|
+
Log statistics about the entries, summarized by action and model.
|
|
50
|
+
"""
|
|
51
|
+
from collections import defaultdict
|
|
52
|
+
from rich.console import Console
|
|
53
|
+
|
|
54
|
+
console = Console()
|
|
55
|
+
summary: defaultdict[Any, int] = defaultdict(int)
|
|
56
|
+
for entry in log_entries:
|
|
57
|
+
key = (entry["action"], entry["model"])
|
|
58
|
+
summary[key] += 1
|
|
59
|
+
|
|
60
|
+
# Print statistics in a table-like format in green color
|
|
61
|
+
console.print("Summary of Changes:", style="green")
|
|
62
|
+
console.print(f"{'Action':<20} {'Model':<20} {'Count':<10}", style="green")
|
|
63
|
+
console.print("-" * 50, style="green")
|
|
64
|
+
for (action, model), count in summary.items():
|
|
65
|
+
console.print(f"{action:<20} {model:<20} {count:<10}", style="green")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def sync_controls(catalog_controls: Dict[str, rm.SecurityControl], ssp_id: int, dry_run: bool = False):
|
|
69
|
+
"""
|
|
70
|
+
Sync controls from a catalog to an SSP
|
|
71
|
+
|
|
72
|
+
:param Dict[str, rm.SecurityControl] catalog_controls: List of controls from the catalog
|
|
73
|
+
:param int ssp_id: SSP ID
|
|
74
|
+
:param bool dry_run: Dry run flag
|
|
75
|
+
"""
|
|
76
|
+
logger.info(f"Syncing controls from catalog to SSP: {ssp_id}")
|
|
77
|
+
|
|
78
|
+
catalog_id_to_label = {x.id: x.controlId for x in catalog_controls.values()}
|
|
79
|
+
|
|
80
|
+
ssp = rm.SecurityPlan.get_object(object_id=ssp_id)
|
|
81
|
+
if not ssp:
|
|
82
|
+
logger.warning("SSP not found")
|
|
83
|
+
exit(1)
|
|
84
|
+
|
|
85
|
+
ssp_controls = {
|
|
86
|
+
parentheses_to_dot(catalog_id_to_label[x.controlID]): x
|
|
87
|
+
for x in rm.ControlImplementation.get_all_by_parent(
|
|
88
|
+
parent_id=ssp.id, parent_module=rm.SecurityPlan.get_module_string()
|
|
89
|
+
)
|
|
90
|
+
if x.controlID in catalog_id_to_label
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Add or update controls
|
|
94
|
+
for control_id, control in catalog_controls.items():
|
|
95
|
+
sync_control(control, control_id, dry_run, ssp, ssp_controls)
|
|
96
|
+
|
|
97
|
+
# Remove controls not in the catalog
|
|
98
|
+
for ssp_control_id, ssp_control in ssp_controls.items():
|
|
99
|
+
if ssp_control.controlID not in catalog_id_to_label:
|
|
100
|
+
_log_change("Remove", "Control", f"Removing control {ssp_control.controlID} from SSP {ssp.systemName}")
|
|
101
|
+
if not dry_run:
|
|
102
|
+
ssp_control.delete()
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def sync_control(
|
|
106
|
+
control: rm.Control,
|
|
107
|
+
control_id: str,
|
|
108
|
+
dry_run: bool,
|
|
109
|
+
ssp: rm.SecurityPlan,
|
|
110
|
+
ssp_controls: Dict[str, rm.ControlImplementation],
|
|
111
|
+
):
|
|
112
|
+
"""
|
|
113
|
+
Sync a control from a catalog to an SSP
|
|
114
|
+
|
|
115
|
+
:param rm.Control control: The control from the catalog
|
|
116
|
+
:param str control_id: The control ID
|
|
117
|
+
:param bool dry_run: Dry run flag
|
|
118
|
+
:param rm.SecurityPlan ssp: The SSP
|
|
119
|
+
:param Dict[str, rm.ControlImplementation] ssp_controls: The controls in the SSP
|
|
120
|
+
"""
|
|
121
|
+
logger.debug(f"Syncing control {control_id}")
|
|
122
|
+
ssp_control = ssp_controls.get(parentheses_to_dot(control_id))
|
|
123
|
+
if ssp_control is None:
|
|
124
|
+
# Create a new control in the SSP
|
|
125
|
+
print(f"Control Owner ID: {ssp.createdById}")
|
|
126
|
+
print(f"API Handler User ID: {rm.ControlImplementation.get_user_id()}")
|
|
127
|
+
ssp_control = rm.ControlImplementation(
|
|
128
|
+
controlOwnerId=ssp.systemOwnerId or ssp.createdById or rm.ControlImplementation.get_user_id(),
|
|
129
|
+
status=ControlImplementationStatus.NotImplemented,
|
|
130
|
+
controlID=control.id,
|
|
131
|
+
parentId=ssp.id,
|
|
132
|
+
parentModule=ssp.get_module_string(),
|
|
133
|
+
)
|
|
134
|
+
_log_change(
|
|
135
|
+
"Add",
|
|
136
|
+
"Control",
|
|
137
|
+
f"Adding control {control.controlId} to SSP {ssp.systemName}",
|
|
138
|
+
changes=ssp_control.show_changes(),
|
|
139
|
+
)
|
|
140
|
+
if not dry_run:
|
|
141
|
+
ssp_control = ssp_control.create()
|
|
142
|
+
else:
|
|
143
|
+
# Update the existing control in the SSP
|
|
144
|
+
if ssp_control.controlOwnerId in ["", None]:
|
|
145
|
+
ssp_control.controlOwnerId = ssp.systemOwnerId or ssp.createdById or rm.ControlImplementation.get_user_id()
|
|
146
|
+
if ssp_control.has_changed():
|
|
147
|
+
_log_change(
|
|
148
|
+
"Update",
|
|
149
|
+
"Control",
|
|
150
|
+
f"Updating control {control.controlId} in SSP {ssp.systemName}",
|
|
151
|
+
changes=ssp_control.show_changes(),
|
|
152
|
+
)
|
|
153
|
+
if not dry_run:
|
|
154
|
+
ssp_control.save()
|
|
155
|
+
sync_objectives(control, dry_run, ssp_control)
|
|
156
|
+
sync_parameters(control, dry_run, ssp_control)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def sync_objectives(control: rm.SecurityControl, dry_run: bool, ssp_control: rm.Control):
|
|
160
|
+
"""
|
|
161
|
+
Sync objectives for a control
|
|
162
|
+
|
|
163
|
+
:param rm.SecurityControl control: The control
|
|
164
|
+
:param bool dry_run: Dry run flag
|
|
165
|
+
:param rm.Control ssp_control: The SSP control
|
|
166
|
+
"""
|
|
167
|
+
ssp_objectives: Dict[int, rm.ImplementationObjective] = { # type: ignore
|
|
168
|
+
x.objectiveId: x
|
|
169
|
+
for x in rm.ImplementationObjective.get_all_by_parent(
|
|
170
|
+
parent_id=ssp_control.id, parent_module=rm.ControlImplementation.get_module_string()
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
control_objectives: Dict[int, rm.ControlObjective] = { # type: ignore
|
|
174
|
+
x.id: x
|
|
175
|
+
for x in rm.ControlObjective.get_all_by_parent(
|
|
176
|
+
parent_id=control.id, parent_module=rm.SecurityControl.get_module_string()
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Remove objectives that are not in the catalog
|
|
181
|
+
for objective_id in ssp_objectives.keys():
|
|
182
|
+
if objective_id not in control_objectives.keys():
|
|
183
|
+
_log_change(
|
|
184
|
+
"Remove",
|
|
185
|
+
"Objective",
|
|
186
|
+
f"Removing objective {objective_id} from control {control.controlId}",
|
|
187
|
+
)
|
|
188
|
+
if not dry_run:
|
|
189
|
+
ssp_objectives[objective_id].delete()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def sync_parameters(control: rm.SecurityControl, dry_run: bool, ssp_control: rm.ControlImplementation):
|
|
193
|
+
"""
|
|
194
|
+
Sync parameters for a control
|
|
195
|
+
|
|
196
|
+
:param rm.SecurityControl control: The control
|
|
197
|
+
:param bool dry_run: Dry run flag
|
|
198
|
+
:param rm.ControlImplementation ssp_control: The SSP control
|
|
199
|
+
"""
|
|
200
|
+
ssp_parameters = get_ssp_parameters(ssp_control)
|
|
201
|
+
control_parameters = get_control_parameters(control)
|
|
202
|
+
|
|
203
|
+
# Add or update parameters
|
|
204
|
+
add_or_update_parameters(control_parameters, ssp_parameters, control, dry_run, ssp_control)
|
|
205
|
+
|
|
206
|
+
# Remove parameters that are not in the catalog
|
|
207
|
+
remove_unused_parameters(ssp_parameters, control_parameters, control, dry_run)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def get_ssp_parameters(ssp_control):
|
|
211
|
+
return {
|
|
212
|
+
x.parentParameterId: x
|
|
213
|
+
for x in rm.Parameter.get_all_by_parent(
|
|
214
|
+
parent_id=ssp_control.id, parent_module=rm.ControlImplementation.get_module_string()
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def get_control_parameters(control):
|
|
220
|
+
return {
|
|
221
|
+
x.id: x
|
|
222
|
+
for x in rm.ControlParameter.get_all_by_parent(
|
|
223
|
+
parent_id=control.id, parent_module=rm.SecurityControl.get_module_string()
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def add_or_update_parameters(control_parameters, ssp_parameters, control, dry_run, ssp_control):
|
|
229
|
+
for control_parameter in control_parameters.values():
|
|
230
|
+
if control_parameter.id in ssp_parameters:
|
|
231
|
+
update_parameter(ssp_parameters[control_parameter.id], control_parameter, control, dry_run)
|
|
232
|
+
else:
|
|
233
|
+
add_parameter(control_parameter, control, dry_run, ssp_control)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def update_parameter(ssp_parameter, control_parameter, control, dry_run):
|
|
237
|
+
ssp_parameter.parentParameterId = control_parameter.id
|
|
238
|
+
ssp_parameter.name = control_parameter.displayName
|
|
239
|
+
ssp_parameter.value = control_parameter.text
|
|
240
|
+
if ssp_parameter.has_changed():
|
|
241
|
+
_log_change(
|
|
242
|
+
"Update",
|
|
243
|
+
"Parameter",
|
|
244
|
+
f"Updating parameter {ssp_parameter.name} in control {control.controlId}",
|
|
245
|
+
changes=ssp_parameter.show_changes(),
|
|
246
|
+
)
|
|
247
|
+
if not dry_run:
|
|
248
|
+
ssp_parameter.save()
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def add_parameter(control_parameter, control, dry_run, ssp_control):
|
|
252
|
+
ssp_parameter = rm.Parameter(
|
|
253
|
+
uuid=uuid.uuid4().__str__(),
|
|
254
|
+
name=control_parameter.displayName,
|
|
255
|
+
value=control_parameter.text or "",
|
|
256
|
+
controlImplementationId=ssp_control.id,
|
|
257
|
+
parentParameterId=control_parameter.id,
|
|
258
|
+
)
|
|
259
|
+
_log_change(
|
|
260
|
+
"Add",
|
|
261
|
+
"Parameter",
|
|
262
|
+
f"Adding parameter {ssp_parameter.name} to control {control.controlId}",
|
|
263
|
+
changes=ssp_parameter.show_changes(),
|
|
264
|
+
)
|
|
265
|
+
if not dry_run:
|
|
266
|
+
ssp_parameter = ssp_parameter.create()
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def remove_unused_parameters(ssp_parameters, control_parameters, control, dry_run):
|
|
270
|
+
for parameter_id in ssp_parameters.keys():
|
|
271
|
+
if parameter_id not in control_parameters:
|
|
272
|
+
_log_change(
|
|
273
|
+
"Remove",
|
|
274
|
+
"Parameter",
|
|
275
|
+
f"Removing parameter {parameter_id} from control {control.controlId}",
|
|
276
|
+
)
|
|
277
|
+
if not dry_run:
|
|
278
|
+
ssp_parameters[parameter_id].delete()
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def get_controls_from_profile(ssp_id: int) -> Dict[str, rm.SecurityControl]:
|
|
282
|
+
"""
|
|
283
|
+
Get controls from a profile
|
|
284
|
+
|
|
285
|
+
:param int ssp_id: The SSP ID
|
|
286
|
+
:return: The controls
|
|
287
|
+
:rtype: Dict[str, rm.SecurityControl]
|
|
288
|
+
"""
|
|
289
|
+
logger.info(f"Getting controls from profile: {ssp_id}")
|
|
290
|
+
profile_links = rm.ProfileLink.get_all_by_parent(
|
|
291
|
+
parent_id=ssp_id, parent_module=rm.SecurityPlan.get_module_string()
|
|
292
|
+
)
|
|
293
|
+
controls: List[rm.SecurityControl] = []
|
|
294
|
+
control_get_count = 0
|
|
295
|
+
for profile_link in profile_links:
|
|
296
|
+
profile_mappings: list[rm.ProfileMapping] = rm.ProfileMapping.get_all_by_parent(
|
|
297
|
+
parent_id=profile_link.profileId, parent_module=rm.Profile.get_module_string()
|
|
298
|
+
)
|
|
299
|
+
for profile_mapping in profile_mappings:
|
|
300
|
+
control = rm.SecurityControl.get_object(object_id=profile_mapping.controlID)
|
|
301
|
+
if control:
|
|
302
|
+
print(control.controlId, end=" ")
|
|
303
|
+
controls.append(control)
|
|
304
|
+
control_get_count += 1
|
|
305
|
+
if control_get_count % 10 == 0:
|
|
306
|
+
print()
|
|
307
|
+
else:
|
|
308
|
+
logger.warning(f"Control {profile_mapping.controlID} not found")
|
|
309
|
+
return {parentheses_to_dot(x.controlId): x for x in controls}
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def sync_plan_controls(ssp_id: int, dry_run: bool = False):
|
|
313
|
+
"""
|
|
314
|
+
Sync controls from a profile to an SSP
|
|
315
|
+
|
|
316
|
+
:param int ssp_id: The SSP ID
|
|
317
|
+
:param bool dry_run: Dry run flag
|
|
318
|
+
"""
|
|
319
|
+
logger.info(f"Syncing controls from profile to SSP: {ssp_id}")
|
|
320
|
+
log_entries.clear()
|
|
321
|
+
catalog_controls = get_controls_from_profile(ssp_id=ssp_id)
|
|
322
|
+
sync_controls(catalog_controls=catalog_controls, ssp_id=ssp_id, dry_run=dry_run)
|
|
323
|
+
_write_log_to_csv(f"plan_{ssp_id}_log_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.csv")
|
|
324
|
+
_log_summary()
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def sync_all_plans(dry_run: bool = False):
|
|
328
|
+
"""
|
|
329
|
+
Sync all plans
|
|
330
|
+
|
|
331
|
+
:param bool dry_run: Dry run flag
|
|
332
|
+
"""
|
|
333
|
+
for ssp in rm.SecurityPlan.get_list(): # type: rm.SecurityPlan
|
|
334
|
+
sync_plan_controls(ssp.id, dry_run)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utility functions for working with files and folders.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Union, List, Iterator, Optional
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class S3FileDownloadError(Exception):
|
|
12
|
+
"""
|
|
13
|
+
Exception raised when an error occurs during S3 file downloadß
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def is_s3_path(path: Union[str, Path]) -> bool:
|
|
20
|
+
"""
|
|
21
|
+
Check if the given path is an S3 URI.
|
|
22
|
+
|
|
23
|
+
:param Union[str, Path] path: The path to check
|
|
24
|
+
:return: True if the path is an S3 URI, False otherwise
|
|
25
|
+
:rtype: bool
|
|
26
|
+
"""
|
|
27
|
+
return isinstance(path, str) and path.startswith("s3://")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def read_file(file_path: Union[str, Path]) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Read a file from local filesystem or S3.
|
|
33
|
+
|
|
34
|
+
:param Union[str, Path] file_path: Path to the file or S3 URI
|
|
35
|
+
:return: Content of the file
|
|
36
|
+
:rtype: str
|
|
37
|
+
"""
|
|
38
|
+
import smart_open # type: ignore # Optimize import performance
|
|
39
|
+
|
|
40
|
+
with smart_open.open(str(file_path), "r") as f:
|
|
41
|
+
return f.read()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def find_files(path: Union[str, Path], pattern: str) -> List[Union[Path, str]]:
|
|
45
|
+
"""
|
|
46
|
+
Find all files matching the pattern in the given path, including S3.
|
|
47
|
+
|
|
48
|
+
:param Union[str, Path] path: Path to a file, a folder, or an S3 URI
|
|
49
|
+
:param str pattern: File pattern to match (e.g., "*.nessus")
|
|
50
|
+
:return: List of Path objects for matching files or S3 URIs
|
|
51
|
+
:rtype: List[Union[Path, str]]
|
|
52
|
+
"""
|
|
53
|
+
import boto3 # type: ignore # Optimize import performance
|
|
54
|
+
|
|
55
|
+
if is_s3_path(path):
|
|
56
|
+
s3_parts = path[5:].split("/", 1)
|
|
57
|
+
bucket = s3_parts[0]
|
|
58
|
+
prefix = s3_parts[1] if len(s3_parts) > 1 else ""
|
|
59
|
+
|
|
60
|
+
s3 = boto3.client("s3")
|
|
61
|
+
paginator = s3.get_paginator("list_objects_v2")
|
|
62
|
+
|
|
63
|
+
files: List[Union[Path, str]] = []
|
|
64
|
+
for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
|
|
65
|
+
for obj in page.get("Contents", []):
|
|
66
|
+
if obj["Key"].endswith(pattern.lstrip("*")):
|
|
67
|
+
files.append(f"s3://{bucket}/{obj['Key']}")
|
|
68
|
+
return files
|
|
69
|
+
|
|
70
|
+
file_path = Path(path)
|
|
71
|
+
if file_path.is_file():
|
|
72
|
+
return [file_path] if file_path.match(pattern) else []
|
|
73
|
+
return list(file_path.glob(pattern))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def move_file(src: Union[str, Path], dst: Union[str, Path]) -> None:
|
|
77
|
+
"""
|
|
78
|
+
Move a file from src to dst. Works with local files and S3.
|
|
79
|
+
|
|
80
|
+
:param Union[str, Path] src: Source file path or S3 URI
|
|
81
|
+
:param Union[str, Path] dst: Destination file path or S3 URI
|
|
82
|
+
"""
|
|
83
|
+
import smart_open # type: ignore # Optimize import performance
|
|
84
|
+
|
|
85
|
+
if is_s3_path(src):
|
|
86
|
+
import boto3 # type: ignore # Optimize import performance
|
|
87
|
+
|
|
88
|
+
# S3 to S3 move
|
|
89
|
+
if is_s3_path(dst):
|
|
90
|
+
s3 = boto3.client("s3")
|
|
91
|
+
src_parts = src[5:].split("/", 1)
|
|
92
|
+
dst_parts = dst[5:].split("/", 1)
|
|
93
|
+
s3.copy_object(
|
|
94
|
+
CopySource={"Bucket": src_parts[0], "Key": src_parts[1]}, Bucket=dst_parts[0], Key=dst_parts[1]
|
|
95
|
+
)
|
|
96
|
+
s3.delete_object(Bucket=src_parts[0], Key=src_parts[1])
|
|
97
|
+
else:
|
|
98
|
+
# S3 to local
|
|
99
|
+
with smart_open.open(src, "rb") as s_file, smart_open.open(dst, "wb") as d_file:
|
|
100
|
+
d_file.write(s_file.read())
|
|
101
|
+
s3 = boto3.client("s3")
|
|
102
|
+
src_parts = src[5:].split("/", 1)
|
|
103
|
+
s3.delete_object(Bucket=src_parts[0], Key=src_parts[1])
|
|
104
|
+
else:
|
|
105
|
+
# Local to local or local to S3
|
|
106
|
+
with smart_open.open(src, "rb") as s_file, smart_open.open(dst, "wb") as d_file:
|
|
107
|
+
d_file.write(s_file.read())
|
|
108
|
+
if not isinstance(dst, str) or not dst.startswith("s3://"):
|
|
109
|
+
os.remove(src)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def iterate_files(file_collection: List[Union[Path, str]]) -> Iterator[Union[Path, str]]:
|
|
113
|
+
"""
|
|
114
|
+
Iterate over a collection of files, yielding each file path.
|
|
115
|
+
|
|
116
|
+
:param List[Union[Path, str]] file_collection: List of file paths or S3 URIs
|
|
117
|
+
:yield: Each file path or S3 URI
|
|
118
|
+
:rtype: Iterator[Union[Path, str]]
|
|
119
|
+
"""
|
|
120
|
+
for file in file_collection:
|
|
121
|
+
yield file
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_processed_file_path(file_path: Union[str, Path], processed_folder: str = "processed") -> Union[str, Path]:
|
|
125
|
+
"""
|
|
126
|
+
Generate a path for the processed file, handling both local and S3 paths.
|
|
127
|
+
|
|
128
|
+
:param Union[str, Path] file_path: Original file path or S3 URI
|
|
129
|
+
:param str processed_folder: Name of the folder for processed files (default: "processed")
|
|
130
|
+
:return: Path or S3 URI for the processed file
|
|
131
|
+
:rtype: Union[str, Path]
|
|
132
|
+
"""
|
|
133
|
+
if is_s3_path(file_path):
|
|
134
|
+
s3_parts = file_path[5:].split("/") # type: ignore # is_s3_path ensures string
|
|
135
|
+
bucket = s3_parts[0]
|
|
136
|
+
key = "/".join(s3_parts[1:])
|
|
137
|
+
new_key = f"processed/{os.path.basename(key)}"
|
|
138
|
+
return f"s3://{bucket}/{new_key}"
|
|
139
|
+
else:
|
|
140
|
+
file_path = Path(file_path)
|
|
141
|
+
timestamp = datetime.now().strftime("%Y%m%d-%I%M%S%p")
|
|
142
|
+
new_filename = f"{file_path.stem}_{timestamp}{file_path.suffix}".replace(" ", "_")
|
|
143
|
+
new_path = file_path.parent / processed_folder / new_filename
|
|
144
|
+
os.makedirs(new_path.parent, exist_ok=True)
|
|
145
|
+
return new_path
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_files_by_folder(
|
|
149
|
+
import_folder: Union[str, Path],
|
|
150
|
+
exclude_non_scan_files: bool,
|
|
151
|
+
file_excludes: Optional[list[str]] = None,
|
|
152
|
+
directory_excludes: Optional[list[str]] = None,
|
|
153
|
+
) -> List[str]:
|
|
154
|
+
"""
|
|
155
|
+
Retrieves a list of file paths from a specified folder, excluding empty files or files that match the excludes list.
|
|
156
|
+
|
|
157
|
+
:param Union[str, Path] import_folder: The path to the folder from which to retrieve file paths.
|
|
158
|
+
:param bool exclude_non_scan_files: exclude files that are not scan files
|
|
159
|
+
:param Optional[List[str]] file_excludes: List of file extensions to exclude from the list of files
|
|
160
|
+
:param Optional[List[str]] directory_excludes: List of directories to exclude from the list of files
|
|
161
|
+
:return: A list of file paths for all non-empty files in the specified folder and subsequent subfolders.
|
|
162
|
+
:rtype: List[str]
|
|
163
|
+
"""
|
|
164
|
+
if file_excludes is None:
|
|
165
|
+
file_excludes = []
|
|
166
|
+
if directory_excludes is None:
|
|
167
|
+
directory_excludes = []
|
|
168
|
+
file_path_list = []
|
|
169
|
+
if not os.path.isdir(import_folder):
|
|
170
|
+
from regscale.core.app.logz import create_logger
|
|
171
|
+
|
|
172
|
+
logger = create_logger()
|
|
173
|
+
logger.error(f"Folder '{import_folder}' does not exist.")
|
|
174
|
+
return file_path_list
|
|
175
|
+
for root, dir, files in os.walk(import_folder):
|
|
176
|
+
for file in files:
|
|
177
|
+
file_path = os.path.join(root, file)
|
|
178
|
+
if any(exclude_str in file_path for exclude_str in directory_excludes):
|
|
179
|
+
continue
|
|
180
|
+
if any(exclude_str in file for exclude_str in file_excludes) and exclude_non_scan_files:
|
|
181
|
+
continue
|
|
182
|
+
if os.path.getsize(file_path) == 0:
|
|
183
|
+
continue
|
|
184
|
+
file_path_list.append(os.path.join(root, file))
|
|
185
|
+
return file_path_list
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def download_from_s3(bucket: str, prefix: str, local_path: Union[str, os.PathLike], aws_profile: str) -> None:
|
|
189
|
+
"""
|
|
190
|
+
Downloads files from an S3 bucket to a local directory.
|
|
191
|
+
|
|
192
|
+
:param str bucket: Name of the S3 bucket
|
|
193
|
+
:param str prefix: Prefix (folder path) within the bucket
|
|
194
|
+
:param Union[str, PathLike] local_path: Local directory to download files to
|
|
195
|
+
:param str aws_profile: AWS profile to use for S3 access
|
|
196
|
+
:rtype: None
|
|
197
|
+
"""
|
|
198
|
+
import boto3
|
|
199
|
+
import logging
|
|
200
|
+
from botocore.exceptions import ClientError
|
|
201
|
+
|
|
202
|
+
logger = logging.getLogger(__name__)
|
|
203
|
+
|
|
204
|
+
session = boto3.Session(profile_name=aws_profile)
|
|
205
|
+
s3_client = session.client("s3")
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
# Create local directory if it doesn't exist
|
|
209
|
+
os.makedirs(local_path, exist_ok=True)
|
|
210
|
+
|
|
211
|
+
# List objects in bucket with given prefix
|
|
212
|
+
paginator = s3_client.get_paginator("list_objects_v2")
|
|
213
|
+
for page in paginator.paginate(Bucket=bucket, Prefix=prefix):
|
|
214
|
+
if "Contents" not in page:
|
|
215
|
+
continue
|
|
216
|
+
|
|
217
|
+
for obj in page["Contents"]:
|
|
218
|
+
# Skip if object is a directory (ends with /)
|
|
219
|
+
if obj["Key"].endswith("/"):
|
|
220
|
+
continue
|
|
221
|
+
|
|
222
|
+
# Create the full local path, preserving directory structure
|
|
223
|
+
relative_path = obj["Key"]
|
|
224
|
+
if prefix:
|
|
225
|
+
# Remove the prefix from the key to get the relative path
|
|
226
|
+
relative_path = relative_path[len(prefix) :].lstrip("/")
|
|
227
|
+
|
|
228
|
+
local_file_path = os.path.join(local_path, relative_path)
|
|
229
|
+
|
|
230
|
+
# Create the directory structure if it doesn't exist
|
|
231
|
+
os.makedirs(os.path.dirname(local_file_path), exist_ok=True)
|
|
232
|
+
|
|
233
|
+
logger.info(f"Downloading {obj['Key']} to {local_file_path}")
|
|
234
|
+
s3_client.download_file(bucket, obj["Key"], local_file_path)
|
|
235
|
+
|
|
236
|
+
except ClientError as e:
|
|
237
|
+
logger.error(f"Error downloading from S3: {str(e)}")
|
|
238
|
+
raise S3FileDownloadError(f"Failed to download files from S3: {str(e)}")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from regscale.core.utils.date import datetime_str, date_str
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def safe_float(value: Any, default: float = 0.0, field_name: str = "value") -> float:
|
|
11
|
+
"""
|
|
12
|
+
Safely convert any value to a float.
|
|
13
|
+
|
|
14
|
+
:param Any value: The value to convert
|
|
15
|
+
:param float default: The default value to return if conversion fails
|
|
16
|
+
:param str field_name: The name of the field being parsed (for logging purposes)
|
|
17
|
+
:return: The parsed float value or the default value
|
|
18
|
+
:rtype: float
|
|
19
|
+
"""
|
|
20
|
+
if value is None:
|
|
21
|
+
return default
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
return float(value)
|
|
25
|
+
except (ValueError, TypeError):
|
|
26
|
+
logger.warning(f"Invalid float {field_name}: {value}. Defaulting to {default}")
|
|
27
|
+
return default
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def safe_int(value: Any, default: int = 0, field_name: str = "value") -> int:
|
|
31
|
+
"""
|
|
32
|
+
Safely convert any value to an integer.
|
|
33
|
+
|
|
34
|
+
:param Any value: The value to convert
|
|
35
|
+
:param int default: The default value to return if conversion fails
|
|
36
|
+
:param str field_name: The name of the field being parsed (for logging purposes)
|
|
37
|
+
:return: The parsed integer value or the default value
|
|
38
|
+
:rtype: int
|
|
39
|
+
"""
|
|
40
|
+
if value is None:
|
|
41
|
+
return default
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
return int(value)
|
|
45
|
+
except (ValueError, TypeError):
|
|
46
|
+
logger.warning(f"Invalid integer {field_name}: {value}. Defaulting to {default}")
|
|
47
|
+
return default
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def safe_datetime_str(
|
|
51
|
+
value: Any, default: datetime.datetime = datetime.datetime.now(), date_format: Optional[str] = None
|
|
52
|
+
) -> str:
|
|
53
|
+
"""
|
|
54
|
+
Safely convert any value to a datetime.
|
|
55
|
+
|
|
56
|
+
:param Any value: The value to convert
|
|
57
|
+
:param datetime default: The default value to return if conversion fails
|
|
58
|
+
:param Optional[str] date_format: The date format to use for the datetime string, defaults to None
|
|
59
|
+
:return: The parsed datetime value or the default value
|
|
60
|
+
:rtype: str
|
|
61
|
+
"""
|
|
62
|
+
value = datetime_str(value, date_format)
|
|
63
|
+
if not value:
|
|
64
|
+
value = datetime_str(default, date_format)
|
|
65
|
+
return value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def safe_date_str(value: Any, default: datetime.date = datetime.date.today()) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Safely convert any value to a date string.
|
|
71
|
+
|
|
72
|
+
:param Any value: The value to convert
|
|
73
|
+
:param date default: The default value to return if conversion fails
|
|
74
|
+
:return: The parsed date string value or the default value
|
|
75
|
+
|
|
76
|
+
:rtype: str
|
|
77
|
+
"""
|
|
78
|
+
value = date_str(value)
|
|
79
|
+
if not value:
|
|
80
|
+
value = date_str(default)
|
|
81
|
+
return value
|