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,823 @@
|
|
|
1
|
+
"""Module for AWS resource inventory scanning integration."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
|
8
|
+
|
|
9
|
+
from regscale.core.utils.date import date_str, datetime_str
|
|
10
|
+
from regscale.integrations.commercial.amazon.common import (
|
|
11
|
+
check_finding_severity,
|
|
12
|
+
determine_status_and_results,
|
|
13
|
+
get_comments,
|
|
14
|
+
get_due_date,
|
|
15
|
+
)
|
|
16
|
+
from regscale.integrations.scanner_integration import (
|
|
17
|
+
IntegrationAsset,
|
|
18
|
+
IntegrationFinding,
|
|
19
|
+
ScannerIntegration,
|
|
20
|
+
ScannerIntegrationType,
|
|
21
|
+
)
|
|
22
|
+
from regscale.models import IssueStatus, regscale_models
|
|
23
|
+
from .inventory import AWSInventoryCollector
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger("regscale")
|
|
26
|
+
|
|
27
|
+
# Constants for file paths
|
|
28
|
+
INVENTORY_FILE_PATH = os.path.join("artifacts", "aws", "inventory.json")
|
|
29
|
+
CACHE_TTL_SECONDS = 8 * 60 * 60 # 8 hours in seconds
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AWSInventoryIntegration(ScannerIntegration):
|
|
33
|
+
"""Integration class for AWS resource inventory scanning."""
|
|
34
|
+
|
|
35
|
+
title = "AWS"
|
|
36
|
+
asset_identifier_field = "awsIdentifier"
|
|
37
|
+
issue_identifier_field = "awsIdentifier"
|
|
38
|
+
finding_severity_map = {
|
|
39
|
+
"CRITICAL": regscale_models.IssueSeverity.High,
|
|
40
|
+
"HIGH": regscale_models.IssueSeverity.High,
|
|
41
|
+
"MEDIUM": regscale_models.IssueSeverity.Moderate,
|
|
42
|
+
"LOW": regscale_models.IssueSeverity.Low,
|
|
43
|
+
"INFORMATIONAL": regscale_models.IssueSeverity.NotAssigned,
|
|
44
|
+
}
|
|
45
|
+
checklist_status_map = {
|
|
46
|
+
"Pass": regscale_models.ChecklistStatus.PASS,
|
|
47
|
+
"Fail": regscale_models.ChecklistStatus.FAIL,
|
|
48
|
+
}
|
|
49
|
+
type = ScannerIntegrationType.CHECKLIST
|
|
50
|
+
|
|
51
|
+
def __init__(self, plan_id: int, **kwargs):
|
|
52
|
+
"""
|
|
53
|
+
Initialize the AWS inventory integration.
|
|
54
|
+
|
|
55
|
+
:param int plan_id: The RegScale plan ID
|
|
56
|
+
"""
|
|
57
|
+
super().__init__(plan_id=plan_id, kwargs=kwargs)
|
|
58
|
+
self.collector: Optional[AWSInventoryCollector] = None
|
|
59
|
+
|
|
60
|
+
def authenticate(
|
|
61
|
+
self,
|
|
62
|
+
aws_access_key_id: Optional[str],
|
|
63
|
+
aws_secret_access_key: Optional[str],
|
|
64
|
+
region: str = os.getenv("AWS_REGION", "us-east-1"),
|
|
65
|
+
aws_session_token: Optional[str] = os.getenv("AWS_SESSION_TOKEN"),
|
|
66
|
+
) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Authenticate with AWS and initialize the inventory collector.
|
|
69
|
+
|
|
70
|
+
:param str aws_access_key_id: Optional AWS access key ID
|
|
71
|
+
:param str aws_secret_access_key: Optional AWS secret access key
|
|
72
|
+
:param str region: AWS region to collect inventory from
|
|
73
|
+
:param str aws_session_token: Optional AWS session ID
|
|
74
|
+
"""
|
|
75
|
+
self.collector = AWSInventoryCollector(
|
|
76
|
+
region=region,
|
|
77
|
+
aws_access_key_id=aws_access_key_id,
|
|
78
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
79
|
+
aws_session_token=aws_session_token,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
def fetch_aws_data_if_needed(
|
|
83
|
+
self,
|
|
84
|
+
region: str,
|
|
85
|
+
aws_access_key_id: Optional[str],
|
|
86
|
+
aws_secret_access_key: Optional[str],
|
|
87
|
+
aws_session_token: Optional[str] = None,
|
|
88
|
+
) -> Dict[str, Any]:
|
|
89
|
+
"""
|
|
90
|
+
Fetch AWS inventory data, using cached data if available and not expired.
|
|
91
|
+
|
|
92
|
+
:param str region: AWS region to collect inventory from
|
|
93
|
+
:param str aws_access_key_id: Optional AWS access key ID
|
|
94
|
+
:param str aws_secret_access_key: Optional AWS secret access key
|
|
95
|
+
:param str aws_session_token: Optional AWS session ID
|
|
96
|
+
:return: Dictionary containing AWS inventory data
|
|
97
|
+
:rtype: Dict[str, Any]
|
|
98
|
+
"""
|
|
99
|
+
from regscale.models import DateTimeEncoder
|
|
100
|
+
|
|
101
|
+
# Check if we have cached data that's still valid
|
|
102
|
+
if os.path.exists(INVENTORY_FILE_PATH):
|
|
103
|
+
file_age = time.time() - os.path.getmtime(INVENTORY_FILE_PATH)
|
|
104
|
+
if file_age < CACHE_TTL_SECONDS:
|
|
105
|
+
with open(INVENTORY_FILE_PATH, "r", encoding="utf-8") as file:
|
|
106
|
+
return json.load(file)
|
|
107
|
+
|
|
108
|
+
# No valid cache, need to fetch new data
|
|
109
|
+
if not self.collector:
|
|
110
|
+
self.authenticate(aws_access_key_id, aws_secret_access_key, region, aws_session_token)
|
|
111
|
+
|
|
112
|
+
if not self.collector:
|
|
113
|
+
raise RuntimeError("Failed to initialize AWS inventory collector")
|
|
114
|
+
|
|
115
|
+
inventory = self.collector.collect_all()
|
|
116
|
+
|
|
117
|
+
# Ensure the artifacts directory exists
|
|
118
|
+
os.makedirs(os.path.dirname(INVENTORY_FILE_PATH), exist_ok=True)
|
|
119
|
+
|
|
120
|
+
with open(INVENTORY_FILE_PATH, "w", encoding="utf-8") as file:
|
|
121
|
+
json.dump(inventory, file, cls=DateTimeEncoder, indent=2)
|
|
122
|
+
|
|
123
|
+
return inventory
|
|
124
|
+
|
|
125
|
+
def _process_asset_collection(
|
|
126
|
+
self, assets: List[Dict[str, Any]], asset_type: str, parser_method
|
|
127
|
+
) -> Iterator[IntegrationAsset]:
|
|
128
|
+
"""
|
|
129
|
+
Process a collection of assets using the specified parser method.
|
|
130
|
+
|
|
131
|
+
:param List[Dict[str, Any]] assets: List of assets to process
|
|
132
|
+
:param str asset_type: Type of asset being processed
|
|
133
|
+
:param callable parser_method: Method to parse the asset
|
|
134
|
+
:yield: Iterator[IntegrationAsset]
|
|
135
|
+
"""
|
|
136
|
+
for asset in assets:
|
|
137
|
+
if not isinstance(asset, dict) and asset not in ["Users", "Roles"]:
|
|
138
|
+
logger.warning(f"Skipping {asset_type} due to invalid data format: {asset}")
|
|
139
|
+
continue
|
|
140
|
+
try:
|
|
141
|
+
if asset in ["Users", "Roles"]:
|
|
142
|
+
for user in assets[asset]:
|
|
143
|
+
self.num_assets_to_process += 1
|
|
144
|
+
yield parser_method(user)
|
|
145
|
+
else:
|
|
146
|
+
self.num_assets_to_process += 1
|
|
147
|
+
yield parser_method(asset)
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.error(f"Error parsing {asset_type} {asset}: {str(e)}", exc_info=True)
|
|
150
|
+
|
|
151
|
+
def _process_inventory_section(
|
|
152
|
+
self, inventory: Dict[str, Any], section_key: str, asset_type: str, parser_method
|
|
153
|
+
) -> Iterator[IntegrationAsset]:
|
|
154
|
+
"""
|
|
155
|
+
Process a section of the inventory.
|
|
156
|
+
|
|
157
|
+
:param Dict[str, Any] inventory: The complete inventory data
|
|
158
|
+
:param str section_key: Key for the section in the inventory
|
|
159
|
+
:param str asset_type: Type of asset being processed
|
|
160
|
+
:param callable parser_method: Method to parse the asset
|
|
161
|
+
:yield: Iterator[IntegrationAsset]
|
|
162
|
+
"""
|
|
163
|
+
assets = inventory.get(section_key, [])
|
|
164
|
+
yield from self._process_asset_collection(assets, asset_type, parser_method)
|
|
165
|
+
|
|
166
|
+
def get_asset_configs(self) -> List[Tuple[str, str, callable]]:
|
|
167
|
+
"""
|
|
168
|
+
Get the asset configurations for parsing.
|
|
169
|
+
|
|
170
|
+
:return: List of asset configurations
|
|
171
|
+
:rtype: List[Tuple[str, str, callable]]
|
|
172
|
+
"""
|
|
173
|
+
return [
|
|
174
|
+
("IAM", "Roles", self.parse_aws_account),
|
|
175
|
+
("EC2Instances", "EC2 instance", self.parse_ec2_instance),
|
|
176
|
+
("LambdaFunctions", "Lambda function", self.parse_lambda_function),
|
|
177
|
+
("S3Buckets", "S3 bucket", self.parse_s3_bucket),
|
|
178
|
+
("RDSInstances", "RDS instance", self.parse_rds_instance),
|
|
179
|
+
("DynamoDBTables", "DynamoDB table", self.parse_dynamodb_table),
|
|
180
|
+
("VPCs", "VPC", self.parse_vpc),
|
|
181
|
+
("LoadBalancers", "Load Balancer", self.parse_load_balancer),
|
|
182
|
+
("ECRRepositories", "ECR repository", self.parse_ecr_repository),
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
def fetch_assets(
|
|
186
|
+
self,
|
|
187
|
+
region: str,
|
|
188
|
+
aws_access_key_id: Optional[str] = None,
|
|
189
|
+
aws_secret_access_key: Optional[str] = None,
|
|
190
|
+
aws_session_token: Optional[str] = None,
|
|
191
|
+
) -> Iterator[IntegrationAsset]:
|
|
192
|
+
"""
|
|
193
|
+
Fetch AWS assets from the inventory.
|
|
194
|
+
|
|
195
|
+
:param str region: AWS region to collect inventory from
|
|
196
|
+
:param str aws_access_key_id: Optional AWS access key ID
|
|
197
|
+
:param str aws_secret_access_key: Optional AWS secret access key
|
|
198
|
+
:param str aws_session_token: Optional AWS session ID
|
|
199
|
+
:yield: Iterator[IntegrationAsset]
|
|
200
|
+
"""
|
|
201
|
+
inventory = self.fetch_aws_data_if_needed(region, aws_access_key_id, aws_secret_access_key, aws_session_token)
|
|
202
|
+
|
|
203
|
+
# Process each asset type using the corresponding parser
|
|
204
|
+
asset_configs = self.get_asset_configs()
|
|
205
|
+
|
|
206
|
+
self.num_assets_to_process = 0
|
|
207
|
+
|
|
208
|
+
for section_key, asset_type, parser_method in asset_configs:
|
|
209
|
+
yield from self._process_inventory_section(inventory, section_key, asset_type, parser_method)
|
|
210
|
+
|
|
211
|
+
def parse_ec2_instance(self, instance: Dict[str, Any]) -> IntegrationAsset:
|
|
212
|
+
"""Parse EC2 instance data into an IntegrationAsset.
|
|
213
|
+
|
|
214
|
+
:param Dict[str, Any] instance: The EC2 instance data
|
|
215
|
+
:return: The parsed IntegrationAsset
|
|
216
|
+
:rtype: IntegrationAsset
|
|
217
|
+
"""
|
|
218
|
+
# Get instance name from tags
|
|
219
|
+
instance_name = next(
|
|
220
|
+
(tag["Value"] for tag in instance.get("Tags", []) if tag["Key"] == "Name"), instance.get("InstanceId", "")
|
|
221
|
+
)
|
|
222
|
+
name = instance_name
|
|
223
|
+
|
|
224
|
+
# Calculate total storage from block devices
|
|
225
|
+
total_storage = 0
|
|
226
|
+
for device in instance.get("BlockDeviceMappings", []):
|
|
227
|
+
if "Ebs" in device:
|
|
228
|
+
# Note: We need to add a call to describe_volumes to get actual size
|
|
229
|
+
total_storage += 8 # Default to 8 GB if size unknown
|
|
230
|
+
|
|
231
|
+
# Calculate RAM based on instance type
|
|
232
|
+
# This would need a mapping of instance types to RAM
|
|
233
|
+
ram = 16 # Default to 16 GB for c5.2xlarge
|
|
234
|
+
|
|
235
|
+
# Get CPU info
|
|
236
|
+
cpu_options = instance.get("CpuOptions", {})
|
|
237
|
+
cpu_count = int(cpu_options.get("CoreCount", 0) * cpu_options.get("ThreadsPerCore", 0))
|
|
238
|
+
|
|
239
|
+
# Determine if instance is public facing
|
|
240
|
+
is_public_facing = bool(instance.get("PublicIpAddress"))
|
|
241
|
+
|
|
242
|
+
# Get OS details from platform and image info
|
|
243
|
+
image_info = instance.get("ImageInfo", {})
|
|
244
|
+
image_name = image_info.get("Name", "").lower()
|
|
245
|
+
|
|
246
|
+
# Check for Palo Alto device first
|
|
247
|
+
if "pa-vm-aws" in image_name:
|
|
248
|
+
operating_system = regscale_models.AssetOperatingSystem.PaloAlto
|
|
249
|
+
# Also update the asset type to reflect it's a network security device
|
|
250
|
+
asset_type = regscale_models.AssetType.Appliance
|
|
251
|
+
asset_category = regscale_models.AssetCategory.Hardware
|
|
252
|
+
component_type = regscale_models.ComponentType.Hardware
|
|
253
|
+
component_names = ["Palo Alto Networks IDPS"]
|
|
254
|
+
elif instance.get("Platform") == "windows":
|
|
255
|
+
operating_system = regscale_models.AssetOperatingSystem.WindowsServer
|
|
256
|
+
asset_type = regscale_models.AssetType.VM
|
|
257
|
+
asset_category = regscale_models.AssetCategory.Hardware
|
|
258
|
+
component_type = regscale_models.ComponentType.Hardware
|
|
259
|
+
component_names = ["EC2 Instances"]
|
|
260
|
+
else:
|
|
261
|
+
operating_system = regscale_models.AssetOperatingSystem.Linux
|
|
262
|
+
asset_type = regscale_models.AssetType.VM
|
|
263
|
+
asset_category = regscale_models.AssetCategory.Hardware
|
|
264
|
+
component_type = regscale_models.ComponentType.Hardware
|
|
265
|
+
component_names = ["EC2 Instances"]
|
|
266
|
+
|
|
267
|
+
os_version = image_info.get("Description", "")
|
|
268
|
+
|
|
269
|
+
# Get FQDN - use public DNS name, private DNS name, or instance name
|
|
270
|
+
fqdn = (
|
|
271
|
+
instance.get("PublicDnsName")
|
|
272
|
+
or instance.get("PrivateDnsName")
|
|
273
|
+
or instance_name
|
|
274
|
+
or instance.get("InstanceId", "")
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Create description
|
|
278
|
+
description = f"{instance_name} - {instance.get('PlatformDetails', 'Linux')} instance running on {instance.get('InstanceType', '')} with {cpu_count} vCPUs and {ram}GB RAM"
|
|
279
|
+
|
|
280
|
+
# Build notes with additional details
|
|
281
|
+
notes = f"""Description: {description}
|
|
282
|
+
AMI ID: {instance.get('ImageId', '')}
|
|
283
|
+
AMI Description: {image_info.get('Description', '')}
|
|
284
|
+
Architecture: {instance.get('Architecture', '')}
|
|
285
|
+
Root Device Type: {image_info.get('RootDeviceType', '')}
|
|
286
|
+
Virtualization: {image_info.get('VirtualizationType', '')}
|
|
287
|
+
Instance Type: {instance.get('InstanceType', '')}
|
|
288
|
+
vCPUs: {cpu_count}
|
|
289
|
+
RAM: {ram}GB
|
|
290
|
+
State: {instance.get('State')}
|
|
291
|
+
Platform Details: {instance.get('PlatformDetails', 'Linux')}
|
|
292
|
+
Private IP: {instance.get('PrivateIpAddress', 'N/A')}
|
|
293
|
+
Public IP: {instance.get('PublicIpAddress', 'N/A')}
|
|
294
|
+
VPC ID: {instance.get('VpcId', 'N/A')}
|
|
295
|
+
Subnet ID: {instance.get('SubnetId', 'N/A')}"""
|
|
296
|
+
|
|
297
|
+
# Create URI for AWS Console link
|
|
298
|
+
uri = f"https://console.aws.amazon.com/ec2/v2/home?region={instance.get('Region', 'us-east-1')}#InstanceDetails:instanceId={instance.get('InstanceId', '')}"
|
|
299
|
+
|
|
300
|
+
return IntegrationAsset(
|
|
301
|
+
name=name,
|
|
302
|
+
identifier=instance.get("InstanceId", ""),
|
|
303
|
+
asset_type=asset_type,
|
|
304
|
+
asset_category=asset_category,
|
|
305
|
+
component_type=component_type,
|
|
306
|
+
component_names=component_names,
|
|
307
|
+
parent_id=self.plan_id,
|
|
308
|
+
parent_module="securityplans",
|
|
309
|
+
status=(
|
|
310
|
+
regscale_models.AssetStatus.Active
|
|
311
|
+
if instance.get("State") == "running"
|
|
312
|
+
else regscale_models.AssetStatus.Inactive
|
|
313
|
+
),
|
|
314
|
+
ip_address=instance.get("PrivateIpAddress") or instance.get("PublicIpAddress", ""),
|
|
315
|
+
mac_address=None, # Would need to get from network interfaces
|
|
316
|
+
fqdn=fqdn,
|
|
317
|
+
disk_storage=total_storage,
|
|
318
|
+
cpu=cpu_count,
|
|
319
|
+
ram=ram,
|
|
320
|
+
operating_system=operating_system,
|
|
321
|
+
os_version=os_version,
|
|
322
|
+
location=instance.get("Region", "us-east-1"),
|
|
323
|
+
notes=notes,
|
|
324
|
+
model=instance.get("InstanceType"),
|
|
325
|
+
manufacturer="AWS",
|
|
326
|
+
is_public_facing=is_public_facing,
|
|
327
|
+
aws_identifier=instance.get("InstanceId"),
|
|
328
|
+
vlan_id=instance.get("SubnetId"),
|
|
329
|
+
uri=uri,
|
|
330
|
+
source_data=instance,
|
|
331
|
+
description=description,
|
|
332
|
+
is_virtual=True, # EC2 instances are always virtual
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def parse_lambda_function(self, function: Dict[str, Any]) -> IntegrationAsset:
|
|
336
|
+
"""
|
|
337
|
+
Parse Lambda function data into RegScale asset format.
|
|
338
|
+
|
|
339
|
+
:param Dict[str, Any] function: Lambda function data
|
|
340
|
+
:return: RegScale asset
|
|
341
|
+
:rtype: IntegrationAsset
|
|
342
|
+
"""
|
|
343
|
+
name = function.get("FunctionName", "")
|
|
344
|
+
notes: str = "" # Initialize notes with type hint
|
|
345
|
+
|
|
346
|
+
# Handle description - only slice if it's a string
|
|
347
|
+
description = function.get("Description")
|
|
348
|
+
if isinstance(description, str) and description:
|
|
349
|
+
# Move description to notes instead
|
|
350
|
+
notes = f"Description: {description}\n{notes}"
|
|
351
|
+
|
|
352
|
+
# Create full description
|
|
353
|
+
full_description = f"AWS Lambda function {function.get('FunctionName', '')} running {function.get('Runtime', 'unknown runtime')} with {function.get('MemorySize', 0)}MB memory"
|
|
354
|
+
if isinstance(description, str) and description:
|
|
355
|
+
full_description += f"\nFunction description: {description}"
|
|
356
|
+
|
|
357
|
+
# Build notes with additional details
|
|
358
|
+
notes = f"""Function Name: {function.get('FunctionName', '')}
|
|
359
|
+
Runtime: {function.get('Runtime', 'unknown')}
|
|
360
|
+
Memory Size: {function.get('MemorySize', 0)} MB
|
|
361
|
+
Timeout: {function.get('Timeout', 0)} seconds
|
|
362
|
+
Handler: {function.get('Handler', '')}
|
|
363
|
+
Description: {description if isinstance(description, str) else ''}"""
|
|
364
|
+
|
|
365
|
+
return IntegrationAsset(
|
|
366
|
+
# Required fields
|
|
367
|
+
name=name,
|
|
368
|
+
identifier=str(function.get("FunctionName", "")),
|
|
369
|
+
asset_type=regscale_models.AssetType.Other,
|
|
370
|
+
asset_category=regscale_models.AssetCategory.Software,
|
|
371
|
+
component_type=regscale_models.ComponentType.Software,
|
|
372
|
+
component_names=["Lambda Functions"],
|
|
373
|
+
# Parent information
|
|
374
|
+
parent_id=self.plan_id,
|
|
375
|
+
parent_module="securityplans",
|
|
376
|
+
# Status and location
|
|
377
|
+
status=regscale_models.AssetStatus.Active, # Lambda functions are always available
|
|
378
|
+
location=function.get("Region"),
|
|
379
|
+
# Software details
|
|
380
|
+
software_name=function.get("Runtime"),
|
|
381
|
+
software_version=function.get("Runtime", "").split(".")[-1] if function.get("Runtime") else None,
|
|
382
|
+
ram=function.get("MemorySize"),
|
|
383
|
+
# Cloud identifiers
|
|
384
|
+
external_id=function.get("FunctionName"),
|
|
385
|
+
aws_identifier=function.get("FunctionArn"),
|
|
386
|
+
uri=function.get("FunctionUrl"),
|
|
387
|
+
# Additional metadata
|
|
388
|
+
manufacturer="AWS",
|
|
389
|
+
source_data=function,
|
|
390
|
+
notes=notes,
|
|
391
|
+
description=full_description,
|
|
392
|
+
is_virtual=True, # Lambda functions are serverless/virtual
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
def parse_aws_account(self, iam: Dict[str, Any]) -> IntegrationAsset:
|
|
396
|
+
"""
|
|
397
|
+
Parse IAM data to an AWS Account RegScale asset.
|
|
398
|
+
|
|
399
|
+
:param Dict[str, Any] iam: iam data
|
|
400
|
+
:return: RegScale asset
|
|
401
|
+
:rtype: IntegrationAsset
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
def get_aws_account_id(arn: str) -> str:
|
|
405
|
+
"""
|
|
406
|
+
Get the AWS account ID from an ARN.
|
|
407
|
+
|
|
408
|
+
:param str arn: The ARN to extract the account ID from
|
|
409
|
+
:return: The AWS account ID
|
|
410
|
+
:rtype: str
|
|
411
|
+
"""
|
|
412
|
+
return arn.split(":")[4]
|
|
413
|
+
|
|
414
|
+
name = get_aws_account_id(iam.get("Arn", ""))
|
|
415
|
+
|
|
416
|
+
return IntegrationAsset(
|
|
417
|
+
# Required fields
|
|
418
|
+
name=name,
|
|
419
|
+
identifier=f"AWS::::Account:{name}",
|
|
420
|
+
asset_type=regscale_models.AssetType.Other,
|
|
421
|
+
asset_category=regscale_models.AssetCategory.Hardware,
|
|
422
|
+
component_type=regscale_models.ComponentType.Software,
|
|
423
|
+
component_names=["AWS Account"],
|
|
424
|
+
# Parent information
|
|
425
|
+
parent_id=self.plan_id,
|
|
426
|
+
parent_module="securityplans",
|
|
427
|
+
# Status and location
|
|
428
|
+
status=regscale_models.AssetStatus.Active,
|
|
429
|
+
location="Unknown",
|
|
430
|
+
# Cloud identifiers
|
|
431
|
+
external_id=name,
|
|
432
|
+
aws_identifier=f"AWS::::Account:{name}",
|
|
433
|
+
# Additional metadata
|
|
434
|
+
manufacturer="AWS",
|
|
435
|
+
source_data=iam,
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
def parse_s3_bucket(self, bucket: Dict[str, Any]) -> IntegrationAsset:
|
|
439
|
+
"""
|
|
440
|
+
Parse S3 bucket data into RegScale asset format.
|
|
441
|
+
|
|
442
|
+
:param Dict[str, Any] bucket: S3 bucket data
|
|
443
|
+
:return: RegScale asset
|
|
444
|
+
:rtype: IntegrationAsset
|
|
445
|
+
"""
|
|
446
|
+
name = bucket.get("Name", "")
|
|
447
|
+
|
|
448
|
+
return IntegrationAsset(
|
|
449
|
+
# Required fields
|
|
450
|
+
name=name,
|
|
451
|
+
identifier=str(bucket.get("Name", "")),
|
|
452
|
+
asset_type=regscale_models.AssetType.Other,
|
|
453
|
+
asset_category=regscale_models.AssetCategory.Hardware,
|
|
454
|
+
component_type=regscale_models.ComponentType.Hardware,
|
|
455
|
+
component_names=["S3 Buckets"],
|
|
456
|
+
# Parent information
|
|
457
|
+
parent_id=self.plan_id,
|
|
458
|
+
parent_module="securityplans",
|
|
459
|
+
# Status and location
|
|
460
|
+
status=regscale_models.AssetStatus.Active,
|
|
461
|
+
location=bucket.get("Region"),
|
|
462
|
+
# Cloud identifiers
|
|
463
|
+
external_id=bucket.get("Name"),
|
|
464
|
+
aws_identifier=f"arn:aws:s3:::{bucket.get('Name')}",
|
|
465
|
+
uri=f"https://{bucket.get('Name')}.s3.amazonaws.com",
|
|
466
|
+
# Additional metadata
|
|
467
|
+
manufacturer="AWS",
|
|
468
|
+
is_public_facing=any(
|
|
469
|
+
grant.get("Grantee", {}).get("URI") == "http://acs.amazonaws.com/groups/global/AllUsers"
|
|
470
|
+
for grant in bucket.get("Grants", [])
|
|
471
|
+
),
|
|
472
|
+
source_data=bucket,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
def parse_rds_instance(self, db: Dict[str, Any]) -> IntegrationAsset:
|
|
476
|
+
"""
|
|
477
|
+
Parse RDS instance data into RegScale asset format.
|
|
478
|
+
|
|
479
|
+
:param Dict[str, Any] db: RDS instance data
|
|
480
|
+
:return: RegScale asset
|
|
481
|
+
:rtype: IntegrationAsset
|
|
482
|
+
"""
|
|
483
|
+
name = db.get("DBInstanceIdentifier", "")
|
|
484
|
+
if db.get("EngineVersion"):
|
|
485
|
+
name += f" {db.get('EngineVersion')}"
|
|
486
|
+
name += f") - {db.get('DBInstanceClass', '')}"
|
|
487
|
+
|
|
488
|
+
return IntegrationAsset(
|
|
489
|
+
# Required fields
|
|
490
|
+
name=name,
|
|
491
|
+
identifier=str(db.get("DBInstanceIdentifier", "")),
|
|
492
|
+
asset_type=regscale_models.AssetType.VM,
|
|
493
|
+
asset_category=regscale_models.AssetCategory.Hardware,
|
|
494
|
+
component_type=regscale_models.ComponentType.Hardware,
|
|
495
|
+
component_names=["RDS Instances"],
|
|
496
|
+
# Parent information
|
|
497
|
+
parent_id=self.plan_id,
|
|
498
|
+
parent_module="securityplans",
|
|
499
|
+
# Network information
|
|
500
|
+
fqdn=db.get("Endpoint", {}).get("Address"),
|
|
501
|
+
vlan_id=db.get("VpcId"),
|
|
502
|
+
# Status and location
|
|
503
|
+
status=(
|
|
504
|
+
regscale_models.AssetStatus.Active
|
|
505
|
+
if db.get("DBInstanceStatus") == "available"
|
|
506
|
+
else regscale_models.AssetStatus.Inactive
|
|
507
|
+
),
|
|
508
|
+
location=db.get("AvailabilityZone"),
|
|
509
|
+
# Hardware details
|
|
510
|
+
model=db.get("DBInstanceClass"),
|
|
511
|
+
manufacturer="AWS",
|
|
512
|
+
disk_storage=db.get("AllocatedStorage"),
|
|
513
|
+
# Software details
|
|
514
|
+
software_name=db.get("Engine"),
|
|
515
|
+
software_version=db.get("EngineVersion"),
|
|
516
|
+
# Cloud identifiers
|
|
517
|
+
external_id=db.get("DBInstanceIdentifier"),
|
|
518
|
+
aws_identifier=db.get("DBInstanceArn"),
|
|
519
|
+
# Additional metadata
|
|
520
|
+
is_public_facing=db.get("PubliclyAccessible", False),
|
|
521
|
+
source_data=db,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
def parse_dynamodb_table(self, table: Dict[str, Any]) -> IntegrationAsset:
|
|
525
|
+
"""
|
|
526
|
+
Parse DynamoDB table data into RegScale asset format.
|
|
527
|
+
|
|
528
|
+
:param Dict[str, Any] table: DynamoDB table data
|
|
529
|
+
:return: RegScale asset
|
|
530
|
+
:rtype: IntegrationAsset
|
|
531
|
+
"""
|
|
532
|
+
name = table.get("TableName", "")
|
|
533
|
+
if table.get("TableStatus"):
|
|
534
|
+
name += f" ({table.get('TableStatus')})"
|
|
535
|
+
|
|
536
|
+
return IntegrationAsset(
|
|
537
|
+
# Required fields
|
|
538
|
+
name=name,
|
|
539
|
+
identifier=str(table.get("TableName", "")),
|
|
540
|
+
asset_type=regscale_models.AssetType.Other,
|
|
541
|
+
asset_category=regscale_models.AssetCategory.Software,
|
|
542
|
+
component_type=regscale_models.ComponentType.Software,
|
|
543
|
+
component_names=["DynamoDB Tables"],
|
|
544
|
+
# Parent information
|
|
545
|
+
parent_id=self.plan_id,
|
|
546
|
+
parent_module="securityplans",
|
|
547
|
+
# Status and location
|
|
548
|
+
status=(
|
|
549
|
+
regscale_models.AssetStatus.Active
|
|
550
|
+
if table.get("TableStatus") == "ACTIVE"
|
|
551
|
+
else regscale_models.AssetStatus.Inactive
|
|
552
|
+
),
|
|
553
|
+
location=table.get("Region"),
|
|
554
|
+
# Hardware details
|
|
555
|
+
disk_storage=table.get("TableSizeBytes"),
|
|
556
|
+
# Cloud identifiers
|
|
557
|
+
external_id=table.get("TableName"),
|
|
558
|
+
aws_identifier=table.get("TableArn"),
|
|
559
|
+
# Additional metadata
|
|
560
|
+
manufacturer="AWS",
|
|
561
|
+
source_data=table,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
def parse_vpc(self, vpc: Dict[str, Any]) -> IntegrationAsset:
|
|
565
|
+
"""
|
|
566
|
+
Parse VPC data into RegScale asset format.
|
|
567
|
+
|
|
568
|
+
:param Dict[str, Any] vpc: VPC data
|
|
569
|
+
:return: RegScale asset
|
|
570
|
+
:rtype: IntegrationAsset
|
|
571
|
+
"""
|
|
572
|
+
# Get VPC name from tags
|
|
573
|
+
name = next((tag["Value"] for tag in vpc.get("Tags", []) if tag["Key"] == "Name"), vpc.get("VpcId", ""))
|
|
574
|
+
notes: str = "" # Initialize notes with type hint
|
|
575
|
+
if vpc.get("IsDefault"):
|
|
576
|
+
notes = "Default VPC\n" + notes
|
|
577
|
+
|
|
578
|
+
return IntegrationAsset(
|
|
579
|
+
# Required fields
|
|
580
|
+
name=name,
|
|
581
|
+
identifier=str(vpc.get("VpcId", "")),
|
|
582
|
+
asset_type=regscale_models.AssetType.NetworkRouter,
|
|
583
|
+
asset_category=regscale_models.AssetCategory.Hardware,
|
|
584
|
+
component_type=regscale_models.ComponentType.Hardware,
|
|
585
|
+
component_names=["VPCs"],
|
|
586
|
+
# Parent information
|
|
587
|
+
parent_id=self.plan_id,
|
|
588
|
+
parent_module="securityplans",
|
|
589
|
+
# Status and location
|
|
590
|
+
status=(
|
|
591
|
+
regscale_models.AssetStatus.Active
|
|
592
|
+
if vpc.get("State") == "available"
|
|
593
|
+
else regscale_models.AssetStatus.Inactive
|
|
594
|
+
),
|
|
595
|
+
location=vpc.get("Region"),
|
|
596
|
+
# Network information
|
|
597
|
+
vlan_id=vpc.get("VpcId"),
|
|
598
|
+
# Cloud identifiers
|
|
599
|
+
external_id=vpc.get("VpcId"),
|
|
600
|
+
aws_identifier=vpc.get("VpcId"),
|
|
601
|
+
# Additional metadata
|
|
602
|
+
manufacturer="AWS",
|
|
603
|
+
notes=f"CIDR: {vpc.get('CidrBlock')}",
|
|
604
|
+
source_data=vpc,
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
def parse_load_balancer(self, lb: Dict[str, Any]) -> IntegrationAsset:
|
|
608
|
+
"""
|
|
609
|
+
Parse Load Balancer data into RegScale asset format.
|
|
610
|
+
|
|
611
|
+
:param Dict[str, Any] lb: Load Balancer data
|
|
612
|
+
:return: RegScale asset
|
|
613
|
+
:rtype: IntegrationAsset
|
|
614
|
+
"""
|
|
615
|
+
name = lb.get("LoadBalancerName", "")
|
|
616
|
+
notes: str = "" # Initialize notes with type hint
|
|
617
|
+
if lb.get("Scheme"):
|
|
618
|
+
notes = f"Scheme: {lb.get('Scheme')}\n{notes}"
|
|
619
|
+
|
|
620
|
+
return IntegrationAsset(
|
|
621
|
+
# Required fields
|
|
622
|
+
name=name,
|
|
623
|
+
identifier=str(lb.get("LoadBalancerName", "")),
|
|
624
|
+
asset_type=regscale_models.AssetType.NetworkRouter,
|
|
625
|
+
asset_category=regscale_models.AssetCategory.Hardware,
|
|
626
|
+
component_type=regscale_models.ComponentType.Hardware,
|
|
627
|
+
component_names=["Load Balancers"],
|
|
628
|
+
# Parent information
|
|
629
|
+
parent_id=self.plan_id,
|
|
630
|
+
parent_module="securityplans",
|
|
631
|
+
# Network information
|
|
632
|
+
fqdn=lb.get("DNSName"),
|
|
633
|
+
vlan_id=lb.get("VpcId"),
|
|
634
|
+
# Status and location
|
|
635
|
+
status=(
|
|
636
|
+
regscale_models.AssetStatus.Active
|
|
637
|
+
if lb.get("State") == "active"
|
|
638
|
+
else regscale_models.AssetStatus.Inactive
|
|
639
|
+
),
|
|
640
|
+
location=lb.get("Region"),
|
|
641
|
+
# Cloud identifiers
|
|
642
|
+
external_id=lb.get("LoadBalancerName"),
|
|
643
|
+
aws_identifier=lb.get("LoadBalancerArn"),
|
|
644
|
+
# Additional metadata
|
|
645
|
+
manufacturer="AWS",
|
|
646
|
+
is_public_facing=lb.get("Scheme") == "internet-facing",
|
|
647
|
+
source_data=lb,
|
|
648
|
+
# Ports and protocols
|
|
649
|
+
ports_and_protocols=[
|
|
650
|
+
{"port": listener.get("Port"), "protocol": listener.get("Protocol")}
|
|
651
|
+
for listener in lb.get("Listeners", [])
|
|
652
|
+
],
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
def parse_ecr_repository(self, repo: Dict[str, Any]) -> IntegrationAsset:
|
|
656
|
+
"""
|
|
657
|
+
Parse ECR repository data into RegScale asset format.
|
|
658
|
+
|
|
659
|
+
:param Dict[str, Any] repo: ECR repository data
|
|
660
|
+
:return: RegScale asset
|
|
661
|
+
:rtype: IntegrationAsset
|
|
662
|
+
"""
|
|
663
|
+
name = repo.get("RepositoryName", "")
|
|
664
|
+
notes: str = "" # Initialize notes with type hint
|
|
665
|
+
if repo.get("ImageTagMutability"):
|
|
666
|
+
notes = f"Image Tag Mutability: {repo.get('ImageTagMutability')}\n{notes}"
|
|
667
|
+
if repo.get("ImageScanningConfiguration", {}).get("ScanOnPush"):
|
|
668
|
+
notes = "Scan on Push enabled\n" + notes
|
|
669
|
+
|
|
670
|
+
return IntegrationAsset(
|
|
671
|
+
# Required fields
|
|
672
|
+
name=name,
|
|
673
|
+
identifier=str(repo.get("RepositoryName", "")),
|
|
674
|
+
asset_type=regscale_models.AssetType.Other,
|
|
675
|
+
asset_category=regscale_models.AssetCategory.Software,
|
|
676
|
+
component_type=regscale_models.ComponentType.Software,
|
|
677
|
+
component_names=["ECR Repositories"],
|
|
678
|
+
# Parent information
|
|
679
|
+
parent_id=self.plan_id,
|
|
680
|
+
parent_module="securityplans",
|
|
681
|
+
# Status and location
|
|
682
|
+
status=regscale_models.AssetStatus.Active,
|
|
683
|
+
location=repo.get("Region"),
|
|
684
|
+
# Cloud identifiers
|
|
685
|
+
external_id=repo.get("RepositoryName"),
|
|
686
|
+
aws_identifier=repo.get("RepositoryArn"),
|
|
687
|
+
uri=repo.get("RepositoryUri"),
|
|
688
|
+
# Additional metadata
|
|
689
|
+
manufacturer="AWS",
|
|
690
|
+
source_data=repo,
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
def fetch_findings(self, *args, **kwargs) -> Iterator[IntegrationFinding]:
|
|
694
|
+
"""
|
|
695
|
+
Fetch security findings.
|
|
696
|
+
Currently, we're only doing inventory collection.
|
|
697
|
+
|
|
698
|
+
:yield: Iterator[IntegrationFinding]
|
|
699
|
+
"""
|
|
700
|
+
import boto3
|
|
701
|
+
|
|
702
|
+
from regscale.integrations.commercial.amazon.common import fetch_aws_findings
|
|
703
|
+
|
|
704
|
+
aws_secret_key_id = kwargs.get("aws_access_key_id") or os.getenv("AWS_ACCESS_KEY_ID")
|
|
705
|
+
aws_secret_access_key = kwargs.get("aws_secret_access_key") or os.getenv("AWS_SECRET_ACCESS_KEY")
|
|
706
|
+
region = kwargs.get("region")
|
|
707
|
+
if not aws_secret_key_id or not aws_secret_access_key:
|
|
708
|
+
raise ValueError(
|
|
709
|
+
"AWS Access Key ID and Secret Access Key are required.\nPlease update in environment "
|
|
710
|
+
"variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) or pass as arguments."
|
|
711
|
+
)
|
|
712
|
+
if not region:
|
|
713
|
+
logger.warning("AWS region not provided. Defaulting to 'us-east-1'.")
|
|
714
|
+
client = boto3.client(
|
|
715
|
+
"securityhub",
|
|
716
|
+
region_name=kwargs.get(region, "us-east-1"),
|
|
717
|
+
aws_access_key_id=aws_secret_key_id,
|
|
718
|
+
aws_secret_access_key=aws_secret_access_key,
|
|
719
|
+
)
|
|
720
|
+
aws_findings = fetch_aws_findings(aws_client=client)
|
|
721
|
+
self.num_findings_to_process = len(aws_findings)
|
|
722
|
+
for finding in aws_findings:
|
|
723
|
+
yield from iter(self.parse_finding(finding))
|
|
724
|
+
|
|
725
|
+
@staticmethod
|
|
726
|
+
def get_baseline(resource: dict) -> str:
|
|
727
|
+
"""
|
|
728
|
+
Get Baseline
|
|
729
|
+
|
|
730
|
+
:param dict resource: AWS Resource
|
|
731
|
+
:return: AWS Baseline string
|
|
732
|
+
:rtype: str
|
|
733
|
+
"""
|
|
734
|
+
baseline = resource.get("Type", "")
|
|
735
|
+
baseline_map = {
|
|
736
|
+
"AwsAccount": "AWS Account",
|
|
737
|
+
"AwsS3Bucket": "S3 Bucket",
|
|
738
|
+
"AwsIamRole": "IAM Role",
|
|
739
|
+
"AwsEc2Instance": "EC2 Instance",
|
|
740
|
+
}
|
|
741
|
+
return baseline_map.get(baseline, baseline)
|
|
742
|
+
|
|
743
|
+
@staticmethod
|
|
744
|
+
def extract_name_from_arn(arn: str) -> Optional[str]:
|
|
745
|
+
"""
|
|
746
|
+
Extract the name from an ARN.
|
|
747
|
+
|
|
748
|
+
:param str arn: The ARN to extract the name from
|
|
749
|
+
:return: The extracted name, or None if not found
|
|
750
|
+
:rtype: Optional[str]
|
|
751
|
+
"""
|
|
752
|
+
# Get the last part after the last '/'
|
|
753
|
+
try:
|
|
754
|
+
return arn.split("/")[-1]
|
|
755
|
+
except IndexError:
|
|
756
|
+
# For ARNs without '/', try getting the last part after ':'
|
|
757
|
+
try:
|
|
758
|
+
return arn.split(":")[-1]
|
|
759
|
+
except IndexError:
|
|
760
|
+
return None
|
|
761
|
+
|
|
762
|
+
def parse_finding(self, finding: dict) -> list[IntegrationFinding]:
|
|
763
|
+
"""
|
|
764
|
+
Parse AWS Security Hub to RegScale IntegrationFinding format.
|
|
765
|
+
|
|
766
|
+
:param dict finding: AWS Security Hub finding
|
|
767
|
+
:return: RegScale IntegrationFinding
|
|
768
|
+
:rtype: list[IntegrationFinding]
|
|
769
|
+
"""
|
|
770
|
+
findings = []
|
|
771
|
+
try:
|
|
772
|
+
for resource in finding["Resources"]:
|
|
773
|
+
status, results = determine_status_and_results(finding)
|
|
774
|
+
comments = get_comments(finding)
|
|
775
|
+
severity = check_finding_severity(comments)
|
|
776
|
+
friendly_sev = "low"
|
|
777
|
+
if severity in ["CRITICAL", "HIGH"]:
|
|
778
|
+
friendly_sev = "high"
|
|
779
|
+
elif severity in ["MEDIUM", "MODERATE"]:
|
|
780
|
+
friendly_sev = "moderate"
|
|
781
|
+
try:
|
|
782
|
+
days = self.app.config["issues"]["amazon"][friendly_sev]
|
|
783
|
+
except KeyError:
|
|
784
|
+
logger.warning("Invalid severity level: %s, defaulting to 30 day due date", severity)
|
|
785
|
+
days = 30
|
|
786
|
+
due_date = datetime_str(get_due_date(date_str(finding["CreatedAt"]), days))
|
|
787
|
+
|
|
788
|
+
plugin_name = next(iter(finding.get("Types", [])))
|
|
789
|
+
findings.append(
|
|
790
|
+
IntegrationFinding(
|
|
791
|
+
asset_identifier=self.extract_name_from_arn(resource["Id"]),
|
|
792
|
+
external_id=self.extract_name_from_arn(resource["Id"]),
|
|
793
|
+
control_labels=[], # Determine how to populate this
|
|
794
|
+
title=finding["Title"],
|
|
795
|
+
category="SecurityHub",
|
|
796
|
+
issue_title=finding["Title"],
|
|
797
|
+
severity=self.finding_severity_map.get(severity),
|
|
798
|
+
description=finding["Description"],
|
|
799
|
+
status=IssueStatus.Open if status == "Fail" else IssueStatus.Closed,
|
|
800
|
+
checklist_status=self.get_checklist_status(status),
|
|
801
|
+
vulnerability_number="",
|
|
802
|
+
results=results,
|
|
803
|
+
recommendation_for_mitigation=finding.get("Remediation", {})
|
|
804
|
+
.get("Recommendation", {})
|
|
805
|
+
.get("Text", ""),
|
|
806
|
+
comments=comments,
|
|
807
|
+
poam_comments=comments,
|
|
808
|
+
date_created=date_str(finding["CreatedAt"]),
|
|
809
|
+
due_date=due_date,
|
|
810
|
+
plugin_name=plugin_name,
|
|
811
|
+
baseline=self.get_baseline(resource),
|
|
812
|
+
observations=comments,
|
|
813
|
+
gaps="",
|
|
814
|
+
evidence="",
|
|
815
|
+
impact="",
|
|
816
|
+
vulnerability_type="Vulnerability Scan",
|
|
817
|
+
)
|
|
818
|
+
)
|
|
819
|
+
|
|
820
|
+
except Exception as e:
|
|
821
|
+
logger.error(f"Error parsing AWS Security Hub finding: {str(e)}", exc_info=True)
|
|
822
|
+
|
|
823
|
+
return findings
|