regscale-cli 6.19.2.0__py3-none-any.whl → 6.20.1.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 -1
- regscale/airflow/config.py +2 -0
- regscale/airflow/tasks/groups.py +11 -47
- regscale/core/app/internal/login.py +49 -43
- regscale/core/app/internal/model_editor.py +2 -1
- regscale/dev/code_gen.py +2 -5
- regscale/integrations/commercial/synqly/assets.py +26 -0
- regscale/integrations/public/fedramp/appendix_parser.py +499 -104
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +5 -3
- regscale/integrations/public/fedramp/fedramp_five.py +89 -43
- regscale/models/integration_models/cisa_kev_data.json +277 -22
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/__init__.py +7 -0
- regscale/models/regscale_models/business_impact_assessment.py +71 -0
- regscale/models/regscale_models/control_implementation.py +15 -0
- regscale/models/regscale_models/evidence.py +72 -4
- regscale/models/regscale_models/evidence_mapping.py +1 -1
- regscale/models/regscale_models/master_assessment.py +19 -0
- regscale/models/regscale_models/policy.py +90 -0
- regscale/models/regscale_models/question.py +30 -2
- regscale/models/regscale_models/questionnaire.py +4 -3
- regscale/models/regscale_models/questionnaire_instance.py +37 -14
- regscale/models/regscale_models/rbac.py +0 -1
- regscale/models/regscale_models/risk_trend.py +67 -0
- regscale/models/regscale_models/task.py +14 -1
- {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/METADATA +114 -55
- {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/RECORD +31 -28
- {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/top_level.txt +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from .assessment import *
|
|
4
4
|
from .asset import *
|
|
5
5
|
from .asset_mapping import *
|
|
6
|
+
from .business_impact_assessment import *
|
|
6
7
|
from .catalog import *
|
|
7
8
|
from .cci import *
|
|
8
9
|
from .change import *
|
|
@@ -21,6 +22,8 @@ from .control_test_result import *
|
|
|
21
22
|
from .custom_field import *
|
|
22
23
|
from .data import *
|
|
23
24
|
from .data_center import *
|
|
25
|
+
from .evidence import *
|
|
26
|
+
from .evidence_mapping import *
|
|
24
27
|
from .email import *
|
|
25
28
|
from .facility import *
|
|
26
29
|
from .file import *
|
|
@@ -32,9 +35,11 @@ from .interconnection import *
|
|
|
32
35
|
from .issue import *
|
|
33
36
|
from .leveraged_authorization import *
|
|
34
37
|
from .link import *
|
|
38
|
+
from .master_assessment import *
|
|
35
39
|
from .meta_data import *
|
|
36
40
|
from .objective import *
|
|
37
41
|
from .parameter import *
|
|
42
|
+
from .policy import *
|
|
38
43
|
from .ports_protocol import *
|
|
39
44
|
from .privacy import *
|
|
40
45
|
from .profile import *
|
|
@@ -48,6 +53,7 @@ from .questionnaire_instance import *
|
|
|
48
53
|
from .reference import *
|
|
49
54
|
from .requirement import *
|
|
50
55
|
from .risk import *
|
|
56
|
+
from .risk_trend import *
|
|
51
57
|
from .sbom import *
|
|
52
58
|
from .scan_history import *
|
|
53
59
|
from .security_control import *
|
|
@@ -55,6 +61,7 @@ from .security_plan import *
|
|
|
55
61
|
from .software_inventory import *
|
|
56
62
|
from .stake_holder import *
|
|
57
63
|
from .stig import *
|
|
64
|
+
from .supply_chain import *
|
|
58
65
|
from .system_role import *
|
|
59
66
|
from .system_role_external_assignment import *
|
|
60
67
|
from .task import *
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the Business Impact Assessment model for RegScale.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import List
|
|
6
|
+
|
|
7
|
+
from pydantic import ConfigDict
|
|
8
|
+
|
|
9
|
+
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BusinessImpactAssessment(RegScaleModel):
|
|
13
|
+
"""RegScale Business Impact Assessment
|
|
14
|
+
|
|
15
|
+
:return: RegScale Business Impact Assessment
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
_module_slug = "business-impact-assessments"
|
|
19
|
+
_module_string = "business-impact-assessments"
|
|
20
|
+
# Should we include baseline, ruleId, check, and results in unique fields?
|
|
21
|
+
_unique_fields = [
|
|
22
|
+
[
|
|
23
|
+
"riskId",
|
|
24
|
+
"category",
|
|
25
|
+
],
|
|
26
|
+
]
|
|
27
|
+
_parent_id_field = "riskId"
|
|
28
|
+
# Required
|
|
29
|
+
id: int = 0
|
|
30
|
+
isPublic: bool = True
|
|
31
|
+
uuid: str = ""
|
|
32
|
+
category: str = ""
|
|
33
|
+
probability: str = ""
|
|
34
|
+
consequence: str = ""
|
|
35
|
+
notes: str = ""
|
|
36
|
+
riskType: str = ""
|
|
37
|
+
riskScore: int = 0
|
|
38
|
+
riskId: int = 0
|
|
39
|
+
|
|
40
|
+
@staticmethod
|
|
41
|
+
def _get_additional_endpoints() -> ConfigDict:
|
|
42
|
+
"""
|
|
43
|
+
Get additional endpoints for the Catalogues model
|
|
44
|
+
|
|
45
|
+
:return: A dictionary of additional endpoints
|
|
46
|
+
:rtype: ConfigDict
|
|
47
|
+
"""
|
|
48
|
+
return ConfigDict( # type: ignore
|
|
49
|
+
get_all_by_risk="/api/{model_slug}/getAllByRisk/{intID}",
|
|
50
|
+
get_all_by_risk_and_type="/api/{model_slug}/getAllByRiskAndType/{intID}/{strType}",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def get_all_by_risk(cls, risk_id: int) -> List["BusinessImpactAssessment"]:
|
|
55
|
+
"""
|
|
56
|
+
Get all Business Impact Assessments by risk ID
|
|
57
|
+
"""
|
|
58
|
+
endpoint = cls.get_endpoint("get_all_by_risk").format(model_slug=cls.get_module_slug(), intID=risk_id)
|
|
59
|
+
res = cls._handle_list_response(cls._get_api_handler().get(endpoint))
|
|
60
|
+
return res
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def get_all_by_risk_and_type(cls, risk_id: int, risk_type: str) -> List["BusinessImpactAssessment"]:
|
|
64
|
+
"""
|
|
65
|
+
Get all Business Impact Assessments by risk ID and type
|
|
66
|
+
"""
|
|
67
|
+
endpoint = cls.get_endpoint("get_all_by_risk_and_type").format(
|
|
68
|
+
model_slug=cls.get_module_slug(), intID=risk_id, strType=risk_type
|
|
69
|
+
)
|
|
70
|
+
res = cls._handle_list_response(cls._get_api_handler().get(endpoint))
|
|
71
|
+
return res
|
|
@@ -39,6 +39,21 @@ class ControlImplementationStatus(str, Enum):
|
|
|
39
39
|
Alternative = "Alternate Implementation"
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
class ImplementationControlOrigin(str, Enum):
|
|
43
|
+
SERVICE_PROVIDER_CORPORATE = "Service Provider Corporate"
|
|
44
|
+
SERVICE_PROVIDER_SYSTEM = "Service Provider System Specific"
|
|
45
|
+
SERVICE_PROVIDER_HYBRID = "Service Provider Hybrid (Corporate and System Specific)"
|
|
46
|
+
CONFIGURED_BY_CUSTOMER = "Configured by Customer (Customer System Specific)"
|
|
47
|
+
PROVIDED_BY_CUSTOMER = "Provided by Customer (Customer Specific)"
|
|
48
|
+
SHARED = "Shared (Service Provider and Customer Responsibility)"
|
|
49
|
+
INHERITED_FROM_PRE_EXISTING_FEDRAMP_AUTHORIZATION = (
|
|
50
|
+
"Inherited from pre-existing FedRAMP Authorization" # noqa: E501
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
return self.value
|
|
55
|
+
|
|
56
|
+
|
|
42
57
|
class ControlImplementationOrigin(str, Enum):
|
|
43
58
|
"""Control Origination"""
|
|
44
59
|
|
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
"""Class for evidence model in RegScale platform"""
|
|
4
4
|
|
|
5
5
|
from typing import Optional
|
|
6
|
+
|
|
6
7
|
from pydantic import ConfigDict, Field
|
|
7
8
|
|
|
9
|
+
from regscale.core.app.utils.app_utils import get_current_datetime
|
|
8
10
|
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
9
11
|
|
|
10
12
|
|
|
@@ -12,7 +14,7 @@ class Evidence(RegScaleModel):
|
|
|
12
14
|
"""Evidence Model"""
|
|
13
15
|
|
|
14
16
|
_module_slug = "evidence"
|
|
15
|
-
_unique_fields
|
|
17
|
+
_unique_fields = [["title"]]
|
|
16
18
|
|
|
17
19
|
id: int = 0
|
|
18
20
|
isPublic: Optional[bool] = True
|
|
@@ -20,9 +22,14 @@ class Evidence(RegScaleModel):
|
|
|
20
22
|
title: Optional[str] = None
|
|
21
23
|
description: Optional[str] = None
|
|
22
24
|
evidenceOwnerId: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
|
|
25
|
+
evidenceApproverId: Optional[str] = Field(default_factory=RegScaleModel.get_user_id)
|
|
26
|
+
status: Optional[str] = None
|
|
23
27
|
updateFrequency: Optional[int] = 365
|
|
24
|
-
lastEvidenceUpdate: Optional[str] =
|
|
25
|
-
dueDate: Optional[str] =
|
|
28
|
+
lastEvidenceUpdate: Optional[str] = Field(default_factory=get_current_datetime)
|
|
29
|
+
dueDate: Optional[str] = Field(default_factory=get_current_datetime)
|
|
30
|
+
dateLastApproved: Optional[str] = Field(default_factory=get_current_datetime)
|
|
31
|
+
|
|
32
|
+
_unique_fields = ["title"]
|
|
26
33
|
|
|
27
34
|
@staticmethod
|
|
28
35
|
def _get_additional_endpoints() -> ConfigDict:
|
|
@@ -42,6 +49,67 @@ class Evidence(RegScaleModel):
|
|
|
42
49
|
get_evidence_by_security_plan="/api/{model_slug}/getEvidenceBySecurityPlan/{intId}",
|
|
43
50
|
filter_evidence="/api/{model_slug}/filterEvidence",
|
|
44
51
|
query_by_custom_field="/api/{model_slug}/queryByCustomField/{strFieldName}/{strValue}",
|
|
45
|
-
mega_api="/api/{model_slug}/megaApi/{intId}",
|
|
46
52
|
get_my_evidence_due_soon="/api/{model_slug}/getMyEvidenceDueSoon/{intDays}/{intPage}/{intPageSize}",
|
|
47
53
|
)
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def filter_evidence(
|
|
57
|
+
cls,
|
|
58
|
+
field: Optional[str] = None,
|
|
59
|
+
type: Optional[str] = None,
|
|
60
|
+
operator: Optional[str] = None,
|
|
61
|
+
value: Optional[str] = None,
|
|
62
|
+
page: Optional[int] = 1,
|
|
63
|
+
page_size: Optional[int] = 10,
|
|
64
|
+
) -> list:
|
|
65
|
+
"""
|
|
66
|
+
Filter evidence based on the given filter parameters.
|
|
67
|
+
|
|
68
|
+
:param Optional[str] field: Filter field
|
|
69
|
+
:param Optional[str] type: Filter type
|
|
70
|
+
:param Optional[str] operator: Filter operator
|
|
71
|
+
:param Optional[str] value: Filter value
|
|
72
|
+
:param Optional[int] page: Page number, defaults to 1
|
|
73
|
+
:param Optional[int] page_size: Page size, defaults to 10
|
|
74
|
+
:return: A list of filtered evidence
|
|
75
|
+
:rtype: list
|
|
76
|
+
"""
|
|
77
|
+
query = {
|
|
78
|
+
"parentID": 0,
|
|
79
|
+
"module": "",
|
|
80
|
+
"friendlyName": "",
|
|
81
|
+
"workbench": "",
|
|
82
|
+
"base": "",
|
|
83
|
+
"sort": "dueDate",
|
|
84
|
+
"direction": "Ascending",
|
|
85
|
+
"simpleSearch": "",
|
|
86
|
+
"page": "",
|
|
87
|
+
"pageSize": "",
|
|
88
|
+
"query": {
|
|
89
|
+
"id": 0,
|
|
90
|
+
"viewName": "",
|
|
91
|
+
"module": "",
|
|
92
|
+
"scope": "",
|
|
93
|
+
"createdById": "",
|
|
94
|
+
"dateCreated": "",
|
|
95
|
+
"parameters": [
|
|
96
|
+
{"id": 0, "field": "", "type": "", "operator": "", "value": "", "viewName": "", "name": ""}
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
"groupBy": "",
|
|
100
|
+
"intDays": 0,
|
|
101
|
+
"subTab": False,
|
|
102
|
+
}
|
|
103
|
+
query["page"] = page
|
|
104
|
+
query["pageSize"] = page_size
|
|
105
|
+
query["query"]["parameters"][0]["field"] = field
|
|
106
|
+
query["query"]["parameters"][0]["type"] = type
|
|
107
|
+
query["query"]["parameters"][0]["operator"] = operator
|
|
108
|
+
query["query"]["parameters"][0]["value"] = value
|
|
109
|
+
|
|
110
|
+
response = cls._get_api_handler().post(
|
|
111
|
+
endpoint=cls.get_endpoint("filter_evidence").format(module_slug=cls._module_slug), data=query
|
|
112
|
+
)
|
|
113
|
+
if not response.raise_for_status():
|
|
114
|
+
return response.json()
|
|
115
|
+
return []
|
|
@@ -34,7 +34,7 @@ class EvidenceMapping(RegScaleModel):
|
|
|
34
34
|
:rtype: ConfigDict
|
|
35
35
|
"""
|
|
36
36
|
return ConfigDict(
|
|
37
|
-
|
|
37
|
+
get_all_by_parent="/api/{model_slug}/getAllByEvidence/{intParentID}",
|
|
38
38
|
get_summary_by_evidence="/api/{model_slug}/getSummaryByEvidence/{intID}",
|
|
39
39
|
batch_create="/api/{model_slug}/batchCreate",
|
|
40
40
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
"""Model for a RegScale Assessment"""
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from typing import List, Optional
|
|
5
6
|
|
|
6
7
|
from pydantic import ConfigDict, Field
|
|
@@ -9,6 +10,13 @@ from regscale.models.regscale_models.assessment import Assessment
|
|
|
9
10
|
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
10
11
|
|
|
11
12
|
|
|
13
|
+
class MasterAssessmentStatus(Enum):
|
|
14
|
+
"""Enum representing different status states with string values."""
|
|
15
|
+
|
|
16
|
+
InProgress = "In Progress"
|
|
17
|
+
Completed = "Completed"
|
|
18
|
+
|
|
19
|
+
|
|
12
20
|
class MasterAssessment(RegScaleModel):
|
|
13
21
|
"""
|
|
14
22
|
Model for a RegScale Assessment
|
|
@@ -125,3 +133,14 @@ class MasterAssessment(RegScaleModel):
|
|
|
125
133
|
if ci := cls.get_object(object_id=ci["id"]):
|
|
126
134
|
assessments.append(ci)
|
|
127
135
|
return assessments
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def is_date_field(cls, field_name: str) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Overrides the base method.
|
|
141
|
+
|
|
142
|
+
:param str field_name: The property name to provide enum values for
|
|
143
|
+
:return: bool if the field should be formatted as a date
|
|
144
|
+
:rtype: bool
|
|
145
|
+
"""
|
|
146
|
+
return field_name in ["plannedStart", "plannedFinish", "actualFinish"]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""This module contains the Policy models."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import ConfigDict
|
|
7
|
+
|
|
8
|
+
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PolicyParameter(RegScaleModel):
|
|
12
|
+
"""Policy Parameter Model"""
|
|
13
|
+
|
|
14
|
+
_module_slug = "policyparameter"
|
|
15
|
+
_unique_fields = [
|
|
16
|
+
["policyId", "name"],
|
|
17
|
+
]
|
|
18
|
+
id: int = 0
|
|
19
|
+
policyId: int
|
|
20
|
+
name: str = ""
|
|
21
|
+
value: str = ""
|
|
22
|
+
default: str = ""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Policy(RegScaleModel):
|
|
26
|
+
"""Policy Model"""
|
|
27
|
+
|
|
28
|
+
_module_slug = "policies"
|
|
29
|
+
_unique_fields = [
|
|
30
|
+
["integrationFindingId", "vulnerabilityId", "status"],
|
|
31
|
+
["otherIdentifier", "parentModule", "parentId", "status"],
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
id: int = 0
|
|
35
|
+
policyNumber: str = ""
|
|
36
|
+
policyOwnerId: str = ""
|
|
37
|
+
policyType: str = ""
|
|
38
|
+
dateApproved: str = ""
|
|
39
|
+
expirationDate: str = ""
|
|
40
|
+
status: str = ""
|
|
41
|
+
title: str = ""
|
|
42
|
+
description: str = ""
|
|
43
|
+
practiceLevel: str = ""
|
|
44
|
+
processLevel: str = ""
|
|
45
|
+
facilityId: Optional[int] = None
|
|
46
|
+
orgId: int = 0
|
|
47
|
+
parentModule: str = ""
|
|
48
|
+
parentId: int = 0
|
|
49
|
+
isPublic: bool = True
|
|
50
|
+
policyTemplate: str = ""
|
|
51
|
+
policyTemplateId: str = ""
|
|
52
|
+
policyParameters: List[PolicyParameter] = []
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def _get_additional_endpoints() -> ConfigDict:
|
|
56
|
+
"""
|
|
57
|
+
Get additional endpoints for the ProfileMapping model, using {model_slug} as a placeholder for the model slug.
|
|
58
|
+
|
|
59
|
+
:return: A dictionary of additional endpoints
|
|
60
|
+
:rtype: ConfigDict
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
return ConfigDict( # type: ignore
|
|
64
|
+
get_list="/api/{model_slug}/getList",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def get_list(cls) -> List[dict]:
|
|
69
|
+
"""
|
|
70
|
+
Get a list of policies.
|
|
71
|
+
|
|
72
|
+
:return: A list of policies
|
|
73
|
+
:rtype: List[dict]
|
|
74
|
+
"""
|
|
75
|
+
endpoint = cls.get_endpoint("get_list")
|
|
76
|
+
res = cls._get_api_handler().get(endpoint=endpoint)
|
|
77
|
+
return res.json()
|
|
78
|
+
|
|
79
|
+
def convert_datetime_to_date_str(self, dt: Optional[datetime]) -> str:
|
|
80
|
+
"""
|
|
81
|
+
Convert a datetime object to a date string in the format 'MMM DD, YYYY'.
|
|
82
|
+
|
|
83
|
+
:param dt: The datetime object to convert
|
|
84
|
+
:type dt: Optional[datetime]
|
|
85
|
+
:return: The date string in format 'MMM DD, YYYY'
|
|
86
|
+
:rtype: str
|
|
87
|
+
"""
|
|
88
|
+
if dt is None:
|
|
89
|
+
return ""
|
|
90
|
+
return dt.strftime("%b %d, %Y")
|
|
@@ -3,9 +3,13 @@ This module contains the Questions model for RegScale.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from enum import Enum
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Dict, List, Optional
|
|
7
|
+
|
|
7
8
|
from pydantic import ConfigDict, Field
|
|
9
|
+
|
|
10
|
+
from regscale.core.app.utils.api_handler import APIRetrieveError
|
|
8
11
|
from regscale.core.app.utils.app_utils import get_current_datetime
|
|
12
|
+
|
|
9
13
|
from .regscale_model import RegScaleModel
|
|
10
14
|
|
|
11
15
|
|
|
@@ -62,6 +66,30 @@ class Questions(RegScaleModel):
|
|
|
62
66
|
uploadEnabled: bool = False
|
|
63
67
|
response: Optional[str] = None # Adjust the type if it's not a string
|
|
64
68
|
|
|
69
|
+
@classmethod
|
|
70
|
+
def get_all_by_parent(cls, parent_questionnaire_id: int) -> List["Questions"]:
|
|
71
|
+
"""
|
|
72
|
+
Retrieve all questions associated with a parent questionnaire.
|
|
73
|
+
|
|
74
|
+
:param parent_questionnaire_id: The ID of the parent questionnaire to fetch questions for.
|
|
75
|
+
:return: A list of Questions objects associated with the parent questionnaire.
|
|
76
|
+
:raises ValueError: If parent_questionnaire_id is less than or equal to 0.
|
|
77
|
+
:raises APIRetrieveError: If the API request fails.
|
|
78
|
+
"""
|
|
79
|
+
if parent_questionnaire_id <= 0:
|
|
80
|
+
raise ValueError("parent_questionnaire_id must be a positive integer")
|
|
81
|
+
|
|
82
|
+
params = {"parentId": parent_questionnaire_id}
|
|
83
|
+
endpoint = cls.get_endpoint("get_all_by_parent").format(
|
|
84
|
+
model_slug=cls._module_slug,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
response = cls._get_api_handler().get(endpoint=endpoint, params=params)
|
|
88
|
+
if not response.ok:
|
|
89
|
+
raise APIRetrieveError(f"Failed to fetch questions: {response.status_code} - {response.text}")
|
|
90
|
+
|
|
91
|
+
return [cls(**ques) for ques in response.json()]
|
|
92
|
+
|
|
65
93
|
@staticmethod
|
|
66
94
|
def _get_additional_endpoints() -> ConfigDict:
|
|
67
95
|
"""
|
|
@@ -71,7 +99,7 @@ class Questions(RegScaleModel):
|
|
|
71
99
|
:rtype: ConfigDict
|
|
72
100
|
"""
|
|
73
101
|
return ConfigDict(
|
|
74
|
-
|
|
102
|
+
get_all_by_parent="/api/{model_slug}/getAllByParent",
|
|
75
103
|
get="/api/{model_slug}/find/{id}",
|
|
76
104
|
insert="/api/{model_slug}/create",
|
|
77
105
|
update="/api/{model_slug}/update",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
# pylint: disable=C0301
|
|
1
2
|
"""
|
|
2
3
|
This module contains the Questionnaires model in RegScale.
|
|
3
4
|
"""
|
|
4
5
|
|
|
5
6
|
import logging
|
|
6
|
-
from typing import
|
|
7
|
+
from typing import List, Optional
|
|
7
8
|
|
|
8
9
|
from pydantic import ConfigDict
|
|
9
10
|
|
|
@@ -19,6 +20,7 @@ class Questionnaires(RegScaleModel):
|
|
|
19
20
|
"""
|
|
20
21
|
|
|
21
22
|
_module_slug = "questionnaires"
|
|
23
|
+
_unique_fields = [["title", "parentQuestionnaireId"]]
|
|
22
24
|
|
|
23
25
|
id: Optional[int] = 0
|
|
24
26
|
uuid: Optional[str] = None
|
|
@@ -82,6 +84,5 @@ class Questionnaires(RegScaleModel):
|
|
|
82
84
|
if response.ok:
|
|
83
85
|
response_json = response.json()
|
|
84
86
|
return [QuestionnaireInstance(**instance) for instance in response_json.get("createdInstances", [])]
|
|
85
|
-
|
|
86
|
-
logger.info(f"Failed to create instances from questionnaires {response.status_code} - {response.text}")
|
|
87
|
+
logger.info("Failed to create instances from questionnaires %i - %s", response.status_code, response.text)
|
|
87
88
|
return None
|
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
This module contains the QuestionnaireInstances model.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from enum import Enum
|
|
6
|
-
from typing import
|
|
7
|
+
from typing import Any, Dict, List, Optional
|
|
7
8
|
|
|
8
|
-
from pydantic import
|
|
9
|
+
from pydantic import ConfigDict, Field
|
|
10
|
+
from requests import Response
|
|
9
11
|
|
|
10
12
|
from regscale.core.app.utils.app_utils import get_current_datetime
|
|
11
13
|
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
12
|
-
import logging
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger(__name__)
|
|
15
16
|
|
|
@@ -113,26 +114,48 @@ class QuestionnaireInstance(RegScaleModel):
|
|
|
113
114
|
return None
|
|
114
115
|
|
|
115
116
|
@classmethod
|
|
116
|
-
def
|
|
117
|
+
def _handle_response(cls, response: Optional[Response]) -> Optional[Dict[str, Any]]:
|
|
117
118
|
"""
|
|
118
|
-
|
|
119
|
+
Helper method to handle API responses consistently.
|
|
119
120
|
|
|
120
|
-
:param
|
|
121
|
-
:return: The response
|
|
121
|
+
:param Optional[Response] response: The response from the API
|
|
122
|
+
:return: The JSON response if successful, None otherwise
|
|
122
123
|
:rtype: Optional[Dict[str, Any]]
|
|
123
124
|
"""
|
|
124
|
-
endpoint = cls.get_endpoint("link_feedback").format(model_slug=cls._module_slug, id=id)
|
|
125
|
-
response = cls._get_api_handler().get(endpoint)
|
|
126
|
-
|
|
127
125
|
if not response or response.status_code in [204, 404]:
|
|
128
126
|
return None
|
|
129
127
|
if response.ok:
|
|
130
|
-
|
|
131
|
-
return response.json()
|
|
132
|
-
except Exception:
|
|
133
|
-
return None
|
|
128
|
+
return response.json()
|
|
134
129
|
return None
|
|
135
130
|
|
|
131
|
+
@classmethod
|
|
132
|
+
def submit_for_feedback(cls, quid: str, payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
133
|
+
"""
|
|
134
|
+
Submits a questionnaire instance for feedback.
|
|
135
|
+
|
|
136
|
+
:param str quid: The QUID of the questionnaire instance
|
|
137
|
+
:param Dict[str, Any] payload: The data to be sent in the request body
|
|
138
|
+
:return: The response from the API or None
|
|
139
|
+
:rtype: Optional[Dict[str, Any]]
|
|
140
|
+
"""
|
|
141
|
+
endpoint = cls.get_endpoint("submit_for_feedback").format(model_slug=cls._module_slug, uuid=quid)
|
|
142
|
+
response = cls._get_api_handler().put(endpoint, data=payload)
|
|
143
|
+
return cls._handle_response(response)
|
|
144
|
+
|
|
145
|
+
@classmethod
|
|
146
|
+
def update_response(cls, quid: str, payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
147
|
+
"""
|
|
148
|
+
Updates questionnaire instance responses.
|
|
149
|
+
|
|
150
|
+
:param str quid: The QUID of the questionnaire instance
|
|
151
|
+
:param Dict[str, Any] payload: The data to be sent in the request body containing instance and questions
|
|
152
|
+
:return: The response from the API or None
|
|
153
|
+
:rtype: Optional[Dict[str, Any]]
|
|
154
|
+
"""
|
|
155
|
+
endpoint = cls.get_endpoint("update_responses").format(model_slug=cls._module_slug, uuid=quid)
|
|
156
|
+
response = cls._get_api_handler().put(endpoint, data=payload)
|
|
157
|
+
return cls._handle_response(response)
|
|
158
|
+
|
|
136
159
|
@classmethod
|
|
137
160
|
def get_all_by_parent(cls, parent_questionnaire_id: int) -> List["QuestionnaireInstance"]:
|
|
138
161
|
"""
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the Risk Trends model for RegScale.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RiskTrend(RegScaleModel):
|
|
11
|
+
"""RegScale Risk Trends
|
|
12
|
+
|
|
13
|
+
:return: RegScale Business Impact Assessment
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
_module_slug = "riskTrends"
|
|
17
|
+
_module_string = "riskTrends"
|
|
18
|
+
# Should we include baseline, ruleId, check, and results in unique fields?
|
|
19
|
+
_unique_fields = [
|
|
20
|
+
[
|
|
21
|
+
"riskId",
|
|
22
|
+
"category",
|
|
23
|
+
],
|
|
24
|
+
]
|
|
25
|
+
_parent_id_field = "riskId"
|
|
26
|
+
# Required fields (Nullable = "NO")
|
|
27
|
+
id: int
|
|
28
|
+
analysis: str = ""
|
|
29
|
+
riskTrend: str = ""
|
|
30
|
+
costImpact: float = 0.0
|
|
31
|
+
scheduleImpact: int = 0
|
|
32
|
+
dateAssessed: str = ""
|
|
33
|
+
riskId: int
|
|
34
|
+
isPublic: bool = True
|
|
35
|
+
difference: float = 0.0
|
|
36
|
+
inherentConsequence: str
|
|
37
|
+
inherentProbability: str
|
|
38
|
+
inherentRiskScore: float = 0.0
|
|
39
|
+
|
|
40
|
+
residualConsequence: str
|
|
41
|
+
residualProbability: str
|
|
42
|
+
residualRisk: str
|
|
43
|
+
residualRiskScore: float = 0.0
|
|
44
|
+
|
|
45
|
+
targetRiskScore: float = 0.0
|
|
46
|
+
|
|
47
|
+
annualLossExpectancy: float = 0.0
|
|
48
|
+
|
|
49
|
+
expectedLost: float = 0.0
|
|
50
|
+
|
|
51
|
+
lossEventFrequency: float = 0.0
|
|
52
|
+
|
|
53
|
+
maximumLoss: float = 0.0
|
|
54
|
+
|
|
55
|
+
minimumLoss: float = 0.0
|
|
56
|
+
|
|
57
|
+
riskExposure: float = 0.0
|
|
58
|
+
threatEventFrequency: float = 0.0
|
|
59
|
+
|
|
60
|
+
# Optional fields (Nullable = "YES")
|
|
61
|
+
uuid: Optional[str] = None
|
|
62
|
+
mitigationEffectiveness: Optional[str] = None
|
|
63
|
+
threatChanges: Optional[str] = None
|
|
64
|
+
impactDescription: Optional[str] = None
|
|
65
|
+
operationalRequirements: Optional[str] = None
|
|
66
|
+
riskStrategy: Optional[str] = None
|
|
67
|
+
assumptions: Optional[str] = None
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
"""Model for Task in the application"""
|
|
4
4
|
|
|
5
|
-
from typing import
|
|
5
|
+
from typing import List, Optional
|
|
6
6
|
|
|
7
7
|
from pydantic import ConfigDict, Field, field_validator
|
|
8
8
|
|
|
@@ -61,6 +61,19 @@ class Task(RegScaleModel):
|
|
|
61
61
|
get_by_date_range="/api/{model_slug}/getByDateRangeAll/{dtStart}/{dtEnd}",
|
|
62
62
|
)
|
|
63
63
|
|
|
64
|
+
@classmethod
|
|
65
|
+
def other_identifier_starts_with(cls, starts_with: str) -> List["Task"]:
|
|
66
|
+
"""
|
|
67
|
+
Get tasks based on otherIdentifier field, starts with.
|
|
68
|
+
|
|
69
|
+
:param str starts_with: The starting characters of a string.
|
|
70
|
+
:return: A list of Task instances matching the query
|
|
71
|
+
:rtype: List[Task]
|
|
72
|
+
"""
|
|
73
|
+
endpoint = cls.get_endpoint("other_identifier_starts_with").format(strId=starts_with)
|
|
74
|
+
response = cls._get_api_handler().get(endpoint=endpoint)
|
|
75
|
+
return cls._handle_list_response(response)
|
|
76
|
+
|
|
64
77
|
@classmethod
|
|
65
78
|
def query_by_custom_field(cls, field_name: str, field_value: str) -> List["Task"]:
|
|
66
79
|
"""
|