regscale-cli 6.20.9.1__py3-none-any.whl → 6.21.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/_version.py +1 -1
- regscale/core/app/application.py +12 -5
- regscale/core/app/internal/set_permissions.py +58 -27
- regscale/integrations/commercial/defender.py +9 -0
- regscale/integrations/commercial/nessus/scanner.py +2 -0
- regscale/integrations/commercial/sonarcloud.py +35 -36
- regscale/integrations/commercial/synqly/ticketing.py +51 -0
- regscale/integrations/commercial/wizv2/async_client.py +325 -0
- regscale/integrations/commercial/wizv2/constants.py +756 -0
- regscale/integrations/commercial/wizv2/scanner.py +1301 -89
- regscale/integrations/commercial/wizv2/utils.py +280 -36
- regscale/integrations/commercial/wizv2/variables.py +2 -10
- regscale/integrations/integration_override.py +15 -6
- regscale/integrations/scanner_integration.py +221 -37
- regscale/integrations/variables.py +1 -0
- regscale/models/integration_models/amazon_models/inspector_scan.py +32 -57
- regscale/models/integration_models/aqua.py +92 -78
- regscale/models/integration_models/cisa_kev_data.json +47 -4
- regscale/models/integration_models/defenderimport.py +64 -59
- regscale/models/integration_models/ecr_models/ecr.py +100 -147
- regscale/models/integration_models/flat_file_importer/__init__.py +52 -38
- regscale/models/integration_models/ibm.py +29 -47
- regscale/models/integration_models/nexpose.py +156 -68
- regscale/models/integration_models/prisma.py +46 -66
- regscale/models/integration_models/qualys.py +99 -93
- regscale/models/integration_models/snyk.py +229 -158
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/veracode.py +15 -20
- regscale/models/integration_models/xray.py +276 -82
- regscale/models/regscale_models/__init__.py +13 -0
- regscale/models/regscale_models/classification.py +23 -0
- regscale/models/regscale_models/control_implementation.py +14 -12
- regscale/models/regscale_models/cryptography.py +56 -0
- regscale/models/regscale_models/deviation.py +4 -4
- regscale/models/regscale_models/group.py +3 -2
- regscale/models/regscale_models/interconnection.py +1 -1
- regscale/models/regscale_models/issue.py +140 -41
- regscale/models/regscale_models/milestone.py +40 -0
- regscale/models/regscale_models/property.py +0 -1
- regscale/models/regscale_models/rbac.py +22 -0
- regscale/models/regscale_models/regscale_model.py +29 -18
- regscale/models/regscale_models/team.py +55 -0
- {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/RECORD +56 -49
- tests/fixtures/test_fixture.py +58 -2
- tests/regscale/core/test_app.py +5 -3
- tests/regscale/integrations/test_integration_mapping.py +522 -40
- tests/regscale/integrations/test_issue_due_date.py +1 -1
- tests/regscale/integrations/test_property_and_milestone_creation.py +684 -0
- tests/regscale/integrations/test_update_finding_dates.py +336 -0
- tests/regscale/models/test_asset.py +406 -50
- tests/regscale/models/test_report.py +105 -29
- {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.9.1.dist-info → regscale_cli-6.21.0.0.dist-info}/top_level.txt +0 -0
|
@@ -54,6 +54,7 @@ from regscale.models import (
|
|
|
54
54
|
ControlImplementationStatus,
|
|
55
55
|
ImplementationObjective,
|
|
56
56
|
)
|
|
57
|
+
from regscale.models.regscale_models.compliance_settings import ComplianceSettings
|
|
57
58
|
from regscale.utils import PaginatedGraphQLClient
|
|
58
59
|
from regscale.utils.decorators import deprecated
|
|
59
60
|
|
|
@@ -781,8 +782,6 @@ def _sync_compliance(
|
|
|
781
782
|
fetch_regscale_data_job = compliance_job_progress.add_task(
|
|
782
783
|
"[#f68d1f]Fetching RegScale Catalog info for framework...", total=1
|
|
783
784
|
)
|
|
784
|
-
compliance_job_progress.update(report_job, completed=True, advance=1)
|
|
785
|
-
|
|
786
785
|
framework_mapping = {
|
|
787
786
|
"CSF": "NIST CSF v1.1",
|
|
788
787
|
"NIST800-53R5": "NIST SP 800-53 Revision 5",
|
|
@@ -836,8 +835,14 @@ def _sync_compliance(
|
|
|
836
835
|
finally:
|
|
837
836
|
compliance_job_progress.update(running_compliance_job, advance=1)
|
|
838
837
|
try:
|
|
838
|
+
controls_with_data = len(controls_to_reports)
|
|
839
|
+
logger.info(f"Creating assessments for {controls_with_data} controls with compliance data")
|
|
840
|
+
if controls_with_data == 0:
|
|
841
|
+
logger.warning("No controls have compliance data from Wiz")
|
|
842
|
+
return report_models
|
|
843
|
+
|
|
839
844
|
saving_regscale_data_job = compliance_job_progress.add_task(
|
|
840
|
-
"[#f68d1f]Saving RegScale data...", total=
|
|
845
|
+
"[#f68d1f]Saving RegScale data...", total=controls_with_data
|
|
841
846
|
)
|
|
842
847
|
create_assessment_from_compliance_report(
|
|
843
848
|
controls_to_reports=controls_to_reports,
|
|
@@ -851,8 +856,8 @@ def _sync_compliance(
|
|
|
851
856
|
except Exception:
|
|
852
857
|
error_message = traceback.format_exc()
|
|
853
858
|
logger.error(f"Error creating ControlImplementations from compliance report: {error_message}")
|
|
854
|
-
|
|
855
|
-
|
|
859
|
+
# Re-raise the exception so it's not silently swallowed
|
|
860
|
+
raise
|
|
856
861
|
return report_models
|
|
857
862
|
|
|
858
863
|
|
|
@@ -932,50 +937,199 @@ def create_assessment_from_compliance_report(
|
|
|
932
937
|
:rtype: None
|
|
933
938
|
"""
|
|
934
939
|
implementations = ControlImplementation.get_all_by_parent(parent_module=regscale_module, parent_id=regscale_id)
|
|
940
|
+
total_controls = len(controls_to_reports)
|
|
941
|
+
processed_count = 0
|
|
942
|
+
|
|
935
943
|
for control_id, reports in controls_to_reports.items():
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
+
try:
|
|
945
|
+
processed_count += 1
|
|
946
|
+
logger.debug(f"Processing control {control_id} ({processed_count}/{total_controls})")
|
|
947
|
+
|
|
948
|
+
control_record_id = None
|
|
949
|
+
for control in controls:
|
|
950
|
+
if control.get("controlId").lower() == control_id:
|
|
951
|
+
control_record_id = control.get("id")
|
|
952
|
+
break
|
|
953
|
+
|
|
954
|
+
filtered_results = [x for x in implementations if x.controlID == control_record_id]
|
|
955
|
+
|
|
956
|
+
start_time = time.time()
|
|
957
|
+
create_report_assessment(filtered_results, reports, control_id)
|
|
958
|
+
end_time = time.time()
|
|
959
|
+
logger.debug(f"Assessment creation for {control_id} took {end_time - start_time:.2f} seconds")
|
|
960
|
+
|
|
961
|
+
progress.update(task, advance=1)
|
|
962
|
+
logger.debug(f"Updated progress: {processed_count}/{total_controls}")
|
|
963
|
+
|
|
964
|
+
except Exception as e:
|
|
965
|
+
logger.error(f"Error processing control {control_id}: {e}")
|
|
966
|
+
# Still update progress even if there's an error
|
|
967
|
+
progress.update(task, advance=1)
|
|
944
968
|
|
|
945
969
|
|
|
946
970
|
def create_report_assessment(filtered_results: List, reports: List, control_id: str) -> None:
|
|
947
971
|
"""
|
|
948
|
-
Create report assessment
|
|
972
|
+
Create a single aggregated report assessment per control
|
|
949
973
|
|
|
950
974
|
:param List filtered_results: Filtered results
|
|
951
|
-
:param List reports:
|
|
975
|
+
:param List reports: List of ComplianceReport objects for this control
|
|
952
976
|
:param str control_id: Control ID
|
|
953
977
|
:return: None
|
|
954
978
|
:rtype: None
|
|
955
979
|
"""
|
|
980
|
+
logger.debug(f"Creating assessment for control {control_id} with {len(reports)} reports")
|
|
981
|
+
|
|
956
982
|
implementation = filtered_results[0] if len(filtered_results) > 0 else None
|
|
983
|
+
if not implementation or not reports:
|
|
984
|
+
logger.debug(
|
|
985
|
+
f"Skipping control {control_id}: implementation={bool(implementation)}, reports={len(reports) if reports else 0}"
|
|
986
|
+
)
|
|
987
|
+
return
|
|
988
|
+
|
|
989
|
+
# Aggregate results: Fail if ANY asset fails, Pass only if ALL pass
|
|
990
|
+
overall_result = "Pass"
|
|
991
|
+
pass_count = 0
|
|
992
|
+
fail_count = 0
|
|
993
|
+
|
|
994
|
+
# Collect detailed results for comprehensive reporting
|
|
995
|
+
asset_details = []
|
|
996
|
+
|
|
957
997
|
for report in reports:
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
998
|
+
if report.result == ComplianceCheckStatus.FAIL.value:
|
|
999
|
+
overall_result = "Fail"
|
|
1000
|
+
fail_count += 1
|
|
1001
|
+
else:
|
|
1002
|
+
pass_count += 1
|
|
1003
|
+
|
|
1004
|
+
# Collect asset details for the report
|
|
1005
|
+
asset_details.append(
|
|
1006
|
+
{
|
|
1007
|
+
"resource_name": report.resource_name,
|
|
1008
|
+
"resource_id": report.resource_id,
|
|
1009
|
+
"cloud_provider": report.cloud_provider,
|
|
1010
|
+
"subscription": report.subscription,
|
|
1011
|
+
"result": report.result,
|
|
1012
|
+
"policy_short_name": report.policy_short_name,
|
|
1013
|
+
"compliance_check": report.compliance_check,
|
|
1014
|
+
"severity": report.severity,
|
|
1015
|
+
"assessed_at": report.assessed_at,
|
|
1016
|
+
}
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
# Create comprehensive HTML summary
|
|
1020
|
+
html_summary = _create_aggregated_assessment_report(
|
|
1021
|
+
control_id=control_id,
|
|
1022
|
+
overall_result=overall_result,
|
|
1023
|
+
pass_count=pass_count,
|
|
1024
|
+
fail_count=fail_count,
|
|
1025
|
+
asset_details=asset_details,
|
|
1026
|
+
total_assets=len(reports),
|
|
1027
|
+
)
|
|
1028
|
+
|
|
1029
|
+
# Create single assessment for this control
|
|
1030
|
+
assessment = Assessment(
|
|
1031
|
+
leadAssessorId=implementation.createdById,
|
|
1032
|
+
title=f"Wiz compliance assessment for {control_id}",
|
|
1033
|
+
assessmentType="Control Testing",
|
|
1034
|
+
plannedStart=get_current_datetime(),
|
|
1035
|
+
plannedFinish=get_current_datetime(),
|
|
1036
|
+
actualFinish=get_current_datetime(),
|
|
1037
|
+
assessmentResult=overall_result,
|
|
1038
|
+
assessmentReport=html_summary,
|
|
1039
|
+
status="Complete",
|
|
1040
|
+
parentId=implementation.id,
|
|
1041
|
+
parentModule="controls",
|
|
1042
|
+
isPublic=True,
|
|
1043
|
+
).create()
|
|
1044
|
+
|
|
1045
|
+
# Update implementation status once with aggregated result
|
|
1046
|
+
update_implementation_status(
|
|
1047
|
+
implementation=implementation,
|
|
1048
|
+
result=overall_result,
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
logger.info(
|
|
1052
|
+
f"Created aggregated assessment for {control_id}: {assessment.id} "
|
|
1053
|
+
f"(Result: {overall_result}, Assets: {len(reports)}, Pass: {pass_count}, Fail: {fail_count})"
|
|
1054
|
+
)
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
def _create_aggregated_assessment_report(
|
|
1058
|
+
control_id: str, overall_result: str, pass_count: int, fail_count: int, asset_details: List[Dict], total_assets: int
|
|
1059
|
+
) -> str:
|
|
1060
|
+
"""
|
|
1061
|
+
Create a comprehensive HTML assessment report for aggregated compliance results
|
|
1062
|
+
|
|
1063
|
+
:param str control_id: Control identifier
|
|
1064
|
+
:param str overall_result: Overall Pass/Fail result
|
|
1065
|
+
:param int pass_count: Number of passing assets
|
|
1066
|
+
:param int fail_count: Number of failing assets
|
|
1067
|
+
:param List[Dict] asset_details: Detailed information about each asset
|
|
1068
|
+
:param int total_assets: Total number of assets assessed
|
|
1069
|
+
:return: HTML formatted assessment report
|
|
1070
|
+
:rtype: str
|
|
1071
|
+
"""
|
|
1072
|
+
# Create summary section
|
|
1073
|
+
summary_html = f"""
|
|
1074
|
+
<div style="margin-bottom: 20px; padding: 15px; border: 2px solid {'#d32f2f' if overall_result == 'Fail' else '#2e7d32'}; border-radius: 5px; background-color: {'#ffebee' if overall_result == 'Fail' else '#e8f5e8'};">
|
|
1075
|
+
<h3 style="margin: 0 0 10px 0; color: {'#d32f2f' if overall_result == 'Fail' else '#2e7d32'};">
|
|
1076
|
+
Assessment Summary for Control {control_id}
|
|
1077
|
+
</h3>
|
|
1078
|
+
<p><strong>Overall Result:</strong> <span style="color: {'#d32f2f' if overall_result == 'Fail' else '#2e7d32'}; font-weight: bold;">{overall_result}</span></p>
|
|
1079
|
+
<p><strong>Total Assets Assessed:</strong> {total_assets}</p>
|
|
1080
|
+
<p><strong>Passing Assets:</strong> <span style="color: #2e7d32;">{pass_count}</span></p>
|
|
1081
|
+
<p><strong>Failing Assets:</strong> <span style="color: #d32f2f;">{fail_count}</span></p>
|
|
1082
|
+
<p><strong>Assessment Date:</strong> {get_current_datetime()}</p>
|
|
1083
|
+
</div>
|
|
1084
|
+
"""
|
|
1085
|
+
|
|
1086
|
+
# Create detailed asset results table
|
|
1087
|
+
if asset_details:
|
|
1088
|
+
table_rows = []
|
|
1089
|
+
for asset in asset_details:
|
|
1090
|
+
result_color = "#d32f2f" if asset["result"] == "Fail" else "#2e7d32"
|
|
1091
|
+
table_rows.append(
|
|
1092
|
+
f"""
|
|
1093
|
+
<tr>
|
|
1094
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('resource_name', 'N/A')}</td>
|
|
1095
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('resource_id', 'N/A')}</td>
|
|
1096
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('cloud_provider', 'N/A')}</td>
|
|
1097
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('subscription', 'N/A')}</td>
|
|
1098
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd; color: {result_color}; font-weight: bold;">{asset.get('result', 'N/A')}</td>
|
|
1099
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('policy_short_name', 'N/A')}</td>
|
|
1100
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('compliance_check', 'N/A')}</td>
|
|
1101
|
+
<td style="padding: 8px; border-bottom: 1px solid #ddd;">{asset.get('severity', 'N/A')}</td>
|
|
1102
|
+
</tr>
|
|
1103
|
+
"""
|
|
977
1104
|
)
|
|
978
|
-
|
|
1105
|
+
|
|
1106
|
+
asset_table_html = f"""
|
|
1107
|
+
<div style="margin-top: 20px;">
|
|
1108
|
+
<h4>Detailed Asset Results</h4>
|
|
1109
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #ddd;">
|
|
1110
|
+
<thead>
|
|
1111
|
+
<tr style="background-color: #f5f5f5;">
|
|
1112
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Resource Name</th>
|
|
1113
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Resource ID</th>
|
|
1114
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Cloud Provider</th>
|
|
1115
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Subscription</th>
|
|
1116
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Result</th>
|
|
1117
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Policy</th>
|
|
1118
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Compliance Check</th>
|
|
1119
|
+
<th style="padding: 10px; border-bottom: 2px solid #ddd; text-align: left;">Severity</th>
|
|
1120
|
+
</tr>
|
|
1121
|
+
</thead>
|
|
1122
|
+
<tbody>
|
|
1123
|
+
{''.join(table_rows)}
|
|
1124
|
+
</tbody>
|
|
1125
|
+
</table>
|
|
1126
|
+
</div>
|
|
1127
|
+
"""
|
|
1128
|
+
else:
|
|
1129
|
+
asset_table_html = "<p><em>No asset details available.</em></p>"
|
|
1130
|
+
|
|
1131
|
+
# Combine summary and details
|
|
1132
|
+
return summary_html + asset_table_html
|
|
979
1133
|
|
|
980
1134
|
|
|
981
1135
|
def update_implementation_status(implementation: ControlImplementation, result: str) -> ControlImplementation:
|
|
@@ -1003,14 +1157,104 @@ def update_implementation_status(implementation: ControlImplementation, result:
|
|
|
1003
1157
|
logger.info(f"Updated implementation status for {implementation.id}: {implementation.status}")
|
|
1004
1158
|
|
|
1005
1159
|
|
|
1160
|
+
def get_wiz_compliance_settings():
|
|
1161
|
+
"""
|
|
1162
|
+
Get Wiz compliance settings for status mapping
|
|
1163
|
+
|
|
1164
|
+
:return: Compliance settings instance or None
|
|
1165
|
+
:rtype: Optional[ComplianceSettings]
|
|
1166
|
+
"""
|
|
1167
|
+
try:
|
|
1168
|
+
settings = ComplianceSettings.get_by_current_tenant()
|
|
1169
|
+
wiz_compliance_setting = next((comp for comp in settings if comp.title == "Wiz Compliance Setting"), None)
|
|
1170
|
+
if not wiz_compliance_setting:
|
|
1171
|
+
logger.debug("No Wiz Compliance Setting found, using default implementation status mapping")
|
|
1172
|
+
else:
|
|
1173
|
+
logger.debug("Using Wiz Compliance Setting for implementation status mapping")
|
|
1174
|
+
return wiz_compliance_setting
|
|
1175
|
+
except Exception as e:
|
|
1176
|
+
logger.debug(f"Error getting Wiz Compliance Setting: {e}")
|
|
1177
|
+
return None
|
|
1178
|
+
|
|
1179
|
+
|
|
1006
1180
|
def report_result_to_implementation_status(result: str) -> str:
|
|
1007
1181
|
"""
|
|
1008
|
-
Convert report result to implementation status
|
|
1182
|
+
Convert report result to implementation status using compliance settings if available
|
|
1009
1183
|
|
|
1010
1184
|
:param str result: Report result
|
|
1011
1185
|
:return: Implementation status
|
|
1012
1186
|
:rtype: str
|
|
1013
1187
|
"""
|
|
1188
|
+
compliance_settings = get_wiz_compliance_settings()
|
|
1189
|
+
|
|
1190
|
+
if compliance_settings:
|
|
1191
|
+
status = _get_status_from_compliance_settings(result, compliance_settings)
|
|
1192
|
+
if status:
|
|
1193
|
+
return status
|
|
1194
|
+
|
|
1195
|
+
# Fallback to default mapping
|
|
1196
|
+
return _get_default_status_mapping(result)
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
def _get_status_from_compliance_settings(result: str, compliance_settings) -> Optional[str]:
|
|
1200
|
+
"""
|
|
1201
|
+
Get implementation status from compliance settings
|
|
1202
|
+
|
|
1203
|
+
:param str result: Report result
|
|
1204
|
+
:param compliance_settings: Compliance settings object
|
|
1205
|
+
:return: Implementation status or None if not found
|
|
1206
|
+
:rtype: Optional[str]
|
|
1207
|
+
"""
|
|
1208
|
+
try:
|
|
1209
|
+
status_labels = compliance_settings.get_field_labels("implementationStatus")
|
|
1210
|
+
result_lower = result.lower()
|
|
1211
|
+
|
|
1212
|
+
for label in status_labels:
|
|
1213
|
+
status = _match_result_to_label(result_lower, label)
|
|
1214
|
+
if status:
|
|
1215
|
+
return status
|
|
1216
|
+
|
|
1217
|
+
logger.debug(f"No matching compliance setting found for result: {result}")
|
|
1218
|
+
return None
|
|
1219
|
+
|
|
1220
|
+
except Exception as e:
|
|
1221
|
+
logger.debug(f"Error using compliance settings for implementation status mapping: {e}")
|
|
1222
|
+
return None
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
def _match_result_to_label(result_lower: str, label: str) -> Optional[str]:
|
|
1226
|
+
"""
|
|
1227
|
+
Match a result to a status label based on predefined mappings
|
|
1228
|
+
|
|
1229
|
+
:param str result_lower: Lowercase result string
|
|
1230
|
+
:param str label: Status label to check
|
|
1231
|
+
:return: Matched label or None
|
|
1232
|
+
:rtype: Optional[str]
|
|
1233
|
+
"""
|
|
1234
|
+
label_lower = label.lower()
|
|
1235
|
+
|
|
1236
|
+
if result_lower == ComplianceCheckStatus.PASS.value.lower():
|
|
1237
|
+
return label if label_lower in ["implemented", "complete", "compliant"] else None
|
|
1238
|
+
|
|
1239
|
+
if result_lower == ComplianceCheckStatus.FAIL.value.lower():
|
|
1240
|
+
return (
|
|
1241
|
+
label
|
|
1242
|
+
if label_lower in ["inremediation", "in remediation", "remediation", "failed", "non-compliant"]
|
|
1243
|
+
else None
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
# Not implemented or other status
|
|
1247
|
+
return label if label_lower in ["notimplemented", "not implemented", "pending", "planned"] else None
|
|
1248
|
+
|
|
1249
|
+
|
|
1250
|
+
def _get_default_status_mapping(result: str) -> str:
|
|
1251
|
+
"""
|
|
1252
|
+
Get default status mapping for a result
|
|
1253
|
+
|
|
1254
|
+
:param str result: Report result
|
|
1255
|
+
:return: Default implementation status
|
|
1256
|
+
:rtype: str
|
|
1257
|
+
"""
|
|
1014
1258
|
if result == ComplianceCheckStatus.PASS.value:
|
|
1015
1259
|
return ControlImplementationStatus.Implemented.value
|
|
1016
1260
|
elif result == ComplianceCheckStatus.FAIL.value:
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"""Wiz Variables"""
|
|
4
4
|
|
|
5
5
|
from regscale.core.app.utils.variables import RsVariableType, RsVariablesMeta
|
|
6
|
+
from regscale.integrations.commercial.wizv2.constants import RECOMMENDED_WIZ_INVENTORY_TYPES
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
class WizVariables(metaclass=RsVariablesMeta):
|
|
@@ -22,16 +23,7 @@ class WizVariables(metaclass=RsVariablesMeta):
|
|
|
22
23
|
wizInventoryFilterBy: RsVariableType(
|
|
23
24
|
str,
|
|
24
25
|
'{"projectId": ["xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"], "type": ["API_GATEWAY"]}',
|
|
25
|
-
default="""{"type":
|
|
26
|
-
[ "API_GATEWAY", "BACKUP_SERVICE", "CDN", "CICD_SERVICE", "CLOUD_LOG_CONFIGURATION",
|
|
27
|
-
"CLOUD_ORGANIZATION", "CONTAINER", "CONTAINER_IMAGE", "CONTAINER_REGISTRY", "CONTAINER_SERVICE",
|
|
28
|
-
"CONTROLLER_REVISION", "DATABASE", "DATA_WORKLOAD", "DB_SERVER", "DOMAIN", "EMAIL_SERVICE", "ENCRYPTION_KEY",
|
|
29
|
-
"FILE_SYSTEM_SERVICE", "FIREWALL", "GATEWAY", "KUBERNETES_CLUSTER", "LOAD_BALANCER",
|
|
30
|
-
"MANAGED_CERTIFICATE", "MESSAGING_SERVICE", "NAMESPACE", "NETWORK_INTERFACE", "PRIVATE_ENDPOINT",
|
|
31
|
-
"PRIVATE_LINK", "RAW_ACCESS_POLICY", "REGISTERED_DOMAIN", "RESOURCE_GROUP", "SECRET",
|
|
32
|
-
"SECRET_CONTAINER", "SERVERLESS", "SERVERLESS_PACKAGE", "SERVICE_ACCOUNT", "SERVICE_CONFIGURATION",
|
|
33
|
-
"STORAGE_ACCOUNT", "SUBNET", "SUBSCRIPTION", "VIRTUAL_DESKTOP", "VIRTUAL_MACHINE",
|
|
34
|
-
"VIRTUAL_MACHINE_IMAGE", "VIRTUAL_NETWORK", "VOLUME", "WEB_SERVICE", "NETWORK_ADDRESS"] }""",
|
|
26
|
+
default="""{"type": ["%s"] }""" % '","'.join(RECOMMENDED_WIZ_INVENTORY_TYPES), # type: ignore
|
|
35
27
|
) # type: ignore
|
|
36
28
|
wizAccessToken: RsVariableType(str, "", sensitive=True, required=False) # type: ignore
|
|
37
29
|
wizClientId: RsVariableType(str, "", sensitive=True) # type: ignore
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
A simple singleton class that loads custom integration mappings, if available
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from regscale.core.app.application import Application
|
|
6
|
-
|
|
7
5
|
# pylint: disable=C0415
|
|
8
6
|
|
|
9
7
|
|
|
@@ -74,9 +72,12 @@ class IntegrationOverride:
|
|
|
74
72
|
:return: The mapped field name
|
|
75
73
|
:rtype: Optional[str]
|
|
76
74
|
"""
|
|
77
|
-
if integration and self.mapping_exists(integration, field_name):
|
|
75
|
+
if integration and field_name and self.mapping_exists(integration, field_name):
|
|
78
76
|
integration_map = self.mapping.get(integration.lower(), {})
|
|
79
|
-
|
|
77
|
+
# Find the actual key that matches case-insensitively
|
|
78
|
+
for key in integration_map.keys():
|
|
79
|
+
if key.lower() == field_name.lower():
|
|
80
|
+
return integration_map.get(key)
|
|
80
81
|
return None
|
|
81
82
|
|
|
82
83
|
def mapping_exists(self, integration: str, field_name: str) -> bool:
|
|
@@ -88,8 +89,16 @@ class IntegrationOverride:
|
|
|
88
89
|
:return: Whether the mapping exists
|
|
89
90
|
:rtype: bool
|
|
90
91
|
"""
|
|
92
|
+
if not integration or not field_name:
|
|
93
|
+
return False
|
|
91
94
|
the_map = self.mapping.get(integration.lower())
|
|
92
|
-
|
|
95
|
+
if not the_map:
|
|
96
|
+
return False
|
|
97
|
+
# Find the actual key that matches case-insensitively
|
|
98
|
+
for key in the_map.keys():
|
|
99
|
+
if key.lower() == field_name.lower():
|
|
100
|
+
return the_map.get(key) != "default"
|
|
101
|
+
return False
|
|
93
102
|
|
|
94
103
|
def field_map_validation(self, obj: Any, model_type: str) -> Optional[str]:
|
|
95
104
|
"""
|
|
@@ -131,7 +140,7 @@ class IntegrationOverride:
|
|
|
131
140
|
},
|
|
132
141
|
}
|
|
133
142
|
# The type an associated fields we are able to override. Limited for now.
|
|
134
|
-
supported_fields = {"asset": {"ipAddress", "name", "fqdn", "dns"}}
|
|
143
|
+
supported_fields = {"asset": {"ipAddress", "name", "fqdn", "dns"}, "issue": {"dateFirstDetected"}}
|
|
135
144
|
if regscale_field not in supported_fields.get(model_type.lower(), set()):
|
|
136
145
|
return match
|
|
137
146
|
|