regscale-cli 6.25.1.0__py3-none-any.whl → 6.27.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/airflow/hierarchy.py +2 -2
- regscale/core/app/application.py +19 -4
- regscale/core/app/internal/evidence.py +419 -2
- regscale/core/app/internal/login.py +0 -1
- regscale/core/app/utils/catalog_utils/common.py +1 -1
- regscale/dev/code_gen.py +24 -20
- regscale/integrations/commercial/jira.py +367 -126
- regscale/integrations/commercial/qualys/__init__.py +7 -8
- regscale/integrations/commercial/qualys/scanner.py +8 -3
- regscale/integrations/commercial/sicura/api.py +14 -13
- regscale/integrations/commercial/sicura/commands.py +8 -2
- regscale/integrations/commercial/sicura/scanner.py +49 -39
- regscale/integrations/commercial/stigv2/ckl_parser.py +5 -5
- regscale/integrations/commercial/synqly/assets.py +17 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
- regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
- regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
- regscale/integrations/commercial/tenablev2/commands.py +142 -1
- regscale/integrations/commercial/tenablev2/scanner.py +0 -1
- regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
- regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +64 -79
- regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
- regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
- regscale/integrations/commercial/wizv2/compliance_report.py +161 -165
- regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
- regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
- regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
- regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
- regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
- regscale/integrations/commercial/wizv2/issue.py +1 -1
- regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
- regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
- regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
- regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
- regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
- regscale/integrations/commercial/wizv2/reports.py +1 -1
- regscale/integrations/commercial/wizv2/sbom.py +1 -1
- regscale/integrations/commercial/wizv2/scanner.py +39 -99
- regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
- regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
- regscale/integrations/commercial/wizv2/variables.py +89 -3
- regscale/integrations/compliance_integration.py +60 -41
- regscale/integrations/control_matcher.py +377 -0
- regscale/integrations/due_date_handler.py +14 -8
- regscale/integrations/milestone_manager.py +291 -0
- regscale/integrations/public/__init__.py +1 -0
- regscale/integrations/public/cci_importer.py +37 -38
- regscale/integrations/public/fedramp/click.py +60 -2
- regscale/integrations/public/fedramp/docx_parser.py +10 -1
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
- regscale/integrations/public/fedramp/fedramp_five.py +1 -1
- regscale/integrations/public/fedramp/poam_export_v5.py +888 -0
- regscale/integrations/scanner_integration.py +277 -153
- regscale/models/integration_models/cisa_kev_data.json +282 -9
- regscale/models/integration_models/nexpose.py +36 -10
- regscale/models/integration_models/qualys.py +3 -4
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
- regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
- regscale/models/locking.py +12 -8
- regscale/models/platform.py +1 -2
- regscale/models/regscale_models/control_implementation.py +47 -22
- regscale/models/regscale_models/issue.py +256 -95
- regscale/models/regscale_models/milestone.py +1 -1
- regscale/models/regscale_models/regscale_model.py +6 -1
- regscale/templates/__init__.py +0 -0
- {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/METADATA +1 -17
- {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/RECORD +145 -65
- tests/regscale/integrations/commercial/__init__.py +0 -0
- tests/regscale/integrations/commercial/conftest.py +28 -0
- tests/regscale/integrations/commercial/microsoft_defender/__init__.py +1 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender.py +1517 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_api.py +1748 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_constants.py +327 -0
- tests/regscale/integrations/commercial/microsoft_defender/test_defender_scanner.py +487 -0
- tests/regscale/integrations/commercial/test_aws.py +3731 -0
- tests/regscale/integrations/commercial/test_burp.py +48 -0
- tests/regscale/integrations/commercial/test_crowdstrike.py +49 -0
- tests/regscale/integrations/commercial/test_dependabot.py +341 -0
- tests/regscale/integrations/commercial/test_gcp.py +1543 -0
- tests/regscale/integrations/commercial/test_gitlab.py +549 -0
- tests/regscale/integrations/commercial/test_ip_mac_address_length.py +84 -0
- tests/regscale/integrations/commercial/test_jira.py +2204 -0
- tests/regscale/integrations/commercial/test_npm_audit.py +42 -0
- tests/regscale/integrations/commercial/test_okta.py +1228 -0
- tests/regscale/integrations/commercial/test_sarif_converter.py +251 -0
- tests/regscale/integrations/commercial/test_sicura.py +350 -0
- tests/regscale/integrations/commercial/test_snow.py +423 -0
- tests/regscale/integrations/commercial/test_sonarcloud.py +394 -0
- tests/regscale/integrations/commercial/test_sqlserver.py +186 -0
- tests/regscale/integrations/commercial/test_stig.py +33 -0
- tests/regscale/integrations/commercial/test_stig_mapper.py +153 -0
- tests/regscale/integrations/commercial/test_stigv2.py +406 -0
- tests/regscale/integrations/commercial/test_wiz.py +1365 -0
- tests/regscale/integrations/commercial/test_wiz_inventory.py +256 -0
- tests/regscale/integrations/commercial/wizv2/__init__.py +339 -0
- tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
- tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
- tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
- tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
- tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
- tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
- tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
- tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
- tests/regscale/integrations/commercial/wizv2/test_compliance_report_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
- tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
- tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
- tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
- tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
- tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
- tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1394 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_unit.py +341 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_control_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2.py +1132 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +519 -0
- tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
- tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
- tests/regscale/integrations/public/fedramp/__init__.py +1 -0
- tests/regscale/integrations/public/fedramp/test_poam_export_v5.py +1293 -0
- tests/regscale/integrations/public/test_fedramp.py +301 -0
- tests/regscale/integrations/test_control_matcher.py +1397 -0
- tests/regscale/integrations/test_control_matching.py +155 -0
- tests/regscale/integrations/test_milestone_manager.py +408 -0
- tests/regscale/models/test_issue.py +378 -1
- regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
- /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
- {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.25.1.0.dist-info → regscale_cli-6.27.0.0.dist-info}/top_level.txt +0 -0
|
@@ -61,7 +61,28 @@ class Vulnerabilities(SynqlyModel):
|
|
|
61
61
|
self.can_fetch_vulns = "query_findings" in self.capabilities
|
|
62
62
|
|
|
63
63
|
@staticmethod
|
|
64
|
-
def
|
|
64
|
+
def _build_severity_filter(severity: Optional[str] = None) -> str:
|
|
65
|
+
"""
|
|
66
|
+
Build severity filter string for Synqly queries
|
|
67
|
+
|
|
68
|
+
:param Optional[str] severity: Minimum severity level, if None, defaults to 'low'
|
|
69
|
+
:return: Comma-separated severity filter string
|
|
70
|
+
:rtype: str
|
|
71
|
+
"""
|
|
72
|
+
if not severity:
|
|
73
|
+
severity = "low"
|
|
74
|
+
severity_map = {
|
|
75
|
+
"critical": ["critical"],
|
|
76
|
+
"high": ["high", "critical"],
|
|
77
|
+
"medium": ["medium", "high", "critical"],
|
|
78
|
+
"low": ["low", "medium", "high", "critical"],
|
|
79
|
+
"info": ["info", "low", "medium", "high", "critical"],
|
|
80
|
+
}
|
|
81
|
+
mapped_severities = severity_map.get(severity.lower(), severity)
|
|
82
|
+
severity_filter = f"severity[in]{','.join(mapped_severities)}"
|
|
83
|
+
return severity_filter
|
|
84
|
+
|
|
85
|
+
def _handle_scan_date_options(self, regscale_ssp_id: int, **kwargs) -> list[str]:
|
|
65
86
|
"""
|
|
66
87
|
Handle scan date options for the integration sync process
|
|
67
88
|
|
|
@@ -71,12 +92,8 @@ class Vulnerabilities(SynqlyModel):
|
|
|
71
92
|
"""
|
|
72
93
|
from regscale.integrations.commercial.tenablev2.utils import get_last_pull_epoch
|
|
73
94
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if isinstance(vuln_filter, str):
|
|
77
|
-
vuln_filter = [f"severity[in]{vuln_filter}"]
|
|
78
|
-
else:
|
|
79
|
-
vuln_filter = ["severity[in]critical,high,medium,low"]
|
|
95
|
+
vuln_filter = [self._build_severity_filter(kwargs.get("minimum_severity_filter"))]
|
|
96
|
+
|
|
80
97
|
if kwargs.get("all_scans"):
|
|
81
98
|
vuln_filter.append("finding.last_seen_time[gte]915148800") # Friday, January 1, 1999 12:00:00 AM UTC
|
|
82
99
|
elif scan_date := kwargs.get("scan_date"):
|
|
@@ -243,7 +243,14 @@ class SynqlyModel(BaseModel, ABC):
|
|
|
243
243
|
config[f"{self._connector_type}_{self.integration}_{key}"] = attribute[key].default
|
|
244
244
|
elif not skip_prompts:
|
|
245
245
|
self.logger.info(f"Enter the {key} for {self.integration}. Description: {attribute[key].description}")
|
|
246
|
-
|
|
246
|
+
if key.lower() in ["secret", "password"] or "token" in key.lower():
|
|
247
|
+
from getpass import getpass
|
|
248
|
+
|
|
249
|
+
print(f"{attribute[key].description} (input will be hidden)")
|
|
250
|
+
provided_secret = getpass(f"{key}: ")
|
|
251
|
+
else:
|
|
252
|
+
print(f"{attribute[key].description} (input will be visible)")
|
|
253
|
+
provided_secret = input(f"{key}: ")
|
|
247
254
|
kwargs[key] = provided_secret
|
|
248
255
|
config[f"{self._connector_type}_{self.integration}_{key}"] = provided_secret
|
|
249
256
|
else:
|
regscale/models/locking.py
CHANGED
|
@@ -90,11 +90,15 @@ class FileLock:
|
|
|
90
90
|
|
|
91
91
|
:rtype: None
|
|
92
92
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
try:
|
|
94
|
+
if os.path.isfile(self.lock_file):
|
|
95
|
+
with open(self.lock_file, "r") as lockfile:
|
|
96
|
+
scope = str(lockfile.read().strip())
|
|
97
|
+
|
|
98
|
+
if scope == str(self.lock_scope):
|
|
99
|
+
os.remove(self.lock_file) # Lock released
|
|
100
|
+
else:
|
|
101
|
+
pass # Lock can only be released by {self.lock_scope}
|
|
102
|
+
except FileNotFoundError:
|
|
103
|
+
# Lock file was already removed by another process, this is acceptable in parallel execution
|
|
104
|
+
pass
|
regscale/models/platform.py
CHANGED
|
@@ -5,10 +5,9 @@ from typing import Optional, Union
|
|
|
5
5
|
|
|
6
6
|
from pydantic import BaseModel, SecretStr
|
|
7
7
|
|
|
8
|
+
from regscale.core.app.api import Api
|
|
8
9
|
from regscale.core.login import get_regscale_token
|
|
9
10
|
from regscale.core.utils.urls import generate_regscale_domain_url
|
|
10
|
-
from regscale.core.app.api import Api
|
|
11
|
-
from regscale.core.app.application import Application
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class RegScaleAuth(BaseModel):
|
|
@@ -10,7 +10,7 @@ from urllib.parse import urljoin
|
|
|
10
10
|
|
|
11
11
|
import requests
|
|
12
12
|
from lxml.etree import Element
|
|
13
|
-
from pydantic import ConfigDict, Field
|
|
13
|
+
from pydantic import ConfigDict, Field, field_validator
|
|
14
14
|
|
|
15
15
|
from regscale.core.app.api import Api
|
|
16
16
|
from regscale.core.app.application import Application
|
|
@@ -41,6 +41,8 @@ class ControlImplementationStatus(str, Enum):
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
class ImplementationControlOrigin(str, Enum):
|
|
44
|
+
"""Control Implementation Origination"""
|
|
45
|
+
|
|
44
46
|
SERVICE_PROVIDER_CORPORATE = "Service Provider Corporate"
|
|
45
47
|
SERVICE_PROVIDER_SYSTEM = "Service Provider System Specific"
|
|
46
48
|
SERVICE_PROVIDER_HYBRID = "Service Provider Hybrid (Corporate and System Specific)"
|
|
@@ -63,6 +65,7 @@ class ControlImplementationOrigin(str, Enum):
|
|
|
63
65
|
CustomerConfigured = "Customer Configured"
|
|
64
66
|
CustomerProvided = "Customer"
|
|
65
67
|
Inherited = "Inherited"
|
|
68
|
+
NotApplicable = "Not Applicable"
|
|
66
69
|
|
|
67
70
|
|
|
68
71
|
class ControlImplementation(RegScaleModel):
|
|
@@ -77,7 +80,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
77
80
|
controlOwnersIds: Optional[List[str]] = Field(default=None)
|
|
78
81
|
status: str # Required
|
|
79
82
|
controlID: int # Required foreign key to Security Control
|
|
80
|
-
status_lst: List[ControlImplementationStatus] = []
|
|
83
|
+
status_lst: List[ControlImplementationStatus] = Field(default=[], exclude=True)
|
|
81
84
|
id: int = 0
|
|
82
85
|
parentId: Optional[int] = None
|
|
83
86
|
parentModule: Optional[str] = None
|
|
@@ -85,7 +88,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
85
88
|
createdById: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
|
|
86
89
|
uuid: Optional[str] = None
|
|
87
90
|
policy: Optional[str] = None
|
|
88
|
-
implementation: Optional[str] =
|
|
91
|
+
implementation: Optional[str] = Field(default="N/A")
|
|
89
92
|
dateLastAssessed: Optional[str] = None
|
|
90
93
|
lastAssessmentResult: Optional[str] = None
|
|
91
94
|
practiceLevel: Optional[str] = None
|
|
@@ -115,7 +118,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
115
118
|
dateCreated: Optional[str] = Field(default_factory=get_current_datetime)
|
|
116
119
|
lastUpdatedById: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
|
|
117
120
|
dateLastUpdated: Optional[str] = Field(default_factory=get_current_datetime)
|
|
118
|
-
weight: Optional[
|
|
121
|
+
weight: Optional[float] = None
|
|
119
122
|
isPublic: Optional[bool] = True
|
|
120
123
|
inheritable: Optional[bool] = False
|
|
121
124
|
systemRoleId: Optional[int] = None
|
|
@@ -144,6 +147,20 @@ class ControlImplementation(RegScaleModel):
|
|
|
144
147
|
maturityLevel: Optional[str] = None
|
|
145
148
|
assessmentFrequency: int = 0
|
|
146
149
|
|
|
150
|
+
@field_validator("implementation", mode="before")
|
|
151
|
+
@classmethod
|
|
152
|
+
def validate_implementation(cls, v: Optional[str]) -> str:
|
|
153
|
+
"""
|
|
154
|
+
Validate implementation field - convert empty strings to 'N/A'.
|
|
155
|
+
|
|
156
|
+
:param Optional[str] v: The implementation value
|
|
157
|
+
:return: The validated implementation value
|
|
158
|
+
:rtype: str
|
|
159
|
+
"""
|
|
160
|
+
if v is None or (isinstance(v, str) and v.strip() == ""):
|
|
161
|
+
return "N/A"
|
|
162
|
+
return v
|
|
163
|
+
|
|
147
164
|
def __str__(self):
|
|
148
165
|
return f"Control Implementation {self.id}: {self.controlID}"
|
|
149
166
|
|
|
@@ -160,16 +177,21 @@ class ControlImplementation(RegScaleModel):
|
|
|
160
177
|
if self.controlOwnersIds is None and self.controlOwnerId:
|
|
161
178
|
self.controlOwnersIds = [self.controlOwnerId]
|
|
162
179
|
|
|
163
|
-
#
|
|
164
|
-
|
|
165
|
-
self.responsibility
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
self.
|
|
180
|
+
# Check if responsibility needs to be set (empty string, None, or default value)
|
|
181
|
+
should_update_responsibility = (
|
|
182
|
+
not self.responsibility # Handles empty string or None
|
|
183
|
+
or self.responsibility == self.get_default_responsibility()
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if should_update_responsibility:
|
|
187
|
+
if self.parentId and self.parentModule == "securityplans":
|
|
188
|
+
# Try to get a more specific default based on the actual security plan's compliance settings
|
|
189
|
+
better_default = self.get_default_responsibility(parent_id=self.parentId)
|
|
190
|
+
if better_default and better_default != self.responsibility:
|
|
191
|
+
self.responsibility = better_default
|
|
192
|
+
elif not self.responsibility:
|
|
193
|
+
# If still empty/None and no parent info, set to generic default
|
|
194
|
+
self.responsibility = self.get_default_responsibility()
|
|
173
195
|
|
|
174
196
|
def __setattr__(self, name: str, value: Any) -> None:
|
|
175
197
|
"""
|
|
@@ -207,8 +229,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
207
229
|
actual_compliance_setting_id = compliance_setting_id or cls._get_compliance_setting_id_from_parent(parent_id)
|
|
208
230
|
|
|
209
231
|
if actual_compliance_setting_id:
|
|
210
|
-
responsibility
|
|
211
|
-
if responsibility:
|
|
232
|
+
if responsibility := cls._get_responsibility_from_compliance_settings(actual_compliance_setting_id):
|
|
212
233
|
return responsibility
|
|
213
234
|
|
|
214
235
|
return cls._get_fallback_responsibility(actual_compliance_setting_id)
|
|
@@ -227,7 +248,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
227
248
|
try:
|
|
228
249
|
from regscale.models.regscale_models.security_plan import SecurityPlan
|
|
229
250
|
|
|
230
|
-
security_plan = SecurityPlan.get_object(parent_id)
|
|
251
|
+
security_plan: SecurityPlan = SecurityPlan.get_object(parent_id)
|
|
231
252
|
return security_plan.complianceSettingsId if security_plan else None
|
|
232
253
|
except Exception:
|
|
233
254
|
return None
|
|
@@ -240,14 +261,18 @@ class ControlImplementation(RegScaleModel):
|
|
|
240
261
|
|
|
241
262
|
Cached to avoid repeated API calls for the same compliance setting.
|
|
242
263
|
"""
|
|
264
|
+
responsibility = ControlImplementationOrigin.NotApplicable.value
|
|
243
265
|
try:
|
|
244
266
|
from regscale.models.regscale_models.compliance_settings import ComplianceSettings
|
|
245
267
|
|
|
246
|
-
|
|
268
|
+
if cs_responsibility := ComplianceSettings.get_default_responsibility_for_compliance_setting(
|
|
269
|
+
compliance_setting_id
|
|
270
|
+
):
|
|
271
|
+
responsibility = cs_responsibility
|
|
247
272
|
except Exception:
|
|
248
|
-
|
|
273
|
+
return responsibility
|
|
249
274
|
|
|
250
|
-
return
|
|
275
|
+
return responsibility
|
|
251
276
|
|
|
252
277
|
@classmethod
|
|
253
278
|
def _get_fallback_responsibility(cls, compliance_setting_id: Optional[int] = None) -> str:
|
|
@@ -262,7 +287,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
262
287
|
return cls._get_framework_default_responsibility(compliance_setting_id)
|
|
263
288
|
|
|
264
289
|
# Ultimate fallback for unknown compliance settings
|
|
265
|
-
return ControlImplementationOrigin.
|
|
290
|
+
return ControlImplementationOrigin.NotApplicable.value
|
|
266
291
|
|
|
267
292
|
@classmethod
|
|
268
293
|
def _get_framework_default_responsibility(cls, compliance_setting_id: int) -> str:
|
|
@@ -1146,7 +1171,7 @@ class ControlImplementation(RegScaleModel):
|
|
|
1146
1171
|
if field_name == "status":
|
|
1147
1172
|
return [imp_status.value for imp_status in ControlImplementationStatus]
|
|
1148
1173
|
if field_name == "responsibility":
|
|
1149
|
-
return [
|
|
1174
|
+
return [origin.value for origin in ControlImplementationOrigin]
|
|
1150
1175
|
return cls.get_bool_enums(field_name)
|
|
1151
1176
|
|
|
1152
1177
|
@classmethod
|