regscale-cli 6.24.0.1__py3-none-any.whl → 6.25.0.1__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/api.py +1 -1
- regscale/core/app/application.py +5 -3
- regscale/core/app/internal/evidence.py +308 -202
- regscale/dev/code_gen.py +84 -3
- regscale/integrations/commercial/__init__.py +2 -0
- regscale/integrations/commercial/jira.py +4 -4
- regscale/integrations/commercial/microsoft_defender/defender.py +326 -5
- regscale/integrations/commercial/microsoft_defender/defender_api.py +348 -14
- regscale/integrations/commercial/microsoft_defender/defender_constants.py +157 -0
- regscale/integrations/commercial/synqly/assets.py +99 -16
- regscale/integrations/commercial/synqly/query_builder.py +533 -0
- regscale/integrations/commercial/synqly/vulnerabilities.py +134 -14
- regscale/integrations/commercial/wizv2/compliance_report.py +22 -0
- regscale/integrations/compliance_integration.py +17 -0
- regscale/integrations/scanner_integration.py +16 -0
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +12 -2
- regscale/models/integration_models/synqly_models/filter_parser.py +332 -0
- regscale/models/integration_models/synqly_models/synqly_model.py +47 -3
- regscale/models/regscale_models/compliance_settings.py +28 -0
- regscale/models/regscale_models/component.py +1 -0
- regscale/models/regscale_models/control_implementation.py +130 -1
- regscale/regscale.py +1 -1
- regscale/validation/record.py +23 -1
- {regscale_cli-6.24.0.1.dist-info → regscale_cli-6.25.0.1.dist-info}/METADATA +1 -1
- {regscale_cli-6.24.0.1.dist-info → regscale_cli-6.25.0.1.dist-info}/RECORD +31 -29
- {regscale_cli-6.24.0.1.dist-info → regscale_cli-6.25.0.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.24.0.1.dist-info → regscale_cli-6.25.0.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.24.0.1.dist-info → regscale_cli-6.25.0.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.24.0.1.dist-info → regscale_cli-6.25.0.1.dist-info}/top_level.txt +0 -0
|
@@ -14,6 +14,33 @@ def vulnerabilities() -> None:
|
|
|
14
14
|
pass
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
@vulnerabilities.command(name="build-query")
|
|
18
|
+
@click.option(
|
|
19
|
+
"--provider",
|
|
20
|
+
required=False,
|
|
21
|
+
help="Provider ID (e.g., vulnerabilities_armis_centrix). If not specified, starts interactive mode.",
|
|
22
|
+
)
|
|
23
|
+
@click.option("--validate", help="Validate a filter string against provider capabilities")
|
|
24
|
+
@click.option("--list-fields", is_flag=True, default=False, help="List all available fields for the provider")
|
|
25
|
+
def build_query(provider, validate, list_fields):
|
|
26
|
+
"""
|
|
27
|
+
Build and validate filter queries for Vulnerabilities connectors.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
# Build a filter query
|
|
31
|
+
regscale vulnerabilities build-query
|
|
32
|
+
|
|
33
|
+
# List all fields for a specific provider
|
|
34
|
+
regscale vulnerabilities build-query --provider vulnerabilities_armis_centrix --list-fields
|
|
35
|
+
|
|
36
|
+
# Validate a filter string
|
|
37
|
+
regscale vulnerabilities build-query --provider vulnerabilities_armis_centrix --validate "device.ip[eq]192.168.1.1"
|
|
38
|
+
"""
|
|
39
|
+
from regscale.integrations.commercial.synqly.query_builder import handle_build_query
|
|
40
|
+
|
|
41
|
+
handle_build_query("vulnerabilities", provider, validate, list_fields)
|
|
42
|
+
|
|
43
|
+
|
|
17
44
|
@vulnerabilities.command(name="sync_crowdstrike")
|
|
18
45
|
@regscale_ssp_id()
|
|
19
46
|
@click.option(
|
|
@@ -37,19 +64,33 @@ def vulnerabilities() -> None:
|
|
|
37
64
|
is_flag=True,
|
|
38
65
|
default=False,
|
|
39
66
|
)
|
|
67
|
+
@click.option(
|
|
68
|
+
"--asset_filter",
|
|
69
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
70
|
+
required=False,
|
|
71
|
+
type=str,
|
|
72
|
+
default=None,
|
|
73
|
+
)
|
|
40
74
|
@click.option(
|
|
41
75
|
"--url",
|
|
42
76
|
type=click.STRING,
|
|
43
77
|
help="Base URL for the CrowdStrike Falcon® Spotlight API.",
|
|
44
78
|
required=False,
|
|
45
79
|
)
|
|
46
|
-
def sync_crowdstrike(
|
|
80
|
+
def sync_crowdstrike(
|
|
81
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str, url: str
|
|
82
|
+
) -> None:
|
|
47
83
|
"""Sync Vulnerabilities from Crowdstrike to RegScale."""
|
|
48
84
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
49
85
|
|
|
50
86
|
vulnerabilities_crowdstrike = Vulnerabilities("crowdstrike")
|
|
51
87
|
vulnerabilities_crowdstrike.run_sync(
|
|
52
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
88
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
89
|
+
vuln_filter=vuln_filter,
|
|
90
|
+
scan_date=scan_date,
|
|
91
|
+
all_scans=all_scans,
|
|
92
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
93
|
+
url=url,
|
|
53
94
|
)
|
|
54
95
|
|
|
55
96
|
|
|
@@ -72,13 +113,26 @@ def sync_crowdstrike(regscale_ssp_id: int, vuln_filter: str, scan_date: datetime
|
|
|
72
113
|
@click.option(
|
|
73
114
|
"--all_scans", help="Whether to sync all vulnerabilities from Nucleus", required=False, is_flag=True, default=False
|
|
74
115
|
)
|
|
75
|
-
|
|
116
|
+
@click.option(
|
|
117
|
+
"--asset_filter",
|
|
118
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
119
|
+
required=False,
|
|
120
|
+
type=str,
|
|
121
|
+
default=None,
|
|
122
|
+
)
|
|
123
|
+
def sync_nucleus(
|
|
124
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str
|
|
125
|
+
) -> None:
|
|
76
126
|
"""Sync Vulnerabilities from Nucleus to RegScale."""
|
|
77
127
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
78
128
|
|
|
79
129
|
vulnerabilities_nucleus = Vulnerabilities("nucleus")
|
|
80
130
|
vulnerabilities_nucleus.run_sync(
|
|
81
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
131
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
132
|
+
vuln_filter=vuln_filter,
|
|
133
|
+
scan_date=scan_date,
|
|
134
|
+
all_scans=all_scans,
|
|
135
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
82
136
|
)
|
|
83
137
|
|
|
84
138
|
|
|
@@ -105,13 +159,26 @@ def sync_nucleus(regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, al
|
|
|
105
159
|
is_flag=True,
|
|
106
160
|
default=False,
|
|
107
161
|
)
|
|
108
|
-
|
|
162
|
+
@click.option(
|
|
163
|
+
"--asset_filter",
|
|
164
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
165
|
+
required=False,
|
|
166
|
+
type=str,
|
|
167
|
+
default=None,
|
|
168
|
+
)
|
|
169
|
+
def sync_qualys_cloud(
|
|
170
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str
|
|
171
|
+
) -> None:
|
|
109
172
|
"""Sync Vulnerabilities from Qualys Cloud to RegScale."""
|
|
110
173
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
111
174
|
|
|
112
175
|
vulnerabilities_qualys_cloud = Vulnerabilities("qualys_cloud")
|
|
113
176
|
vulnerabilities_qualys_cloud.run_sync(
|
|
114
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
177
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
178
|
+
vuln_filter=vuln_filter,
|
|
179
|
+
scan_date=scan_date,
|
|
180
|
+
all_scans=all_scans,
|
|
181
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
115
182
|
)
|
|
116
183
|
|
|
117
184
|
|
|
@@ -138,13 +205,26 @@ def sync_qualys_cloud(regscale_ssp_id: int, vuln_filter: str, scan_date: datetim
|
|
|
138
205
|
is_flag=True,
|
|
139
206
|
default=False,
|
|
140
207
|
)
|
|
141
|
-
|
|
208
|
+
@click.option(
|
|
209
|
+
"--asset_filter",
|
|
210
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
211
|
+
required=False,
|
|
212
|
+
type=str,
|
|
213
|
+
default=None,
|
|
214
|
+
)
|
|
215
|
+
def sync_rapid7_insight_cloud(
|
|
216
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str
|
|
217
|
+
) -> None:
|
|
142
218
|
"""Sync Vulnerabilities from Rapid7 Insight Cloud to RegScale."""
|
|
143
219
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
144
220
|
|
|
145
221
|
vulnerabilities_rapid7_insight_cloud = Vulnerabilities("rapid7_insight_cloud")
|
|
146
222
|
vulnerabilities_rapid7_insight_cloud.run_sync(
|
|
147
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
223
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
224
|
+
vuln_filter=vuln_filter,
|
|
225
|
+
scan_date=scan_date,
|
|
226
|
+
all_scans=all_scans,
|
|
227
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
148
228
|
)
|
|
149
229
|
|
|
150
230
|
|
|
@@ -171,13 +251,26 @@ def sync_rapid7_insight_cloud(regscale_ssp_id: int, vuln_filter: str, scan_date:
|
|
|
171
251
|
is_flag=True,
|
|
172
252
|
default=False,
|
|
173
253
|
)
|
|
174
|
-
|
|
254
|
+
@click.option(
|
|
255
|
+
"--asset_filter",
|
|
256
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
257
|
+
required=False,
|
|
258
|
+
type=str,
|
|
259
|
+
default=None,
|
|
260
|
+
)
|
|
261
|
+
def sync_servicenow_vr(
|
|
262
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str
|
|
263
|
+
) -> None:
|
|
175
264
|
"""Sync Vulnerabilities from Servicenow Vr to RegScale."""
|
|
176
265
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
177
266
|
|
|
178
267
|
vulnerabilities_servicenow_vr = Vulnerabilities("servicenow_vr")
|
|
179
268
|
vulnerabilities_servicenow_vr.run_sync(
|
|
180
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
269
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
270
|
+
vuln_filter=vuln_filter,
|
|
271
|
+
scan_date=scan_date,
|
|
272
|
+
all_scans=all_scans,
|
|
273
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
181
274
|
)
|
|
182
275
|
|
|
183
276
|
|
|
@@ -204,13 +297,26 @@ def sync_servicenow_vr(regscale_ssp_id: int, vuln_filter: str, scan_date: dateti
|
|
|
204
297
|
is_flag=True,
|
|
205
298
|
default=False,
|
|
206
299
|
)
|
|
207
|
-
|
|
300
|
+
@click.option(
|
|
301
|
+
"--asset_filter",
|
|
302
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
303
|
+
required=False,
|
|
304
|
+
type=str,
|
|
305
|
+
default=None,
|
|
306
|
+
)
|
|
307
|
+
def sync_tanium_cloud(
|
|
308
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str
|
|
309
|
+
) -> None:
|
|
208
310
|
"""Sync Vulnerabilities from Tanium Cloud to RegScale."""
|
|
209
311
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
210
312
|
|
|
211
313
|
vulnerabilities_tanium_cloud = Vulnerabilities("tanium_cloud")
|
|
212
314
|
vulnerabilities_tanium_cloud.run_sync(
|
|
213
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
315
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
316
|
+
vuln_filter=vuln_filter,
|
|
317
|
+
scan_date=scan_date,
|
|
318
|
+
all_scans=all_scans,
|
|
319
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
214
320
|
)
|
|
215
321
|
|
|
216
322
|
|
|
@@ -237,19 +343,33 @@ def sync_tanium_cloud(regscale_ssp_id: int, vuln_filter: str, scan_date: datetim
|
|
|
237
343
|
is_flag=True,
|
|
238
344
|
default=False,
|
|
239
345
|
)
|
|
346
|
+
@click.option(
|
|
347
|
+
"--asset_filter",
|
|
348
|
+
help='STRING: Apply filters to asset queries. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
349
|
+
required=False,
|
|
350
|
+
type=str,
|
|
351
|
+
default=None,
|
|
352
|
+
)
|
|
240
353
|
@click.option(
|
|
241
354
|
"--url",
|
|
242
355
|
type=click.STRING,
|
|
243
356
|
help="Base URL for the Tenable Cloud API.",
|
|
244
357
|
required=False,
|
|
245
358
|
)
|
|
246
|
-
def sync_tenable_cloud(
|
|
359
|
+
def sync_tenable_cloud(
|
|
360
|
+
regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, asset_filter: str, url: str
|
|
361
|
+
) -> None:
|
|
247
362
|
"""Sync Vulnerabilities from Tenable Cloud to RegScale."""
|
|
248
363
|
from regscale.models.integration_models.synqly_models.connectors import Vulnerabilities
|
|
249
364
|
|
|
250
365
|
vulnerabilities_tenable_cloud = Vulnerabilities("tenable_cloud")
|
|
251
366
|
vulnerabilities_tenable_cloud.run_sync(
|
|
252
|
-
regscale_ssp_id=regscale_ssp_id,
|
|
367
|
+
regscale_ssp_id=regscale_ssp_id,
|
|
368
|
+
vuln_filter=vuln_filter,
|
|
369
|
+
scan_date=scan_date,
|
|
370
|
+
all_scans=all_scans,
|
|
371
|
+
filter=asset_filter.split(";") if asset_filter else [],
|
|
372
|
+
url=url,
|
|
253
373
|
)
|
|
254
374
|
|
|
255
375
|
|
|
@@ -925,6 +925,19 @@ class WizComplianceReportProcessor(ComplianceIntegration):
|
|
|
925
925
|
impl.lastAssessmentResult = "Pass"
|
|
926
926
|
impl.bStatusImplemented = True
|
|
927
927
|
|
|
928
|
+
# Ensure required fields are set if empty
|
|
929
|
+
if not impl.responsibility:
|
|
930
|
+
impl.responsibility = ControlImplementation.get_default_responsibility(
|
|
931
|
+
parent_id=impl.parentId
|
|
932
|
+
)
|
|
933
|
+
logger.debug(
|
|
934
|
+
f"Setting default responsibility for control {control_id}: {impl.responsibility}"
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
if not impl.implementation:
|
|
938
|
+
impl.implementation = f"Implementation details for {control_id} will be documented."
|
|
939
|
+
logger.debug(f"Setting default implementation statement for control {control_id}")
|
|
940
|
+
|
|
928
941
|
# Set audit fields if available
|
|
929
942
|
user_id = self.app.config.get("userId")
|
|
930
943
|
if user_id:
|
|
@@ -1041,6 +1054,15 @@ class WizComplianceReportProcessor(ComplianceIntegration):
|
|
|
1041
1054
|
impl.lastAssessmentResult = "Fail"
|
|
1042
1055
|
impl.bStatusImplemented = False
|
|
1043
1056
|
|
|
1057
|
+
# Ensure required fields are set if empty
|
|
1058
|
+
if not impl.responsibility:
|
|
1059
|
+
impl.responsibility = ControlImplementation.get_default_responsibility(parent_id=impl.parentId)
|
|
1060
|
+
logger.debug(f"Setting default responsibility for control {control_id}: {impl.responsibility}")
|
|
1061
|
+
|
|
1062
|
+
if not impl.implementation:
|
|
1063
|
+
impl.implementation = f"Implementation details for {control_id} will be documented."
|
|
1064
|
+
logger.debug(f"Setting default implementation statement for control {control_id}")
|
|
1065
|
+
|
|
1044
1066
|
# Set audit fields if available
|
|
1045
1067
|
user_id = self.app.config.get("userId")
|
|
1046
1068
|
if user_id:
|
|
@@ -1864,6 +1864,23 @@ class ComplianceIntegration(ScannerIntegration, ABC):
|
|
|
1864
1864
|
implementation.status = new_status
|
|
1865
1865
|
implementation.dateLastAssessed = get_current_datetime()
|
|
1866
1866
|
implementation.lastAssessmentResult = result
|
|
1867
|
+
|
|
1868
|
+
# Ensure required fields are set if empty
|
|
1869
|
+
if not implementation.responsibility:
|
|
1870
|
+
implementation.responsibility = ControlImplementation.get_default_responsibility(
|
|
1871
|
+
parent_id=implementation.parentId
|
|
1872
|
+
)
|
|
1873
|
+
logger.debug(
|
|
1874
|
+
f"Setting default responsibility for implementation {implementation.id}: {implementation.responsibility}"
|
|
1875
|
+
)
|
|
1876
|
+
|
|
1877
|
+
if not implementation.implementation:
|
|
1878
|
+
control_id = (
|
|
1879
|
+
getattr(implementation.control, "controlId", "control") if implementation.control else "control"
|
|
1880
|
+
)
|
|
1881
|
+
implementation.implementation = f"Implementation details for {control_id} will be documented."
|
|
1882
|
+
logger.debug(f"Setting default implementation statement for implementation {implementation.id}")
|
|
1883
|
+
|
|
1867
1884
|
implementation.save()
|
|
1868
1885
|
|
|
1869
1886
|
# Update objectives if they exist
|
|
@@ -1162,6 +1162,21 @@ class ScannerIntegration(ABC):
|
|
|
1162
1162
|
self.components_by_title[component_name] = component
|
|
1163
1163
|
return component
|
|
1164
1164
|
|
|
1165
|
+
def _get_compliance_settings_id(self) -> Optional[int]:
|
|
1166
|
+
"""
|
|
1167
|
+
Get the compliance settings ID from the security plan.
|
|
1168
|
+
|
|
1169
|
+
:return: The compliance settings ID if available
|
|
1170
|
+
:rtype: Optional[int]
|
|
1171
|
+
"""
|
|
1172
|
+
try:
|
|
1173
|
+
security_plan = regscale_models.SecurityPlan.get_object(object_id=self.plan_id)
|
|
1174
|
+
if security_plan and hasattr(security_plan, "complianceSettingsId"):
|
|
1175
|
+
return security_plan.complianceSettingsId
|
|
1176
|
+
except Exception as e:
|
|
1177
|
+
logger.debug(f"Failed to get compliance settings ID from security plan {self.plan_id}: {e}")
|
|
1178
|
+
return None
|
|
1179
|
+
|
|
1165
1180
|
def _create_new_component(self, asset: IntegrationAsset, component_name: str) -> regscale_models.Component:
|
|
1166
1181
|
"""
|
|
1167
1182
|
Create a new component for the asset.
|
|
@@ -1178,6 +1193,7 @@ class ScannerIntegration(ABC):
|
|
|
1178
1193
|
securityPlansId=self.plan_id,
|
|
1179
1194
|
description=component_name,
|
|
1180
1195
|
componentOwnerId=self.get_assessor_id(),
|
|
1196
|
+
complianceSettingsId=self._get_compliance_settings_id(),
|
|
1181
1197
|
).get_or_create()
|
|
1182
1198
|
self.components.append(component)
|
|
1183
1199
|
return component
|