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,548 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module is used to parse a DOCX file containing FedRAMP Security Controls and their implementation statuses.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from typing import Dict, Union, Any, List, Optional
|
|
9
|
+
|
|
10
|
+
import docx
|
|
11
|
+
from lxml import etree
|
|
12
|
+
from rapidfuzz import fuzz
|
|
13
|
+
|
|
14
|
+
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
SCHEMA = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" # noqa
|
|
18
|
+
TEXT_ELEMENT = ".//{%s}%s" % (SCHEMA, "t")
|
|
19
|
+
CHECKBOX_ELEMENT = ".//{%s}%s" % (SCHEMA, "checkBox")
|
|
20
|
+
NA_STATUS = "Not Applicable"
|
|
21
|
+
|
|
22
|
+
# define our statuses we are looking for in the document
|
|
23
|
+
STATUSES = [
|
|
24
|
+
"Implemented",
|
|
25
|
+
"Partially Implemented",
|
|
26
|
+
"Planned",
|
|
27
|
+
"In Remediation",
|
|
28
|
+
"Inherited",
|
|
29
|
+
"Alternative Implementation",
|
|
30
|
+
NA_STATUS,
|
|
31
|
+
"Archived",
|
|
32
|
+
"Risk Accepted",
|
|
33
|
+
]
|
|
34
|
+
LOWER_STATUSES = [status.lower() for status in STATUSES]
|
|
35
|
+
|
|
36
|
+
ORIGINATIONS = [
|
|
37
|
+
"Service Provider Corporate",
|
|
38
|
+
"Service Provider System Specific",
|
|
39
|
+
"Service Provider Hybrid (Corporate and System Specific)",
|
|
40
|
+
"Configured by Customer (Customer System Specific)",
|
|
41
|
+
"Provided by Customer (Customer System Specific)",
|
|
42
|
+
"Shared (Service Provider and Customer Responsibility)",
|
|
43
|
+
"Inherited from pre-existing FedRAMP Authorization",
|
|
44
|
+
]
|
|
45
|
+
LOWER_ORIGINATIONS = [origin.lower() for origin in ORIGINATIONS]
|
|
46
|
+
DEFAULT_ORIGINATION = "Service Provider Corporate"
|
|
47
|
+
POSITIVE_KEYWORDS = ["yes", "true", "1", "☒", "True", "Yes", "☑", "☑️"]
|
|
48
|
+
|
|
49
|
+
# Define your keywords or phrases that map to each status
|
|
50
|
+
STATUS_KEYWORDS = {
|
|
51
|
+
"Implemented": ["implemented", "complete", "done", "yes", "☒", "1"],
|
|
52
|
+
"Partially Implemented": [
|
|
53
|
+
"partially implemented",
|
|
54
|
+
"incomplete",
|
|
55
|
+
"partially done",
|
|
56
|
+
"partial",
|
|
57
|
+
"In process",
|
|
58
|
+
"in process",
|
|
59
|
+
"☒",
|
|
60
|
+
"1",
|
|
61
|
+
],
|
|
62
|
+
"Planned": ["planned", "scheduled", "Planned", "☒", "1"],
|
|
63
|
+
"Alternative Implementation": [
|
|
64
|
+
"alternative implementation",
|
|
65
|
+
"alternative",
|
|
66
|
+
"Equivalent",
|
|
67
|
+
"☒",
|
|
68
|
+
"1",
|
|
69
|
+
],
|
|
70
|
+
NA_STATUS: ["not applicable", "irrelevant", "not relevant", "no", "☒", "1"],
|
|
71
|
+
}
|
|
72
|
+
DEFAULT_STATUS = "Not Implemented"
|
|
73
|
+
CONTROL_ORIGIN_KEY = "Control Origination"
|
|
74
|
+
CONTROL_SUMMARY_KEY = "Control Summary Information"
|
|
75
|
+
|
|
76
|
+
STATEMENT_CHECK = "What is the solution and how is it implemented".lower()
|
|
77
|
+
DEFAULT_PART = "Default Part"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class AppendixAParser:
|
|
81
|
+
"""
|
|
82
|
+
A class to parse a DOCX file containing FedRAMP Security Controls and their implementation statuses.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self, filename: str):
|
|
86
|
+
self.controls_implementations = {}
|
|
87
|
+
self.control_id = ""
|
|
88
|
+
self.doc = docx.Document(filename)
|
|
89
|
+
self.header_row_text = ""
|
|
90
|
+
self.cell_data_status = None
|
|
91
|
+
self.processed_texts = []
|
|
92
|
+
self.joined_processed_texts = ""
|
|
93
|
+
self.xml = None
|
|
94
|
+
self.text_elements = None
|
|
95
|
+
self.checkbox_states = None
|
|
96
|
+
self.cell_data = {}
|
|
97
|
+
self.parts = self.generate_parts_full_alphabet()
|
|
98
|
+
self.parts_set = {p.lower() for p in self.parts}
|
|
99
|
+
|
|
100
|
+
def fetch_controls_implementations(self) -> Dict:
|
|
101
|
+
"""
|
|
102
|
+
Fetch the implementation statuses of the controls from the DOCX file.
|
|
103
|
+
:return: A dictionary containing the control IDs and their implementation statuses.
|
|
104
|
+
:rtype: Dict
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
return self.get_implementation_statuses()
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def score_similarity(string1: str, string2: str) -> int:
|
|
111
|
+
"""
|
|
112
|
+
Score the similarity between two strings using the RapidFuzz library.
|
|
113
|
+
:param str string1: The first string to compare.
|
|
114
|
+
:param str string2: The second string to compare.
|
|
115
|
+
:return: The similarity score between the two strings.
|
|
116
|
+
:rtype: int
|
|
117
|
+
"""
|
|
118
|
+
# Scoring the similarity
|
|
119
|
+
score = fuzz.ratio(string1.lower(), string2.lower())
|
|
120
|
+
|
|
121
|
+
# Optionally, convert to a percentage
|
|
122
|
+
percentage = score # fuzz.ratio already gives a score out of 100
|
|
123
|
+
|
|
124
|
+
return round(percentage)
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
def determine_origination(text: str) -> Optional[str]:
|
|
128
|
+
tokens = text.split()
|
|
129
|
+
rejoined_text = " ".join(tokens) # this removes any newlines or spaces
|
|
130
|
+
rejoined_text = rejoined_text.replace("( ", "(")
|
|
131
|
+
rejoined_text = rejoined_text.replace(" )", ")")
|
|
132
|
+
|
|
133
|
+
if CONTROL_ORIGIN_KEY not in text:
|
|
134
|
+
return None
|
|
135
|
+
for origin in ORIGINATIONS:
|
|
136
|
+
for keyword in POSITIVE_KEYWORDS:
|
|
137
|
+
valid_option = f"{keyword} {origin}".lower()
|
|
138
|
+
lower_text = rejoined_text.lower()
|
|
139
|
+
if valid_option in lower_text:
|
|
140
|
+
return origin # Return the first matching status
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
@staticmethod
|
|
144
|
+
def determine_status(text: str) -> str:
|
|
145
|
+
# Tokenize the input text
|
|
146
|
+
tokens = text.split()
|
|
147
|
+
|
|
148
|
+
# Convert tokens to a single lowercased string for comparison
|
|
149
|
+
token_string = " ".join(tokens).lower()
|
|
150
|
+
|
|
151
|
+
matches = []
|
|
152
|
+
|
|
153
|
+
# Search for keywords in the tokenized text to determine the status
|
|
154
|
+
for status, keywords in STATUS_KEYWORDS.items():
|
|
155
|
+
for keyword in keywords:
|
|
156
|
+
if f"1 {keyword}" in token_string or f"☒ {keyword}" in token_string:
|
|
157
|
+
matches.append(status)
|
|
158
|
+
|
|
159
|
+
# Determine the status to return
|
|
160
|
+
if len(matches) > 1:
|
|
161
|
+
# More than one match found
|
|
162
|
+
# not applicable takes presendence over planned/partially implemented (only 2 valid multi select statuses for fedramp)
|
|
163
|
+
if matches[1] == NA_STATUS:
|
|
164
|
+
return matches[1]
|
|
165
|
+
else:
|
|
166
|
+
return matches[0]
|
|
167
|
+
elif matches:
|
|
168
|
+
return matches[0] # Return the first match if only one
|
|
169
|
+
else:
|
|
170
|
+
return DEFAULT_STATUS # No matches found
|
|
171
|
+
|
|
172
|
+
@staticmethod
|
|
173
|
+
def _process_text_element(input_text: str) -> Union[Dict, str]:
|
|
174
|
+
"""
|
|
175
|
+
Process a text element from a DOCX cell, checking for structured checkbox information.
|
|
176
|
+
:param str input_text: The text content of the element.
|
|
177
|
+
:return: The processed text or a dictionary containing checkbox information.
|
|
178
|
+
:rtype: Union[Dict, str]
|
|
179
|
+
"""
|
|
180
|
+
# Check if the text contains structured checkbox information
|
|
181
|
+
checkbox_info = re.findall(r"\[(.*?): (True|False)\]", input_text)
|
|
182
|
+
if checkbox_info:
|
|
183
|
+
return {item[0].strip(): item[1] == "True" for item in checkbox_info}
|
|
184
|
+
else:
|
|
185
|
+
return input_text
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def _get_checkbox_state(checkbox_element: Any) -> bool:
|
|
189
|
+
"""
|
|
190
|
+
Get the state of a checkbox element from a DOCX cell.
|
|
191
|
+
:param Any checkbox_element: The checkbox element from the DOCX cell.
|
|
192
|
+
:return: The state of the checkbox.
|
|
193
|
+
:rtype: bool
|
|
194
|
+
"""
|
|
195
|
+
# First, try getting the attribute 'val' directly
|
|
196
|
+
val = "{%s}%s" % (SCHEMA, "val")
|
|
197
|
+
checked = "{%s}%s" % (SCHEMA, "checked")
|
|
198
|
+
default = "{%s}%s" % (SCHEMA, "default")
|
|
199
|
+
state = checkbox_element.get(val)
|
|
200
|
+
if state is not None:
|
|
201
|
+
return state == "1"
|
|
202
|
+
|
|
203
|
+
# If not found, look for a child element 'checked' that may contain the 'val' attribute
|
|
204
|
+
checked_element = checkbox_element.find(checked)
|
|
205
|
+
if checked_element is not None:
|
|
206
|
+
state = checked_element.get(val)
|
|
207
|
+
return state == "1"
|
|
208
|
+
|
|
209
|
+
# If still not found, check for a 'default' state as a fallback
|
|
210
|
+
default_element = checkbox_element.find(default)
|
|
211
|
+
if default_element is not None:
|
|
212
|
+
state = default_element.get(val)
|
|
213
|
+
return state == "1"
|
|
214
|
+
|
|
215
|
+
# If there's no indication of the state, return False or handle accordingly
|
|
216
|
+
return False
|
|
217
|
+
|
|
218
|
+
def get_implementation_statuses(self) -> Dict:
|
|
219
|
+
"""
|
|
220
|
+
Get the implementation statuses of the controls from the DOCX file.
|
|
221
|
+
:return: A dictionary containing the control IDs and their implementation statuses.
|
|
222
|
+
:rtype: Dict
|
|
223
|
+
"""
|
|
224
|
+
for table in self.doc.tables:
|
|
225
|
+
for i, row in enumerate(table.rows):
|
|
226
|
+
self._handle_row(i, row)
|
|
227
|
+
|
|
228
|
+
logger.debug(f"Found {len(self.controls_implementations.items())} Controls")
|
|
229
|
+
return self.controls_implementations
|
|
230
|
+
|
|
231
|
+
def _handle_row(self, i: int, row: Any):
|
|
232
|
+
"""
|
|
233
|
+
Handle a row in the DOCX table.
|
|
234
|
+
:param int i: The index of the row.
|
|
235
|
+
:param Any row: The row element from the DOCX table.
|
|
236
|
+
"""
|
|
237
|
+
self.header_row_text = " ".join([c.text.strip() for c in row.cells]) if i == 0 else self.header_row_text
|
|
238
|
+
if CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower():
|
|
239
|
+
self.control_id = self.header_row_text.split(" ")[0] if self.header_row_text else None
|
|
240
|
+
if self.control_id not in self.controls_implementations:
|
|
241
|
+
self.controls_implementations[self.control_id] = {}
|
|
242
|
+
|
|
243
|
+
cells = row.cells
|
|
244
|
+
cell_count = len(cells)
|
|
245
|
+
self.handle_row_parts(cells, cell_count)
|
|
246
|
+
for cell_index, cell in enumerate(row.cells):
|
|
247
|
+
self._handle_cell(cell)
|
|
248
|
+
|
|
249
|
+
def handle_row_parts(self, cells: Any, cell_count: int) -> None:
|
|
250
|
+
"""
|
|
251
|
+
Handle the parts of the control implementation.
|
|
252
|
+
:param Any cells: The cells in the DOCX row.
|
|
253
|
+
:param int cell_count: The number of cells in the row.
|
|
254
|
+
:return: None
|
|
255
|
+
:rtype: None
|
|
256
|
+
"""
|
|
257
|
+
check = "what is the solution and how is it implemented".lower()
|
|
258
|
+
if check not in self.header_row_text.lower():
|
|
259
|
+
return
|
|
260
|
+
control_dict = self.controls_implementations.get(self.control_id, {})
|
|
261
|
+
self.handle_part(cells, cell_count, control_dict, check)
|
|
262
|
+
|
|
263
|
+
def handle_part(self, cells: Any, cell_count: int, control_dict: Dict, check: str):
|
|
264
|
+
"""
|
|
265
|
+
Handle the parts of the control implementation.
|
|
266
|
+
:param Any cells: The cells in the DOCX row.
|
|
267
|
+
:param int cell_count: The number of cells in the row.
|
|
268
|
+
:param Dict control_dict: The dictionary containing the control implementation data.
|
|
269
|
+
:param str check: The check string to exclude from the part value.
|
|
270
|
+
"""
|
|
271
|
+
if cell_count > 1:
|
|
272
|
+
name = self.get_cell_text(cells[0]) if cells[0].text else DEFAULT_PART
|
|
273
|
+
value = self.get_cell_text(cells[1])
|
|
274
|
+
part_list = control_dict.get("parts", [])
|
|
275
|
+
val_dict = {"name": name, "value": value}
|
|
276
|
+
if check not in value.lower() and val_dict not in part_list:
|
|
277
|
+
part_list.append(val_dict)
|
|
278
|
+
control_dict["parts"] = part_list
|
|
279
|
+
else:
|
|
280
|
+
value = self.get_cell_text(cells[0])
|
|
281
|
+
value_lower = value.lower()
|
|
282
|
+
pattern = re.compile(r"\b(" + "|".join(re.escape(part) for part in self.parts_set) + r")\b", re.IGNORECASE)
|
|
283
|
+
match = pattern.search(value_lower)
|
|
284
|
+
name = match.group(1) if match else DEFAULT_PART
|
|
285
|
+
part_list = control_dict.get("parts", [])
|
|
286
|
+
val_dict = {"name": name, "value": value}
|
|
287
|
+
if check.lower() not in value_lower and val_dict not in part_list:
|
|
288
|
+
part_list.append(val_dict)
|
|
289
|
+
control_dict["parts"] = part_list
|
|
290
|
+
|
|
291
|
+
def set_cell_text(self, cell: Any):
|
|
292
|
+
"""
|
|
293
|
+
Set the text content of the cell and process it.
|
|
294
|
+
:param Any cell: The cell element from the DOCX table.
|
|
295
|
+
"""
|
|
296
|
+
processed_texts = ""
|
|
297
|
+
self.xml = etree.fromstring(cell._element.xml)
|
|
298
|
+
self.text_elements = self.xml.findall(TEXT_ELEMENT)
|
|
299
|
+
self.checkbox_states = self.xml.findall(CHECKBOX_ELEMENT)
|
|
300
|
+
for element in self.text_elements:
|
|
301
|
+
if element.text:
|
|
302
|
+
processed_texts += self._process_text_element(element.text)
|
|
303
|
+
self.joined_processed_texts = re.sub(r"\.(?!\s|\d|$)", ". ", processed_texts)
|
|
304
|
+
|
|
305
|
+
def get_cell_text(self, cell: Any) -> str:
|
|
306
|
+
"""
|
|
307
|
+
Get the text content of the cell.
|
|
308
|
+
:param Any cell: The cell element from the DOCX table.
|
|
309
|
+
:return: The text content of the cell.
|
|
310
|
+
:rtype: str
|
|
311
|
+
"""
|
|
312
|
+
processed_texts = ""
|
|
313
|
+
xml = etree.fromstring(cell._element.xml)
|
|
314
|
+
text_elements = xml.findall(TEXT_ELEMENT)
|
|
315
|
+
for element in text_elements:
|
|
316
|
+
if element.text:
|
|
317
|
+
processed_texts += self._process_text_element(element.text)
|
|
318
|
+
return re.sub(r"\.(?!\s|\d|$)", ". ", processed_texts)
|
|
319
|
+
|
|
320
|
+
def _handle_cell(self, cell: Any):
|
|
321
|
+
"""
|
|
322
|
+
Handle a cell in the DOCX table.
|
|
323
|
+
:param Any cell: The cell element from the DOCX table.
|
|
324
|
+
"""
|
|
325
|
+
self.set_cell_text(cell)
|
|
326
|
+
self.cell_data = {}
|
|
327
|
+
self._handle_params()
|
|
328
|
+
self.cell_data_status = None
|
|
329
|
+
self._handle_checkbox_states()
|
|
330
|
+
self._handle_implementation_status()
|
|
331
|
+
self._handle_implementation_origination()
|
|
332
|
+
self._handle_implementation_statement()
|
|
333
|
+
# self._handle_implementation_parts(cell_index, cells)
|
|
334
|
+
self._handle_responsibility()
|
|
335
|
+
|
|
336
|
+
def _handle_params(self):
|
|
337
|
+
"""
|
|
338
|
+
Handle the parameters of the control implementation.
|
|
339
|
+
"""
|
|
340
|
+
if (
|
|
341
|
+
CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower()
|
|
342
|
+
and "parameter" in self.joined_processed_texts.lower()
|
|
343
|
+
and self.control_id in self.controls_implementations
|
|
344
|
+
):
|
|
345
|
+
control_dict = self.controls_implementations[self.control_id]
|
|
346
|
+
if "parameters" not in control_dict:
|
|
347
|
+
control_dict["parameters"] = []
|
|
348
|
+
# split the first occurrence of : to get the parameter name and value
|
|
349
|
+
parts = self.joined_processed_texts.split(":", 1)
|
|
350
|
+
param_text = self.joined_processed_texts
|
|
351
|
+
param = {"name": "Default Name", "value": "Default Value"}
|
|
352
|
+
if len(parts) == 2:
|
|
353
|
+
param["name"] = parts[0].strip().replace("Parameter", "")
|
|
354
|
+
param["value"] = parts[1].strip()
|
|
355
|
+
if param not in control_dict["parameters"]:
|
|
356
|
+
control_dict["parameters"].append(param)
|
|
357
|
+
else:
|
|
358
|
+
param["value"] = param_text.replace("parameters", "").strip()
|
|
359
|
+
if param not in control_dict["parameters"]:
|
|
360
|
+
control_dict["parameters"].append(param)
|
|
361
|
+
|
|
362
|
+
def _handle_implementation_origination(self):
|
|
363
|
+
"""
|
|
364
|
+
Handle the origination of the control implementation.
|
|
365
|
+
"""
|
|
366
|
+
if (
|
|
367
|
+
self.cell_data_status
|
|
368
|
+
and any(
|
|
369
|
+
[self.score_similarity(self.cell_data_status.lower(), origin) > 90 for origin in LOWER_ORIGINATIONS]
|
|
370
|
+
)
|
|
371
|
+
and CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower()
|
|
372
|
+
and CONTROL_ORIGIN_KEY.lower() in self.joined_processed_texts.lower()
|
|
373
|
+
and self.header_row_text.split(" ")[0] in self.controls_implementations
|
|
374
|
+
):
|
|
375
|
+
if self.control_id in self.controls_implementations:
|
|
376
|
+
control_dict = self.controls_implementations[self.control_id]
|
|
377
|
+
control_dict["origination"] = self.cell_data_status
|
|
378
|
+
elif origination := self.determine_origination(self.joined_processed_texts):
|
|
379
|
+
if origination in ORIGINATIONS:
|
|
380
|
+
if self.control_id in self.controls_implementations:
|
|
381
|
+
control_dict = self.controls_implementations[self.control_id]
|
|
382
|
+
control_dict["origination"] = origination
|
|
383
|
+
|
|
384
|
+
def _handle_implementation_status(self):
|
|
385
|
+
"""
|
|
386
|
+
Handle the implementation status of the control.
|
|
387
|
+
"""
|
|
388
|
+
if (
|
|
389
|
+
self.cell_data_status
|
|
390
|
+
and self.cell_data_status.lower() in LOWER_STATUSES
|
|
391
|
+
and CONTROL_SUMMARY_KEY in self.header_row_text
|
|
392
|
+
):
|
|
393
|
+
# logger.debug(header_row_text)
|
|
394
|
+
if self.control_id in self.controls_implementations:
|
|
395
|
+
control_dict = self.controls_implementations[self.control_id]
|
|
396
|
+
control_dict["status"] = self.cell_data_status
|
|
397
|
+
elif status := self.determine_status(self.joined_processed_texts):
|
|
398
|
+
if status.lower() in LOWER_STATUSES and CONTROL_SUMMARY_KEY in self.header_row_text:
|
|
399
|
+
if self.control_id in self.controls_implementations:
|
|
400
|
+
control_dict = self.controls_implementations[self.control_id]
|
|
401
|
+
control_dict["status"] = status
|
|
402
|
+
|
|
403
|
+
def _handle_implementation_statement(self):
|
|
404
|
+
"""
|
|
405
|
+
Handle the implementation statement of the control.
|
|
406
|
+
"""
|
|
407
|
+
|
|
408
|
+
value_check = f"{self.control_id} What is the solution and how is it implemented?"
|
|
409
|
+
if (
|
|
410
|
+
STATEMENT_CHECK in self.header_row_text.lower()
|
|
411
|
+
and value_check.lower() != self.joined_processed_texts.lower()
|
|
412
|
+
and self.control_id in self.controls_implementations
|
|
413
|
+
):
|
|
414
|
+
control_dict = self.controls_implementations.get(self.control_id, {})
|
|
415
|
+
imp_list = control_dict.get("statement", [])
|
|
416
|
+
if (
|
|
417
|
+
self.joined_processed_texts.strip() != ""
|
|
418
|
+
and STATEMENT_CHECK not in self.joined_processed_texts.strip().lower()
|
|
419
|
+
):
|
|
420
|
+
imp_list.append(self.joined_processed_texts.strip())
|
|
421
|
+
control_dict["statement"] = imp_list
|
|
422
|
+
|
|
423
|
+
@staticmethod
|
|
424
|
+
def generate_parts_full_alphabet() -> List[str]:
|
|
425
|
+
"""
|
|
426
|
+
Generates a list of strings in the format "part {letter}"
|
|
427
|
+
for each letter of the alphabet from 'a' to 'z'.
|
|
428
|
+
|
|
429
|
+
:return: A list of strings in the format "part {letter}"
|
|
430
|
+
:rtype: List[str]
|
|
431
|
+
"""
|
|
432
|
+
# Use chr to convert ASCII codes to letters: 97 is 'a', 122 is 'z'
|
|
433
|
+
parts = [f"part {chr(letter)}" for letter in range(97, 122 + 1)]
|
|
434
|
+
return parts
|
|
435
|
+
|
|
436
|
+
def _handle_implementation_parts(self, cell_index: int, cells: Any):
|
|
437
|
+
"""
|
|
438
|
+
Handle the implementation statement of the control.
|
|
439
|
+
"""
|
|
440
|
+
value_check = f"{self.control_id} What is the solution and how is it implemented?"
|
|
441
|
+
generic_value_check = "What is the solution and how is it implemented".lower()
|
|
442
|
+
if (
|
|
443
|
+
generic_value_check in self.header_row_text.lower()
|
|
444
|
+
and value_check.lower() != self.joined_processed_texts.lower()
|
|
445
|
+
and self.control_id in self.controls_implementations
|
|
446
|
+
):
|
|
447
|
+
part_value = self.joined_processed_texts.strip()
|
|
448
|
+
control_dict = self.controls_implementations.get(self.control_id, {})
|
|
449
|
+
part_list = control_dict.get("parts", [])
|
|
450
|
+
if any(
|
|
451
|
+
[
|
|
452
|
+
part_value.strip().lower() == p.lower() or part_value.strip().lower() == f"{p.lower()}:"
|
|
453
|
+
for p in self.parts
|
|
454
|
+
]
|
|
455
|
+
):
|
|
456
|
+
part_name = part_value.strip() or DEFAULT_PART
|
|
457
|
+
next_cell_text = self.get_cell_text(cells[cell_index + 1])
|
|
458
|
+
if ":" not in part_value:
|
|
459
|
+
part_value = ": ".join(
|
|
460
|
+
[
|
|
461
|
+
part_value.strip(),
|
|
462
|
+
next_cell_text.strip(),
|
|
463
|
+
]
|
|
464
|
+
)
|
|
465
|
+
else:
|
|
466
|
+
part_value = " ".join([part_value.strip(), next_cell_text.strip()])
|
|
467
|
+
self.build_part_dict(
|
|
468
|
+
part_name=part_name,
|
|
469
|
+
part_value=part_value,
|
|
470
|
+
control_dict=control_dict,
|
|
471
|
+
part_list=part_list,
|
|
472
|
+
generic_value_check=generic_value_check,
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
def build_part_dict(
|
|
476
|
+
self, part_name: str, part_value: str, control_dict: Dict, part_list: List, generic_value_check: str
|
|
477
|
+
):
|
|
478
|
+
"""
|
|
479
|
+
Build a dictionary for a part of the control implementation.
|
|
480
|
+
:param str part_name: The name of the part.
|
|
481
|
+
:param str part_value: The value of the part.
|
|
482
|
+
:param Dict control_dict: The dictionary containing the control implementation data.
|
|
483
|
+
:param List part_list: The list of parts in the control implementation.
|
|
484
|
+
:param str generic_value_check: The generic value check string.
|
|
485
|
+
"""
|
|
486
|
+
if part_value.lower().startswith("part"):
|
|
487
|
+
parts = part_value.split(":", 1)
|
|
488
|
+
part_dict = {"name": part_name, "value": DEFAULT_PART}
|
|
489
|
+
if len(parts) == 2 and parts[1].strip() != "":
|
|
490
|
+
part_dict["name"] = parts[0].strip()
|
|
491
|
+
part_dict["value"] = parts[1].strip()
|
|
492
|
+
logger.debug(f"Part: {part_dict}")
|
|
493
|
+
self.add_to_list(new_dict=part_dict, the_list=part_list)
|
|
494
|
+
elif part_value.strip() != "" and generic_value_check not in part_value.lower():
|
|
495
|
+
part_dict["value"] = part_value.strip()
|
|
496
|
+
self.add_to_list(new_dict=part_dict, the_list=part_list)
|
|
497
|
+
elif generic_value_check not in part_value.lower():
|
|
498
|
+
pdict = {
|
|
499
|
+
"name": DEFAULT_PART,
|
|
500
|
+
"value": part_value.strip(),
|
|
501
|
+
}
|
|
502
|
+
self.add_to_list(new_dict=pdict, the_list=part_list)
|
|
503
|
+
control_dict["parts"] = part_list
|
|
504
|
+
|
|
505
|
+
@staticmethod
|
|
506
|
+
def add_to_list(new_dict: Dict, the_list: List):
|
|
507
|
+
"""
|
|
508
|
+
Add a value to a list in the control dictionary.
|
|
509
|
+
:param Dict new_dict: The new dictionary to add to the list.
|
|
510
|
+
:param List the_list: The list to add the dictionary to.
|
|
511
|
+
"""
|
|
512
|
+
if new_dict not in the_list:
|
|
513
|
+
the_list.append(new_dict)
|
|
514
|
+
|
|
515
|
+
def _handle_responsibility(self):
|
|
516
|
+
"""
|
|
517
|
+
Handle the responsible roles of the control.
|
|
518
|
+
"""
|
|
519
|
+
if (
|
|
520
|
+
CONTROL_SUMMARY_KEY.lower() in self.header_row_text.lower()
|
|
521
|
+
and self.control_id in self.controls_implementations
|
|
522
|
+
and self.joined_processed_texts.lower().startswith("responsible role:")
|
|
523
|
+
):
|
|
524
|
+
control_dict = self.controls_implementations.get(self.control_id, {})
|
|
525
|
+
parts = self.joined_processed_texts.split(":")
|
|
526
|
+
if len(parts) == 2:
|
|
527
|
+
control_dict["responsibility"] = parts[1].strip()
|
|
528
|
+
|
|
529
|
+
def _handle_checkbox_states(self):
|
|
530
|
+
"""
|
|
531
|
+
Handle the checkbox states in the DOCX table.
|
|
532
|
+
"""
|
|
533
|
+
updated_checkbox_states = [self._get_checkbox_state(state) for state in self.checkbox_states]
|
|
534
|
+
for item in self.processed_texts[1:]:
|
|
535
|
+
if isinstance(item, dict):
|
|
536
|
+
self.cell_data.update(item)
|
|
537
|
+
else:
|
|
538
|
+
self.cell_data[item.strip()] = updated_checkbox_states.pop(0) if updated_checkbox_states else None
|
|
539
|
+
self._get_cell_data_status()
|
|
540
|
+
|
|
541
|
+
def _get_cell_data_status(self):
|
|
542
|
+
"""
|
|
543
|
+
Get the status of the cell data.
|
|
544
|
+
"""
|
|
545
|
+
if self.cell_data != {}:
|
|
546
|
+
for k, v in self.cell_data.items():
|
|
547
|
+
if v:
|
|
548
|
+
self.cell_data_status = k
|