regscale-cli 6.27.2.0__py3-none-any.whl → 6.27.3.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 +1 -0
- regscale/core/app/internal/control_editor.py +73 -21
- regscale/core/app/internal/login.py +4 -1
- regscale/core/app/internal/model_editor.py +219 -64
- regscale/core/login.py +21 -4
- regscale/core/utils/date.py +77 -1
- regscale/integrations/commercial/aws/scanner.py +4 -1
- regscale/integrations/commercial/synqly/query_builder.py +4 -1
- regscale/integrations/control_matcher.py +78 -23
- regscale/integrations/public/csam/csam.py +572 -763
- regscale/integrations/public/csam/csam_agency_defined.py +179 -0
- regscale/integrations/public/csam/csam_common.py +154 -0
- regscale/integrations/public/csam/csam_controls.py +432 -0
- regscale/integrations/public/csam/csam_poam.py +124 -0
- regscale/integrations/public/fedramp/click.py +17 -4
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +271 -62
- regscale/integrations/public/fedramp/poam/scanner.py +74 -7
- regscale/integrations/scanner_integration.py +16 -1
- regscale/models/integration_models/cisa_kev_data.json +49 -19
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +35 -2
- regscale/models/integration_models/synqly_models/ocsf_mapper.py +41 -12
- regscale/models/platform.py +3 -0
- regscale/models/regscale_models/__init__.py +5 -0
- regscale/models/regscale_models/component.py +1 -1
- regscale/models/regscale_models/control_implementation.py +55 -24
- regscale/models/regscale_models/organization.py +3 -0
- regscale/models/regscale_models/regscale_model.py +17 -5
- regscale/models/regscale_models/security_plan.py +1 -0
- regscale/regscale.py +11 -1
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.27.3.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.27.3.0.dist-info}/RECORD +40 -36
- tests/regscale/core/test_login.py +171 -4
- tests/regscale/integrations/test_control_matcher.py +24 -0
- tests/regscale/models/test_control_implementation.py +118 -3
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.27.3.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.27.3.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.27.3.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.27.2.0.dist-info → regscale_cli-6.27.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Integrates CSAM agencydefineddata into RegScale"""
|
|
4
|
+
|
|
5
|
+
from rich.progress import track
|
|
6
|
+
import logging
|
|
7
|
+
from regscale.models.regscale_models import SecurityPlan, FormFieldValue
|
|
8
|
+
from regscale.integrations.public.csam.csam_common import retrieve_from_csam, set_custom_fields
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("regscale")
|
|
11
|
+
|
|
12
|
+
"""This should be refactored as a custom field to custom field parser"""
|
|
13
|
+
"""Add logic to grab all the fields from agencydefineddataitems"""
|
|
14
|
+
"""Create custom fields in RegScale under "CSAM custom fields tab in SSP"""
|
|
15
|
+
"""Shove in whatever attributename: value pairs"""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def update_ssp_agency_details(ssps: list, custom_fields_basic_map: dict) -> list:
|
|
19
|
+
"""
|
|
20
|
+
Update the Agency Details of the SSPs
|
|
21
|
+
This requires a call to the /system/{id}/agencydefineddataitems
|
|
22
|
+
endpoint
|
|
23
|
+
|
|
24
|
+
:param list ssps: list of RegScale SSPs
|
|
25
|
+
:param dict custom_fields_basic_map: map of custom fields in RegScale
|
|
26
|
+
:return: list of updated SSPs
|
|
27
|
+
:return_type: List[SecurityPlan]
|
|
28
|
+
"""
|
|
29
|
+
updated_ssps = []
|
|
30
|
+
# Check for no ssps:
|
|
31
|
+
if len(ssps) == 0:
|
|
32
|
+
return updated_ssps
|
|
33
|
+
|
|
34
|
+
# Check if existing customer
|
|
35
|
+
custom_fields_system_list = ["AI/ML Components", "IOT/OT"]
|
|
36
|
+
custom_fields_system_map = FormFieldValue.check_custom_fields(
|
|
37
|
+
custom_fields_system_list, "securityplans", "System Information"
|
|
38
|
+
)
|
|
39
|
+
if custom_fields_system_map:
|
|
40
|
+
# Specific
|
|
41
|
+
updated_ssps = update_specific_details(ssps, custom_fields_basic_map, custom_fields_system_map)
|
|
42
|
+
return updated_ssps
|
|
43
|
+
|
|
44
|
+
# Generic Agency
|
|
45
|
+
# STUB - REG-18030
|
|
46
|
+
|
|
47
|
+
return updated_ssps
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def update_specific_details(ssps: list, custom_fields_basic_map: dict, custom_fields_system_map: dict):
|
|
51
|
+
"""
|
|
52
|
+
Handles the CSAM custom fields for existing customer
|
|
53
|
+
|
|
54
|
+
:param ssps: List of SSP ids
|
|
55
|
+
:param custom_fields_basic_map: dictionary mapping custom field names to ids
|
|
56
|
+
:param custom_fields_system_map: dictionary mapping custom field names to ids
|
|
57
|
+
:return: list of ssps
|
|
58
|
+
:return_type: list
|
|
59
|
+
"""
|
|
60
|
+
updated_ssps = []
|
|
61
|
+
if len(ssps) == 0:
|
|
62
|
+
return updated_ssps
|
|
63
|
+
|
|
64
|
+
for index in track(
|
|
65
|
+
range(len(ssps)),
|
|
66
|
+
description=f"Importing {len(ssps)} SSP agency details...",
|
|
67
|
+
):
|
|
68
|
+
ssp = ssps[index]
|
|
69
|
+
csam_id = ssp.otherIdentifier
|
|
70
|
+
if not csam_id:
|
|
71
|
+
logger.error(f"Could not find CSAM ID for SSP {ssp.systemName} id: {ssp.id}")
|
|
72
|
+
continue
|
|
73
|
+
else:
|
|
74
|
+
updated_ssps.append(ssp)
|
|
75
|
+
|
|
76
|
+
result = retrieve_from_csam(
|
|
77
|
+
csam_endpoint=f"/CSAM/api/v1/systems/{csam_id}/agencydefineddataitems",
|
|
78
|
+
)
|
|
79
|
+
if len(result) == 0:
|
|
80
|
+
logger.error(
|
|
81
|
+
f"Could not retrieve details for CSAM ID {csam_id}. RegScale SSP: Name: {ssp.systemName} id: {ssp.id}"
|
|
82
|
+
)
|
|
83
|
+
continue
|
|
84
|
+
# Get the custom fields
|
|
85
|
+
set_agency_details(result, ssp, custom_fields_basic_map, custom_fields_system_map)
|
|
86
|
+
|
|
87
|
+
logger.info(f"Updated {len(updated_ssps)} Security Plans with Agency Details")
|
|
88
|
+
return updated_ssps
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def set_agency_details(result: list, ssp: SecurityPlan, custom_fields_basic_map: dict, custom_fields_system_map: dict):
|
|
92
|
+
"""
|
|
93
|
+
Loop through results of agencydefineddataitems
|
|
94
|
+
and set the custom fields in RegScale
|
|
95
|
+
|
|
96
|
+
:param list result: list of dict objects from CSAM
|
|
97
|
+
:param SecurityPlan ssp: RegScale Security Plan
|
|
98
|
+
:param dict custom_fields_basic_map: map of custom field names to ids
|
|
99
|
+
:param dict custom_fields_system_map: map of custom fields names to ids
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
field_values = []
|
|
103
|
+
# Update the fields we need
|
|
104
|
+
for item in result:
|
|
105
|
+
if item.get("attributeName") == "High Value Asset":
|
|
106
|
+
ssp.hva = True if item.get("value") == "1" else False
|
|
107
|
+
|
|
108
|
+
# Binary Values
|
|
109
|
+
elif item.get("attributeName") in ["External Web Interface", "CFO Designation", "Law Enforcement Sensitive"]:
|
|
110
|
+
field_values.append(set_binary_fields(item, ssp, custom_fields_basic_map))
|
|
111
|
+
|
|
112
|
+
elif item.get("attributeName") == "Cloud System":
|
|
113
|
+
ssp = set_cloud_system(ssp, item)
|
|
114
|
+
|
|
115
|
+
elif item.get("attributeName") == "Cloud Service Model":
|
|
116
|
+
ssp = set_cloud_service(ssp, item)
|
|
117
|
+
|
|
118
|
+
elif item.get("attributeName") == "HVA Identifier":
|
|
119
|
+
field_values.append(set_custom_fields(item, ssp, custom_fields_basic_map))
|
|
120
|
+
|
|
121
|
+
elif item.get("attributeName") in ["AI/ML Components", "IOT/OT"]:
|
|
122
|
+
field_values.append(set_custom_fields(item, ssp, custom_fields_system_map))
|
|
123
|
+
|
|
124
|
+
# Save the SSP & Custom Fields
|
|
125
|
+
ssp.defaultAssessmentDays = 0
|
|
126
|
+
ssp.save()
|
|
127
|
+
if len(field_values) > 0:
|
|
128
|
+
FormFieldValue.save_custom_fields(field_values)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def set_cloud_system(ssp: SecurityPlan, item: dict) -> SecurityPlan:
|
|
132
|
+
"""
|
|
133
|
+
Set the cloud system values in the SSP
|
|
134
|
+
:param SeucrityPlan ssp: RegScale Security Plan
|
|
135
|
+
:param dict item: record from CSAM
|
|
136
|
+
:return: SecurityPlan object with updated cloud system values
|
|
137
|
+
:return_type: SecurityPlan
|
|
138
|
+
"""
|
|
139
|
+
ssp.bDeployPublic = True if item.get("value") == "Public" else False
|
|
140
|
+
ssp.bDeployPrivate = True if item.get("value") == "Private" else False
|
|
141
|
+
ssp.bDeployHybrid = True if item.get("value") == "Hybrid" else False
|
|
142
|
+
ssp.bDeployGov = True if item.get("value") == "GovCloud" else False
|
|
143
|
+
ssp.bDeployOther = True if item.get("value") == "Community" else False
|
|
144
|
+
if ssp.bDeployHybrid or ssp.bDeployOther:
|
|
145
|
+
ssp.deployOtherRemarks = "Hybrid or Community"
|
|
146
|
+
|
|
147
|
+
return ssp
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def set_cloud_service(ssp: SecurityPlan, item: dict) -> SecurityPlan:
|
|
151
|
+
"""
|
|
152
|
+
Set the cloud service model values in the SSP
|
|
153
|
+
|
|
154
|
+
:param SecurityPlan ssp: RegScale Security Plan
|
|
155
|
+
:param dict item: record from CSAM
|
|
156
|
+
:return: Updated SecurityPlan object
|
|
157
|
+
:return_type: SecurityPlan
|
|
158
|
+
"""
|
|
159
|
+
ssp.bModelIaaS = True if "IaaS" in item.get("value") else False
|
|
160
|
+
ssp.bModelPaaS = True if "PaaS" in item.get("value") else False
|
|
161
|
+
ssp.bModelSaaS = True if "SaaS" in item.get("value") else False
|
|
162
|
+
return ssp
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def set_binary_fields(item: dict, ssp: SecurityPlan, custom_fields_map: dict) -> dict:
|
|
166
|
+
"""
|
|
167
|
+
Logic to set the custom fields were the source are binary
|
|
168
|
+
|
|
169
|
+
:param dict item: record from CSAM
|
|
170
|
+
:param SecurityPlan ssp: RegScale Security Plan
|
|
171
|
+
:param custom_fields_map: map of custom field names to ids
|
|
172
|
+
:return: RegScale custom fields records
|
|
173
|
+
:return_type: dict
|
|
174
|
+
"""
|
|
175
|
+
return {
|
|
176
|
+
"record_id": ssp.id,
|
|
177
|
+
"form_field_id": custom_fields_map[item.get("attributeName")],
|
|
178
|
+
"field_value": "Yes" if (item.get("value")) == "1" else "No",
|
|
179
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Integrates CSAM into RegScale"""
|
|
4
|
+
|
|
5
|
+
# standard python imports
|
|
6
|
+
import logging
|
|
7
|
+
from typing import List
|
|
8
|
+
from urllib.parse import urljoin
|
|
9
|
+
from regscale.core.app.application import Application
|
|
10
|
+
from regscale.core.app.api import Api
|
|
11
|
+
|
|
12
|
+
from regscale.models.regscale_models import SecurityPlan, Module
|
|
13
|
+
from regscale.models.regscale_models.regscale_model import RegScaleModel
|
|
14
|
+
from regscale.integrations.control_matcher import ControlMatcher
|
|
15
|
+
from regscale.models.regscale_models.form_field_value import FormFieldValue
|
|
16
|
+
|
|
17
|
+
SSP_BASIC_TAB = "Basic Info"
|
|
18
|
+
CSAM_FIELD_NAME = "CSAM Id"
|
|
19
|
+
FISMA_FIELD_NAME = "FISMA Id"
|
|
20
|
+
SYSTEM_ID = "System ID"
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger("regscale")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def set_custom_fields(item: dict, ssp: SecurityPlan, custom_fields_map: dict) -> dict:
|
|
26
|
+
"""
|
|
27
|
+
Set the custom fields for the SSP
|
|
28
|
+
|
|
29
|
+
:param dict item: record from CSAM
|
|
30
|
+
:param SecurityPlan ssp: RegScale Security Plan
|
|
31
|
+
:param dict custom_fields_map: map of custom fields in RegScale
|
|
32
|
+
:return: dictionary of field values to be saved
|
|
33
|
+
:return_type: dict
|
|
34
|
+
"""
|
|
35
|
+
return {
|
|
36
|
+
"record_id": ssp.id,
|
|
37
|
+
"record_module": "securityplans",
|
|
38
|
+
"form_field_id": custom_fields_map[item.get("attributeName")],
|
|
39
|
+
"field_value": str(item.get("value")),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def retrieve_custom_form_ssps_map(tab_name: str, field_form_id: int) -> dict:
|
|
44
|
+
"""
|
|
45
|
+
Retreives a list of the SSPs in RegScale
|
|
46
|
+
Returns a map of Custom Field Value: RegScale Id
|
|
47
|
+
|
|
48
|
+
:param str tab_name: The RegScale tab name where the custom field is located
|
|
49
|
+
:param int field_form_id: The RegScale Form Id of custom field
|
|
50
|
+
:param int tab_id: The RegScale tab id
|
|
51
|
+
:return: dictionary of FieldForm Id: regscale_ssp_id
|
|
52
|
+
:return_type: dict
|
|
53
|
+
"""
|
|
54
|
+
tab = Module.get_tab_by_name(regscale_module_name="securityplans", regscale_tab_name=tab_name)
|
|
55
|
+
|
|
56
|
+
field_form_map = {}
|
|
57
|
+
ssps = SecurityPlan.get_ssp_list()
|
|
58
|
+
form_values = []
|
|
59
|
+
for ssp in ssps:
|
|
60
|
+
form_values = FormFieldValue.get_field_values(
|
|
61
|
+
record_id=ssp["id"], module_name=SecurityPlan.get_module_slug(), form_id=tab.id
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
for form in form_values:
|
|
65
|
+
if form.formFieldId == field_form_id and form.data:
|
|
66
|
+
field_form_map[form.data] = ssp["id"]
|
|
67
|
+
form_values = []
|
|
68
|
+
return field_form_map
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def retrieve_ssps_custom_form_map(tab_name: str, field_form_id: int) -> dict:
|
|
72
|
+
"""
|
|
73
|
+
Retreives a list of the SSPs in RegScale
|
|
74
|
+
Returns a map of Custom Field Value: RegScale Id
|
|
75
|
+
|
|
76
|
+
:param str tab_name: The RegScale tab name where the custom field is located
|
|
77
|
+
:param int field_form_id: The RegScale Form Id of custom field
|
|
78
|
+
:param int tab_id: The RegScale tab id
|
|
79
|
+
:return: dictionary of FieldForm Id: regscale_ssp_id
|
|
80
|
+
:return_type: dict
|
|
81
|
+
"""
|
|
82
|
+
tab = Module.get_tab_by_name(regscale_module_name="securityplans", regscale_tab_name=tab_name)
|
|
83
|
+
|
|
84
|
+
field_form_map = {}
|
|
85
|
+
ssps = SecurityPlan.get_ssp_list()
|
|
86
|
+
form_values = []
|
|
87
|
+
for ssp in ssps:
|
|
88
|
+
form_values = FormFieldValue.get_field_values(
|
|
89
|
+
record_id=ssp["id"], module_name=SecurityPlan.get_module_slug(), form_id=tab.id
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
for form in form_values:
|
|
93
|
+
if form.formFieldId == field_form_id and form.data:
|
|
94
|
+
field_form_map[ssp["id"]] = form.data
|
|
95
|
+
form_values = []
|
|
96
|
+
return field_form_map
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def retrieve_from_csam(csam_endpoint: str) -> list:
|
|
100
|
+
"""
|
|
101
|
+
Connect to CSAM and retrieve data
|
|
102
|
+
|
|
103
|
+
:param str csam_endpoint: API Endpoint
|
|
104
|
+
:return: List of dict objects
|
|
105
|
+
:return_type: list
|
|
106
|
+
"""
|
|
107
|
+
logger.debug("Retrieving data from CSAM")
|
|
108
|
+
app = Application()
|
|
109
|
+
api = Api()
|
|
110
|
+
csam_token = app.config.get("csamToken")
|
|
111
|
+
csam_url = app.config.get("csamURL")
|
|
112
|
+
|
|
113
|
+
if "Bearer" not in csam_token:
|
|
114
|
+
csam_token = f"Bearer {csam_token}"
|
|
115
|
+
|
|
116
|
+
url = urljoin(csam_url, csam_endpoint)
|
|
117
|
+
headers = {
|
|
118
|
+
"Content-Type": "application/json",
|
|
119
|
+
"Accept": "application/json",
|
|
120
|
+
"Authorization": csam_token,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
issue_response = api.get(url=url, headers=headers)
|
|
124
|
+
if not issue_response or issue_response.status_code in [204, 404]:
|
|
125
|
+
logger.warning(f"Call to {url} Returned error: {issue_response.text}")
|
|
126
|
+
return []
|
|
127
|
+
if issue_response.ok:
|
|
128
|
+
return issue_response.json()
|
|
129
|
+
|
|
130
|
+
return []
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def fix_form_field_value(form_fields: list) -> list:
|
|
134
|
+
"""
|
|
135
|
+
Cleans up a list of FormFieldValue dicts to prevent
|
|
136
|
+
400 errors due to misformed values
|
|
137
|
+
|
|
138
|
+
:param form_fields: list of formFieldValue dicts
|
|
139
|
+
:return: list of fixed formFieldValues dicts
|
|
140
|
+
"""
|
|
141
|
+
new_field_values = []
|
|
142
|
+
for field_value in form_fields:
|
|
143
|
+
# Check if record_id, record_module, and form_field_id are set
|
|
144
|
+
if (field_value.get("record_id") is None) or (field_value.get("record_id") == 0):
|
|
145
|
+
continue
|
|
146
|
+
if (field_value.get("form_field_id") is None) or (field_value.get("form_field_id") == 0):
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
# Check if value == "None"
|
|
150
|
+
if field_value.get("field_value") == "None":
|
|
151
|
+
field_value["field_value"] = ""
|
|
152
|
+
|
|
153
|
+
new_field_values.append(field_value)
|
|
154
|
+
return new_field_values
|