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,720 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Qualys Scan information
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
# pylint: disable=C0415
|
|
9
|
+
import re
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from typing import Any, Iterator, Optional, TypeVar, TextIO
|
|
12
|
+
|
|
13
|
+
from openpyxl.reader.excel import load_workbook
|
|
14
|
+
|
|
15
|
+
from regscale.core.app import create_logger
|
|
16
|
+
from regscale.core.app.application import Application
|
|
17
|
+
from regscale.core.app.utils.app_utils import get_current_datetime
|
|
18
|
+
from regscale.core.app.utils.parser_utils import safe_int, safe_float
|
|
19
|
+
from regscale.core.utils.date import date_str, datetime_str
|
|
20
|
+
from regscale.integrations.scanner_integration import IntegrationAsset
|
|
21
|
+
from regscale.models import (
|
|
22
|
+
Asset,
|
|
23
|
+
AssetCategory,
|
|
24
|
+
AssetType,
|
|
25
|
+
ImportValidater,
|
|
26
|
+
IssueSeverity,
|
|
27
|
+
SecurityPlan,
|
|
28
|
+
Vulnerability,
|
|
29
|
+
VulnerabilityStatus,
|
|
30
|
+
AssetStatus,
|
|
31
|
+
VulnerabilitySeverity,
|
|
32
|
+
)
|
|
33
|
+
from regscale.models.integration_models.flat_file_importer import FlatFileImporter
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
T = TypeVar("T")
|
|
38
|
+
QG_HOST_ID = "QG Host ID"
|
|
39
|
+
CVE_ID = "CVE ID"
|
|
40
|
+
SEVERITY = "Severity"
|
|
41
|
+
EXPLOITABILITY = "Exploitability"
|
|
42
|
+
SOLUTION = "Solution"
|
|
43
|
+
DNS = "DNS"
|
|
44
|
+
IP = "IP"
|
|
45
|
+
OS = "OS"
|
|
46
|
+
NETBIOS = "NetBIOS"
|
|
47
|
+
FQDN = "FQDN"
|
|
48
|
+
IMAGE_ID_FIELD = "IMAGE ID"
|
|
49
|
+
CVE_ID_FIELD = "CVE ID"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Qualys(FlatFileImporter):
|
|
53
|
+
"""Qualys Scan information"""
|
|
54
|
+
|
|
55
|
+
title = "Qualys Scanner Export Integration"
|
|
56
|
+
asset_identifier_field = "name"
|
|
57
|
+
|
|
58
|
+
def __init__(self, **kwargs):
|
|
59
|
+
self.name = kwargs.get("name")
|
|
60
|
+
self.vuln_title = "Title"
|
|
61
|
+
self.fmt = "%Y-%m-%d"
|
|
62
|
+
self.dt_format = "%Y-%m-%d %H:%M:%S"
|
|
63
|
+
self.required_headers = [
|
|
64
|
+
SEVERITY,
|
|
65
|
+
self.vuln_title,
|
|
66
|
+
EXPLOITABILITY,
|
|
67
|
+
CVE_ID,
|
|
68
|
+
SOLUTION,
|
|
69
|
+
DNS,
|
|
70
|
+
IP,
|
|
71
|
+
QG_HOST_ID,
|
|
72
|
+
OS,
|
|
73
|
+
NETBIOS,
|
|
74
|
+
FQDN,
|
|
75
|
+
]
|
|
76
|
+
logger = create_logger()
|
|
77
|
+
skip_rows = kwargs.pop("skip_rows")
|
|
78
|
+
self.mapping_file = kwargs.get("mappings_path")
|
|
79
|
+
self.disable_mapping = kwargs.get("disable_mapping")
|
|
80
|
+
self.validater = ImportValidater(
|
|
81
|
+
self.required_headers, kwargs.get("file_path"), self.mapping_file, self.disable_mapping, skip_rows=skip_rows
|
|
82
|
+
)
|
|
83
|
+
self.headers = self.validater.parsed_headers
|
|
84
|
+
self.mapping = self.validater.mapping
|
|
85
|
+
super().__init__(
|
|
86
|
+
logger=logger,
|
|
87
|
+
app=Application(),
|
|
88
|
+
headers=self.headers,
|
|
89
|
+
header_line_number=skip_rows,
|
|
90
|
+
asset_func=self.create_asset,
|
|
91
|
+
vuln_func=self.create_vuln,
|
|
92
|
+
**kwargs,
|
|
93
|
+
)
|
|
94
|
+
# header is line# 11
|
|
95
|
+
# start self.file_data from line #12
|
|
96
|
+
|
|
97
|
+
def create_asset(self, dat: Optional[dict] = None) -> Optional[Asset]:
|
|
98
|
+
"""
|
|
99
|
+
Create an asset from a row in the Qualys file
|
|
100
|
+
|
|
101
|
+
:param Optional[dict] dat: Data row from CSV file
|
|
102
|
+
:return: RegScale Issue object or None
|
|
103
|
+
:rtype: Optional[Asset]
|
|
104
|
+
"""
|
|
105
|
+
return Asset(
|
|
106
|
+
**{
|
|
107
|
+
"id": 0,
|
|
108
|
+
"name": self.mapping.get_value(dat, DNS),
|
|
109
|
+
"ipAddress": self.mapping.get_value(dat, IP),
|
|
110
|
+
"isPublic": True,
|
|
111
|
+
"status": "Active (On Network)",
|
|
112
|
+
"assetCategory": "Hardware",
|
|
113
|
+
"qualysId": self.mapping.get_value(dat, QG_HOST_ID), # UUID from Nessus HostProperties tag
|
|
114
|
+
"bLatestScan": True,
|
|
115
|
+
"bAuthenticatedScan": True,
|
|
116
|
+
"scanningTool": "Qualys",
|
|
117
|
+
"assetOwnerId": self.attributes.app.config["userId"],
|
|
118
|
+
"netBIOS": self.mapping.get_value(dat, NETBIOS),
|
|
119
|
+
"assetType": "Other",
|
|
120
|
+
"fqdn": self.mapping.get_value(dat, FQDN),
|
|
121
|
+
"operatingSystem": Asset.find_os(self.mapping.get_value(dat, OS)),
|
|
122
|
+
"operatingSystemVersion": self.mapping.get_value(dat, OS),
|
|
123
|
+
"systemAdministratorId": self.attributes.app.config["userId"],
|
|
124
|
+
"parentId": self.attributes.parent_id,
|
|
125
|
+
"parentModule": self.attributes.parent_module,
|
|
126
|
+
}
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Create a vuln from a row in the Qualys file
|
|
132
|
+
|
|
133
|
+
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
134
|
+
:rtype: None
|
|
135
|
+
"""
|
|
136
|
+
from regscale.integrations.commercial.qualys import map_qualys_severity_to_regscale
|
|
137
|
+
|
|
138
|
+
dns: str = self.mapping.get_value(dat, DNS)
|
|
139
|
+
other_id: str = self.mapping.get_value(dat, QG_HOST_ID)
|
|
140
|
+
distro: str = self.mapping.get_value(dat, OS)
|
|
141
|
+
cve: str = self.mapping.get_value(dat, CVE_ID)
|
|
142
|
+
description: str = self.mapping.get_value(dat, "Threat")
|
|
143
|
+
title = self.mapping.get_value(dat, self.vuln_title)
|
|
144
|
+
regscale_vuln = None
|
|
145
|
+
severity = self.mapping.get_value(dat, SEVERITY)
|
|
146
|
+
regscale_severity = map_qualys_severity_to_regscale(int(severity))[1]
|
|
147
|
+
config = self.attributes.app.config
|
|
148
|
+
asset_match = [asset for asset in self.data["assets"] if asset.name == dns]
|
|
149
|
+
asset = asset_match[0] if asset_match else None
|
|
150
|
+
if dat and asset_match:
|
|
151
|
+
regscale_vuln = Vulnerability(
|
|
152
|
+
id=0,
|
|
153
|
+
scanId=0, # set later
|
|
154
|
+
parentId=asset.id,
|
|
155
|
+
parentModule="assets",
|
|
156
|
+
ipAddress=self.mapping.get_value(dat, IP),
|
|
157
|
+
lastSeen=self.mapping.get_value(dat, "Last Detected"),
|
|
158
|
+
firstSeen=self.mapping.get_value(dat, "First Detected"),
|
|
159
|
+
daysOpen=None,
|
|
160
|
+
dns=self.mapping.get_value(dat, DNS, other_id),
|
|
161
|
+
mitigated=None,
|
|
162
|
+
operatingSystem=(Asset.find_os(distro) if Asset.find_os(distro) else None),
|
|
163
|
+
severity=regscale_severity,
|
|
164
|
+
plugInName=self.mapping.get_value(dat, self.vuln_title),
|
|
165
|
+
plugInId=self.mapping.get_value(dat, "QID"),
|
|
166
|
+
cve=cve,
|
|
167
|
+
vprScore=None,
|
|
168
|
+
cvsSv3BaseScore=self.extract_float(self.mapping.get_value(dat, "CVSS3.1 Base", 0.0)),
|
|
169
|
+
tenantsId=0,
|
|
170
|
+
title=title,
|
|
171
|
+
description=description,
|
|
172
|
+
plugInText=title,
|
|
173
|
+
createdById=config["userId"],
|
|
174
|
+
lastUpdatedById=config["userId"],
|
|
175
|
+
dateCreated=get_current_datetime(),
|
|
176
|
+
)
|
|
177
|
+
return regscale_vuln
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def extract_float(s: str) -> Any:
|
|
181
|
+
"""
|
|
182
|
+
Extract a float from a string
|
|
183
|
+
|
|
184
|
+
:param str s: String to extract float from
|
|
185
|
+
:return: Float extracted from string or None
|
|
186
|
+
:rtype: Any
|
|
187
|
+
"""
|
|
188
|
+
matches = re.findall(r"[-+]?[0-9]*\.?[0-9]+", s)
|
|
189
|
+
if matches:
|
|
190
|
+
return float(matches[0])
|
|
191
|
+
else:
|
|
192
|
+
return None
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class QualysContainerScansImporter(FlatFileImporter): # (ScannerIntegration):
|
|
196
|
+
"""
|
|
197
|
+
Import Qualys Container Scans data
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
plan_id = 0
|
|
201
|
+
# asset_identifier_field: str = "otherTrackingNumber"
|
|
202
|
+
threat_field = "THREAT"
|
|
203
|
+
severity_field = "SEVERITY"
|
|
204
|
+
title_field = "TITLE"
|
|
205
|
+
asset_notes_field = "IMAGE LABEL"
|
|
206
|
+
cve_v3_base = "CVSS3 BASE"
|
|
207
|
+
cve_base = "CVSS BASE"
|
|
208
|
+
mitigation_field = "SOLUTION"
|
|
209
|
+
external_id_field = "QID"
|
|
210
|
+
first_seen_field = "CREATED ON"
|
|
211
|
+
last_seen_field = "UPDATED"
|
|
212
|
+
container_id = "IMAGE UUID"
|
|
213
|
+
records = []
|
|
214
|
+
|
|
215
|
+
def __init__(self, **kwargs: dict):
|
|
216
|
+
self.asset_identifier_field = "otherTrackingNumber"
|
|
217
|
+
self.name = kwargs.get("name")
|
|
218
|
+
self.fmt = "%Y-%m-%d"
|
|
219
|
+
self.dt_format = "%Y-%m-%d %H:%M:%S"
|
|
220
|
+
self.plan_id = kwargs.get("parent_id")
|
|
221
|
+
self.required_headers = [
|
|
222
|
+
self.severity_field,
|
|
223
|
+
self.title_field,
|
|
224
|
+
self.asset_notes_field,
|
|
225
|
+
self.threat_field,
|
|
226
|
+
CVE_ID_FIELD,
|
|
227
|
+
self.mitigation_field,
|
|
228
|
+
self.cve_v3_base,
|
|
229
|
+
self.cve_base,
|
|
230
|
+
self.external_id_field,
|
|
231
|
+
self.first_seen_field,
|
|
232
|
+
self.last_seen_field,
|
|
233
|
+
self.container_id,
|
|
234
|
+
]
|
|
235
|
+
self.file_path: str = kwargs.get("file_path")
|
|
236
|
+
skip_rows = kwargs.pop("skip_rows")
|
|
237
|
+
self.mapping_file = kwargs.get("mappings_path")
|
|
238
|
+
self.disable_mapping = kwargs.get("disable_mapping")
|
|
239
|
+
self.validater = ImportValidater(
|
|
240
|
+
required_headers=self.required_headers,
|
|
241
|
+
file_path=self.file_path,
|
|
242
|
+
mapping_file_path=self.mapping_file,
|
|
243
|
+
disable_mapping=self.disable_mapping,
|
|
244
|
+
skip_rows=skip_rows,
|
|
245
|
+
)
|
|
246
|
+
self.headers = self.validater.parsed_headers
|
|
247
|
+
self.header = self.headers
|
|
248
|
+
self.mapping = self.validater.mapping
|
|
249
|
+
# super().__init__(plan_id=self.plan_id)
|
|
250
|
+
# self.import_data()
|
|
251
|
+
kwargs["asset_identifier_field"] = "otherTrackingNumber"
|
|
252
|
+
super().__init__(
|
|
253
|
+
logger=logger,
|
|
254
|
+
app=Application(),
|
|
255
|
+
headers=self.required_headers,
|
|
256
|
+
header_line_number=skip_rows,
|
|
257
|
+
asset_func=self.create_asset,
|
|
258
|
+
vuln_func=self.create_vuln,
|
|
259
|
+
**kwargs,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[Asset]:
|
|
263
|
+
"""
|
|
264
|
+
Fetch assets from the Qualys CSV file
|
|
265
|
+
|
|
266
|
+
:return: Iterator of IntegrationAsset objects
|
|
267
|
+
:rtype: Iterator[IntegrationAsset]
|
|
268
|
+
"""
|
|
269
|
+
bad_ids = ["", "0", "None", "Unknown"]
|
|
270
|
+
if self.mapping.get_value(row, self.container_id) in bad_ids:
|
|
271
|
+
return None
|
|
272
|
+
max_length = 450 # max length of asset name
|
|
273
|
+
asset = Asset(
|
|
274
|
+
name=self.mapping.get_value(row, self.asset_notes_field)[:max_length] or "Unknown",
|
|
275
|
+
notes=self.mapping.get_value(row, self.asset_notes_field),
|
|
276
|
+
otherTrackingNumber=self.mapping.get_value(row, self.container_id),
|
|
277
|
+
# identifier=self.mapping.get_value(row, IMAGE_ID_FIELD),
|
|
278
|
+
assetType=AssetType.VM.value,
|
|
279
|
+
assetCategory=AssetCategory.Software.value,
|
|
280
|
+
parentId=safe_int(self.plan_id),
|
|
281
|
+
parentModule=SecurityPlan.get_module_slug(),
|
|
282
|
+
status=AssetStatus.Active,
|
|
283
|
+
)
|
|
284
|
+
return asset
|
|
285
|
+
|
|
286
|
+
def handle_integration_date(self, input_date_str: str) -> str:
|
|
287
|
+
"""
|
|
288
|
+
Handle the integration date to ingest to date and back to string to get into the correct format if needed.
|
|
289
|
+
|
|
290
|
+
:param str date_str: Date string
|
|
291
|
+
:return: Date string
|
|
292
|
+
:rtype: str
|
|
293
|
+
"""
|
|
294
|
+
date_obj_value = datetime.strptime(
|
|
295
|
+
input_date_str,
|
|
296
|
+
"%Y-%m-%d %H:%M:%S %z %Z",
|
|
297
|
+
)
|
|
298
|
+
return date_str(date_obj_value, self.dt_format)
|
|
299
|
+
|
|
300
|
+
def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
|
|
301
|
+
"""
|
|
302
|
+
Fetch vulnerabilities from the Qualys CSV file
|
|
303
|
+
|
|
304
|
+
:return: Iterator of IntegrationFinding objects
|
|
305
|
+
:rtype: Vulnerability
|
|
306
|
+
"""
|
|
307
|
+
|
|
308
|
+
if self.mapping.get_value(row, CVE_ID_FIELD) and isinstance(self.mapping.get_value(row, CVE_ID_FIELD), str):
|
|
309
|
+
for cve_id in self.mapping.get_value(row, CVE_ID_FIELD, "").split(","):
|
|
310
|
+
finding = Vulnerability(
|
|
311
|
+
title=self.mapping.get_value(row, self.title_field),
|
|
312
|
+
description=self.mapping.get_value(row, self.threat_field),
|
|
313
|
+
cve=cve_id,
|
|
314
|
+
severity=severity_to_regscale(self.mapping.get_value(row, self.severity_field)),
|
|
315
|
+
status=VulnerabilityStatus.Open,
|
|
316
|
+
cvsSv3BaseScore=safe_float(self.mapping.get_value(row, self.cve_v3_base, 0.0)),
|
|
317
|
+
vprScore=safe_float(self.mapping.get_value(row, self.cve_base, 0.0)),
|
|
318
|
+
firstSeen=self.handle_integration_date(
|
|
319
|
+
self.mapping.get_value(row, self.first_seen_field, get_current_datetime())
|
|
320
|
+
),
|
|
321
|
+
lastSeen=self.handle_integration_date(
|
|
322
|
+
self.mapping.get_value(row, self.last_seen_field, get_current_datetime())
|
|
323
|
+
),
|
|
324
|
+
plugInName=cve_id,
|
|
325
|
+
dns=self.mapping.get_value(row, self.container_id), # really asset_identifier
|
|
326
|
+
)
|
|
327
|
+
return finding
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class QualysWasScansImporter(FlatFileImporter):
|
|
331
|
+
"""
|
|
332
|
+
Import Qualys Container Scans data
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
plan_id = 0
|
|
336
|
+
threat_field = "THREAT"
|
|
337
|
+
severity_field = "SEVERITY"
|
|
338
|
+
title_field = "Title"
|
|
339
|
+
asset_notes_field = "Url"
|
|
340
|
+
cve_v3_base = "CVSS V3 Base"
|
|
341
|
+
# cve_base = "CVSS BASE"
|
|
342
|
+
# mitigation_field = "SOLUTION"
|
|
343
|
+
external_id_field = "ID"
|
|
344
|
+
first_seen_field = "Detection Date"
|
|
345
|
+
last_seen_field = "Detection Date"
|
|
346
|
+
container_id = "ID"
|
|
347
|
+
row_key = "VULNERABILITY"
|
|
348
|
+
records = []
|
|
349
|
+
|
|
350
|
+
def __init__(self, **kwargs: dict):
|
|
351
|
+
self.asset_identifier_field = "otherTrackingNumber"
|
|
352
|
+
self.name = kwargs.get("name")
|
|
353
|
+
self.fmt = "%Y-%m-%d"
|
|
354
|
+
self.dt_format = "%Y-%m-%d %H:%M:%S"
|
|
355
|
+
self.plan_id = kwargs.get("parent_id")
|
|
356
|
+
self.required_headers = [
|
|
357
|
+
"VULNERABILITY",
|
|
358
|
+
"ID",
|
|
359
|
+
"Detection ID",
|
|
360
|
+
"QID",
|
|
361
|
+
"Url",
|
|
362
|
+
"Param/Cookie",
|
|
363
|
+
"Function",
|
|
364
|
+
"Form Entry Point",
|
|
365
|
+
"Access Path",
|
|
366
|
+
"Authentication",
|
|
367
|
+
"Ajax Request",
|
|
368
|
+
"Ajax Request ID",
|
|
369
|
+
"Ignored",
|
|
370
|
+
"Ignore Reason",
|
|
371
|
+
"Ignore Date",
|
|
372
|
+
"Ignore User",
|
|
373
|
+
"Ignore Comments",
|
|
374
|
+
"Detection Date",
|
|
375
|
+
"Payload #1",
|
|
376
|
+
"Request Method #1",
|
|
377
|
+
"Request URL #1",
|
|
378
|
+
"Request Headers #1",
|
|
379
|
+
"Response #1",
|
|
380
|
+
"Evidence #1",
|
|
381
|
+
"Unique ID",
|
|
382
|
+
"Flags",
|
|
383
|
+
"Protocol",
|
|
384
|
+
"Virtual Host",
|
|
385
|
+
"IP",
|
|
386
|
+
"Port",
|
|
387
|
+
"Result",
|
|
388
|
+
"Info#1",
|
|
389
|
+
"CVSS V3 Base",
|
|
390
|
+
"CVSS V3 Temporal",
|
|
391
|
+
"CVSS V3 Attack Vector",
|
|
392
|
+
"Request Body #1",
|
|
393
|
+
"Potential",
|
|
394
|
+
]
|
|
395
|
+
self.file_path: str = kwargs.get("file_path")
|
|
396
|
+
skip_rows = kwargs.pop("skip_rows")
|
|
397
|
+
self.mapping_file = kwargs.get("mappings_path")
|
|
398
|
+
self.disable_mapping = kwargs.get("disable_mapping")
|
|
399
|
+
self.validater = ImportValidater(
|
|
400
|
+
required_headers=self.required_headers,
|
|
401
|
+
file_path=self.file_path,
|
|
402
|
+
mapping_file_path=self.mapping_file,
|
|
403
|
+
disable_mapping=self.disable_mapping,
|
|
404
|
+
skip_rows=skip_rows,
|
|
405
|
+
)
|
|
406
|
+
self.headers = self.validater.parsed_headers
|
|
407
|
+
self.header = self.headers
|
|
408
|
+
self.mapping = self.validater.mapping
|
|
409
|
+
# super().__init__(plan_id=self.plan_id)
|
|
410
|
+
# self.import_data()
|
|
411
|
+
kwargs["asset_identifier_field"] = "otherTrackingNumber"
|
|
412
|
+
super().__init__(
|
|
413
|
+
logger=logger,
|
|
414
|
+
app=Application(),
|
|
415
|
+
headers=self.required_headers,
|
|
416
|
+
header_line_number=skip_rows,
|
|
417
|
+
asset_func=self.create_asset,
|
|
418
|
+
vuln_func=self.create_vuln,
|
|
419
|
+
**kwargs,
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
def convert_xlsx_to_dict(self, file: TextIO, start_line_number: int = 0) -> tuple:
|
|
423
|
+
"""
|
|
424
|
+
Converts an xlsx file to a list of dictionaries, handling multiple sections.
|
|
425
|
+
|
|
426
|
+
:param TextIO file: The xlsx file to convert
|
|
427
|
+
:param int start_line_number: The line number to start reading from
|
|
428
|
+
:return: Tuple of merged data and headers
|
|
429
|
+
:rtype: tuple
|
|
430
|
+
"""
|
|
431
|
+
# Load the workbook and select the first sheet
|
|
432
|
+
workbook = load_workbook(filename=file.name)
|
|
433
|
+
sheet = workbook.active
|
|
434
|
+
|
|
435
|
+
# Get all data from the sheet
|
|
436
|
+
data = list(sheet.values)
|
|
437
|
+
|
|
438
|
+
# Identify the start of the second section (QID header row)
|
|
439
|
+
second_section_start = next((i for i, row in enumerate(data) if row and row[0] == "QID"), None)
|
|
440
|
+
second_section_end = next(
|
|
441
|
+
(
|
|
442
|
+
i
|
|
443
|
+
for i, row in enumerate(data[second_section_start + 1 :], start=second_section_start + 1)
|
|
444
|
+
if row and row[0] != "QID"
|
|
445
|
+
),
|
|
446
|
+
None,
|
|
447
|
+
)
|
|
448
|
+
first_section_end = next(
|
|
449
|
+
(
|
|
450
|
+
i
|
|
451
|
+
for i, row in enumerate(data[start_line_number + 1 :], start=start_line_number + 1)
|
|
452
|
+
if row and row[0] != "VULNERABILITY"
|
|
453
|
+
),
|
|
454
|
+
None,
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Extract the first section
|
|
458
|
+
first_section_header = list(data[start_line_number])
|
|
459
|
+
first_section_data = data[start_line_number + 1 : first_section_end]
|
|
460
|
+
first_section_dict = [dict(zip(first_section_header, row)) for row in first_section_data]
|
|
461
|
+
|
|
462
|
+
# Extract the second section
|
|
463
|
+
second_section_header = list(data[second_section_start])
|
|
464
|
+
second_section_data = data[second_section_start + 1 : second_section_end]
|
|
465
|
+
second_section_dict = [dict(zip(second_section_header, row)) for row in second_section_data]
|
|
466
|
+
|
|
467
|
+
# Convert second section into a lookup dictionary based on QID
|
|
468
|
+
second_section_lookup = {item.get("Id"): item for item in second_section_dict}
|
|
469
|
+
|
|
470
|
+
# Keys to extract from the second section
|
|
471
|
+
keys_to_merge = ["Title", "Severity Level", "CVSS Base", "CWE", "Solution"]
|
|
472
|
+
|
|
473
|
+
# Merge the two sections by adding specific keys from the second section
|
|
474
|
+
merged_data = []
|
|
475
|
+
for item in first_section_dict:
|
|
476
|
+
qid = item.get("QID")
|
|
477
|
+
if qid in second_section_lookup:
|
|
478
|
+
for key in keys_to_merge:
|
|
479
|
+
item[key] = second_section_lookup[qid].get(key)
|
|
480
|
+
merged_data.append(item)
|
|
481
|
+
|
|
482
|
+
# Convert any string lists to actual lists
|
|
483
|
+
for dat in merged_data:
|
|
484
|
+
for key, val in dat.items():
|
|
485
|
+
if isinstance(val, str) and val.startswith("["):
|
|
486
|
+
try:
|
|
487
|
+
dat[key] = ast.literal_eval(val)
|
|
488
|
+
except SyntaxError as rex:
|
|
489
|
+
self.attributes.app.logger.debug("SyntaxError: %s", rex)
|
|
490
|
+
|
|
491
|
+
# Return merged data and headers
|
|
492
|
+
return merged_data, first_section_header + keys_to_merge
|
|
493
|
+
|
|
494
|
+
def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[Asset]:
|
|
495
|
+
"""
|
|
496
|
+
Fetch assets from the Qualys CSV file
|
|
497
|
+
|
|
498
|
+
:return: Iterator of IntegrationAsset objects
|
|
499
|
+
:rtype: Iterator[IntegrationAsset]
|
|
500
|
+
"""
|
|
501
|
+
bad_ids = ["", "0", "None", "Unknown"]
|
|
502
|
+
if self.mapping.get_value(row, self.container_id) in bad_ids:
|
|
503
|
+
return None
|
|
504
|
+
max_length = 450 # max length of asset name
|
|
505
|
+
asset = Asset(
|
|
506
|
+
name=self.mapping.get_value(row, self.asset_notes_field)[:max_length] or "Unknown",
|
|
507
|
+
notes=self.mapping.get_value(row, self.asset_notes_field),
|
|
508
|
+
otherTrackingNumber=self.mapping.get_value(row, self.container_id),
|
|
509
|
+
# identifier=self.mapping.get_value(row, IMAGE_ID_FIELD),
|
|
510
|
+
assetType=AssetType.Other.value,
|
|
511
|
+
assetCategory=AssetCategory.Software.value,
|
|
512
|
+
parentId=safe_int(self.plan_id),
|
|
513
|
+
parentModule=SecurityPlan.get_module_slug(),
|
|
514
|
+
status=AssetStatus.Active,
|
|
515
|
+
)
|
|
516
|
+
return asset
|
|
517
|
+
|
|
518
|
+
def handle_integration_date(self, input_date_str: str) -> str:
|
|
519
|
+
"""
|
|
520
|
+
Handle the integration date to ingest to date and back to string to get into the correct format if needed.
|
|
521
|
+
|
|
522
|
+
:param str input_date_str: Date string
|
|
523
|
+
:return: Date string
|
|
524
|
+
:rtype: str
|
|
525
|
+
"""
|
|
526
|
+
if not input_date_str:
|
|
527
|
+
return get_current_datetime()
|
|
528
|
+
try:
|
|
529
|
+
date_obj_value = datetime.strptime(
|
|
530
|
+
input_date_str,
|
|
531
|
+
"%d %b %Y %I:%M%p %Z%z",
|
|
532
|
+
)
|
|
533
|
+
except ValueError:
|
|
534
|
+
return get_current_datetime()
|
|
535
|
+
return date_str(date_obj_value, self.dt_format)
|
|
536
|
+
|
|
537
|
+
def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
|
|
538
|
+
"""
|
|
539
|
+
Fetch vulnerabilities from the Qualys CSV file
|
|
540
|
+
|
|
541
|
+
:return: Iterator of IntegrationFinding objects
|
|
542
|
+
:rtype: Vulnerability
|
|
543
|
+
"""
|
|
544
|
+
# additional_fields = ["Title", "Severity Level", "CVSS Base", "CWE", "Solution"]
|
|
545
|
+
finding = Vulnerability(
|
|
546
|
+
title=row.get("Title", self.mapping.get_value(row, "Url")),
|
|
547
|
+
description=row.get("Solution"),
|
|
548
|
+
cve=row.get("CWE"),
|
|
549
|
+
severity=severity_to_regscale(row.get("Severity Level", "1")),
|
|
550
|
+
status=VulnerabilityStatus.Open,
|
|
551
|
+
cvsSv3BaseScore=safe_float(self.mapping.get_value(row, self.cve_v3_base, 0.0)),
|
|
552
|
+
vprScore=safe_float(row.get("CVSS Base", 0.0)),
|
|
553
|
+
firstSeen=self.handle_integration_date(
|
|
554
|
+
self.mapping.get_value(row, self.first_seen_field, get_current_datetime())
|
|
555
|
+
),
|
|
556
|
+
lastSeen=self.handle_integration_date(
|
|
557
|
+
self.mapping.get_value(row, self.last_seen_field, get_current_datetime())
|
|
558
|
+
),
|
|
559
|
+
plugInName=row.get("CWE"),
|
|
560
|
+
dns=self.mapping.get_value(row, self.container_id), # really asset_identifier
|
|
561
|
+
)
|
|
562
|
+
return finding
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
class QualysPolicyScansImporter(FlatFileImporter):
|
|
566
|
+
"""
|
|
567
|
+
Import Qualys Policy Scans data
|
|
568
|
+
"""
|
|
569
|
+
|
|
570
|
+
plan_id = 0
|
|
571
|
+
records = []
|
|
572
|
+
|
|
573
|
+
def __init__(self, **kwargs: dict):
|
|
574
|
+
self.asset_identifier_field = "qualysId"
|
|
575
|
+
self.name = kwargs.get("name")
|
|
576
|
+
self.fmt = "%Y-%m-%d"
|
|
577
|
+
self.dt_format = "%m/%d/%Y at %H:%M:%S (%Z%z)"
|
|
578
|
+
self.plan_id = kwargs.get("parent_id")
|
|
579
|
+
self.required_headers = [
|
|
580
|
+
"Host IP",
|
|
581
|
+
"DNS Hostname",
|
|
582
|
+
"NetBIOS Hostname",
|
|
583
|
+
"Tracking Method",
|
|
584
|
+
"Operating System",
|
|
585
|
+
"NETWORK",
|
|
586
|
+
"Last Scan Date",
|
|
587
|
+
"Evaluation Date",
|
|
588
|
+
"Control ID",
|
|
589
|
+
"Technology",
|
|
590
|
+
"Control",
|
|
591
|
+
"Criticality Label",
|
|
592
|
+
"Criticality Value",
|
|
593
|
+
"Instance",
|
|
594
|
+
"Rationale",
|
|
595
|
+
"Status",
|
|
596
|
+
"Remediation",
|
|
597
|
+
"Deprecated",
|
|
598
|
+
"Evidence",
|
|
599
|
+
"Exception Assignee",
|
|
600
|
+
"Exception Status",
|
|
601
|
+
"Exception End Date",
|
|
602
|
+
"Exception Creator",
|
|
603
|
+
"Exception Created Date",
|
|
604
|
+
"Exception Modifier",
|
|
605
|
+
"Exception Modified Date",
|
|
606
|
+
"Exception Comments History",
|
|
607
|
+
"Cause of Failure",
|
|
608
|
+
"Qualys Host ID",
|
|
609
|
+
"Previous Status",
|
|
610
|
+
]
|
|
611
|
+
self.file_path: str = kwargs.get("file_path")
|
|
612
|
+
skip_rows = kwargs.pop("skip_rows")
|
|
613
|
+
self.mapping_file = kwargs.get("mappings_path")
|
|
614
|
+
self.disable_mapping = kwargs.get("disable_mapping")
|
|
615
|
+
self.validater = ImportValidater(
|
|
616
|
+
required_headers=self.required_headers,
|
|
617
|
+
file_path=self.file_path,
|
|
618
|
+
mapping_file_path=self.mapping_file,
|
|
619
|
+
disable_mapping=self.disable_mapping,
|
|
620
|
+
skip_rows=skip_rows,
|
|
621
|
+
)
|
|
622
|
+
self.headers = self.validater.parsed_headers
|
|
623
|
+
self.header = self.headers
|
|
624
|
+
self.mapping = self.validater.mapping
|
|
625
|
+
# super().__init__(plan_id=self.plan_id)
|
|
626
|
+
# self.import_data()
|
|
627
|
+
kwargs["asset_identifier_field"] = self.asset_identifier_field
|
|
628
|
+
super().__init__(
|
|
629
|
+
logger=logger,
|
|
630
|
+
app=Application(),
|
|
631
|
+
headers=self.required_headers,
|
|
632
|
+
header_line_number=skip_rows,
|
|
633
|
+
asset_func=self.create_asset,
|
|
634
|
+
vuln_func=self.create_vuln,
|
|
635
|
+
**kwargs,
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
def create_asset(self, row: Optional[dict] = None, **kwargs) -> Optional[Asset]:
|
|
639
|
+
"""
|
|
640
|
+
Fetch assets from the Qualys CSV file
|
|
641
|
+
|
|
642
|
+
:return: Iterator of IntegrationAsset objects
|
|
643
|
+
:rtype: Iterator[IntegrationAsset]
|
|
644
|
+
"""
|
|
645
|
+
max_length = 450 # max length of asset name
|
|
646
|
+
asset = Asset(
|
|
647
|
+
name=row.get("Control", "Uknown")[:max_length],
|
|
648
|
+
# notes=self.mapping.get_value(row, self.asset_notes_field),
|
|
649
|
+
ipAddress=self.mapping.get_value(row, "Host IP"),
|
|
650
|
+
operatingSystem=self.mapping.get_value(row, "Operating System"),
|
|
651
|
+
qualysId=self.mapping.get_value(row, "Control ID"),
|
|
652
|
+
otherTrackingNumber=self.mapping.get_value(row, "Control ID"),
|
|
653
|
+
# identifier=self.mapping.get_value(row, IMAGE_ID_FIELD),
|
|
654
|
+
assetType=AssetType.Other.value,
|
|
655
|
+
assetCategory=AssetCategory.Software.value,
|
|
656
|
+
parentId=safe_int(self.plan_id),
|
|
657
|
+
parentModule=SecurityPlan.get_module_slug(),
|
|
658
|
+
status=AssetStatus.Active,
|
|
659
|
+
)
|
|
660
|
+
return asset
|
|
661
|
+
|
|
662
|
+
def handle_integration_date(self, input_date_str: Optional[str]) -> str:
|
|
663
|
+
"""
|
|
664
|
+
Handle the integration date to ingest to date and back to string to get into the correct format if needed.
|
|
665
|
+
|
|
666
|
+
:param str input_date_str: Date string
|
|
667
|
+
:return: Date string
|
|
668
|
+
:rtype: str
|
|
669
|
+
"""
|
|
670
|
+
if not input_date_str:
|
|
671
|
+
return get_current_datetime()
|
|
672
|
+
date_obj_value = datetime.strptime(
|
|
673
|
+
input_date_str,
|
|
674
|
+
self.dt_format,
|
|
675
|
+
)
|
|
676
|
+
return datetime_str(date_obj_value)
|
|
677
|
+
|
|
678
|
+
def create_vuln(self, row: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
|
|
679
|
+
"""
|
|
680
|
+
Fetch vulnerabilities from the Qualys CSV file
|
|
681
|
+
|
|
682
|
+
:return: Iterator of IntegrationFinding objects
|
|
683
|
+
:rtype: Vulnerability
|
|
684
|
+
"""
|
|
685
|
+
finding = Vulnerability(
|
|
686
|
+
title=self.mapping.get_value(row, "Control"),
|
|
687
|
+
description=self.mapping.get_value(row, "Rationale"),
|
|
688
|
+
severity=severity_to_regscale(self.mapping.get_value(row, "Criticality Value")),
|
|
689
|
+
status=(
|
|
690
|
+
VulnerabilityStatus.Open
|
|
691
|
+
if self.mapping.get_value(row, "Status") == "Failed"
|
|
692
|
+
else VulnerabilityStatus.Closed
|
|
693
|
+
),
|
|
694
|
+
cvsSv3BaseScore=safe_float(0.0),
|
|
695
|
+
vprScore=safe_float(0.0),
|
|
696
|
+
firstSeen=self.handle_integration_date(self.mapping.get_value(row, "Last Scan Date", None)),
|
|
697
|
+
lastSeen=self.handle_integration_date(self.mapping.get_value(row, "Evaluation Date", None)),
|
|
698
|
+
plugInId=self.mapping.get_value(row, "Control ID"),
|
|
699
|
+
plugInName="QualysPolicyScan",
|
|
700
|
+
dns=self.mapping.get_value(row, "Control ID"), # really asset_identifier
|
|
701
|
+
)
|
|
702
|
+
return finding
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def severity_to_regscale(severity: str) -> VulnerabilitySeverity:
|
|
706
|
+
"""
|
|
707
|
+
Convert Qualys severity to RegScale severity
|
|
708
|
+
severity is given in numbers from 1-5, 5 being the highest
|
|
709
|
+
:param str severity: Qualys severity to map to a RegScale severity
|
|
710
|
+
:return: RegScale severity
|
|
711
|
+
:rtype: IssueSeverity
|
|
712
|
+
"""
|
|
713
|
+
severity_mapping = {
|
|
714
|
+
"1": VulnerabilitySeverity.Low,
|
|
715
|
+
"2": VulnerabilitySeverity.Low,
|
|
716
|
+
"3": VulnerabilitySeverity.Medium,
|
|
717
|
+
"4": VulnerabilitySeverity.High,
|
|
718
|
+
"5": VulnerabilitySeverity.Critical,
|
|
719
|
+
}
|
|
720
|
+
return severity_mapping.get(severity, VulnerabilitySeverity.Low)
|