regscale-cli 6.20.3.1__py3-none-any.whl → 6.20.4.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/__init__.py +1 -1
- regscale/integrations/commercial/__init__.py +1 -0
- regscale/integrations/commercial/jira.py +35 -16
- regscale/integrations/commercial/qualys/__init__.py +298 -28
- regscale/integrations/commercial/qualys/qualys_error_handler.py +519 -0
- regscale/integrations/commercial/qualys/scanner.py +222 -97
- regscale/integrations/commercial/synqly/assets.py +11 -1
- regscale/integrations/commercial/synqly/edr.py +4 -4
- regscale/integrations/commercial/synqly/ticketing.py +1 -1
- regscale/integrations/commercial/synqly/vulnerabilities.py +2 -2
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +81 -44
- regscale/models/app_models/import_validater.py +20 -2
- regscale/models/integration_models/cisa_kev_data.json +53 -8
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/task.py +0 -1
- {regscale_cli-6.20.3.1.dist-info → regscale_cli-6.20.4.1.dist-info}/METADATA +1 -1
- {regscale_cli-6.20.3.1.dist-info → regscale_cli-6.20.4.1.dist-info}/RECORD +21 -20
- {regscale_cli-6.20.3.1.dist-info → regscale_cli-6.20.4.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.3.1.dist-info → regscale_cli-6.20.4.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.3.1.dist-info → regscale_cli-6.20.4.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.3.1.dist-info → regscale_cli-6.20.4.1.dist-info}/top_level.txt +0 -0
|
@@ -23,6 +23,7 @@ from regscale.integrations.scanner_integration import (
|
|
|
23
23
|
)
|
|
24
24
|
from regscale.integrations.variables import ScannerVariables
|
|
25
25
|
from regscale.models import AssetStatus, IssueSeverity, IssueStatus
|
|
26
|
+
from regscale.integrations.commercial.qualys.qualys_error_handler import QualysErrorHandler
|
|
26
27
|
|
|
27
28
|
logger = logging.getLogger("regscale")
|
|
28
29
|
|
|
@@ -107,6 +108,13 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
107
108
|
logger.warning("Data is not a dictionary")
|
|
108
109
|
return False, None
|
|
109
110
|
|
|
111
|
+
# Check for Qualys errors in the data
|
|
112
|
+
error_details = QualysErrorHandler.extract_error_details(data)
|
|
113
|
+
if error_details.get("has_error"):
|
|
114
|
+
logger.error("Data contains Qualys error response")
|
|
115
|
+
QualysErrorHandler.log_error_details(error_details)
|
|
116
|
+
return False, None
|
|
117
|
+
|
|
110
118
|
if "HOST_LIST_VM_DETECTION_OUTPUT" not in data:
|
|
111
119
|
logger.warning("Data does not contain HOST_LIST_VM_DETECTION_OUTPUT")
|
|
112
120
|
return False, None
|
|
@@ -142,85 +150,164 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
142
150
|
:return: IntegrationAsset object
|
|
143
151
|
:rtype: IntegrationAsset
|
|
144
152
|
"""
|
|
153
|
+
# Determine which host data to use
|
|
154
|
+
host_data = self._determine_host_data(file_path, data, host)
|
|
155
|
+
|
|
156
|
+
# Handle None input gracefully
|
|
157
|
+
if host_data is None:
|
|
158
|
+
return self._create_placeholder_asset()
|
|
159
|
+
|
|
160
|
+
# Convert XML Element to dict if necessary
|
|
161
|
+
if not isinstance(host_data, dict) and hasattr(host_data, "tag"):
|
|
162
|
+
host_data = self._xml_element_to_dict(host_data)
|
|
163
|
+
|
|
164
|
+
# Process dictionary data
|
|
165
|
+
if isinstance(host_data, dict):
|
|
166
|
+
return self._create_asset_from_dict(host_data)
|
|
167
|
+
|
|
168
|
+
# If we got here, we don't know how to handle the data
|
|
169
|
+
logger.error(f"Unexpected host data type: {type(host_data)}")
|
|
170
|
+
raise ValueError(f"Cannot parse asset from data type: {type(host_data)}")
|
|
171
|
+
|
|
172
|
+
def _determine_host_data(self, file_path, data, host):
|
|
173
|
+
"""
|
|
174
|
+
Determine which host data to use based on provided parameters.
|
|
175
|
+
|
|
176
|
+
:param file_path: File path parameter (may contain host data)
|
|
177
|
+
:param data: Data parameter
|
|
178
|
+
:param host: Host parameter
|
|
179
|
+
:return: Host data to use
|
|
180
|
+
"""
|
|
145
181
|
# Handle the case when the file_path contains the host data (common in tests)
|
|
146
182
|
if isinstance(file_path, dict) and not host and not data:
|
|
147
|
-
|
|
183
|
+
return file_path
|
|
148
184
|
|
|
149
185
|
# Use host parameter if provided (for backward compatibility)
|
|
150
|
-
if host is None:
|
|
151
|
-
|
|
186
|
+
if host is not None:
|
|
187
|
+
return host
|
|
152
188
|
|
|
153
|
-
#
|
|
154
|
-
|
|
155
|
-
logger.warning("No host data provided to parse_asset")
|
|
156
|
-
# Generate a placeholder asset with minimal information
|
|
157
|
-
return IntegrationAsset(
|
|
158
|
-
name="Unknown-Qualys-Asset",
|
|
159
|
-
identifier=str(int(time.time())), # Use timestamp as fallback ID
|
|
160
|
-
asset_type="Server",
|
|
161
|
-
asset_category="IT",
|
|
162
|
-
status=AssetStatus.Active,
|
|
163
|
-
parent_id=self.plan_id,
|
|
164
|
-
parent_module="securityplans",
|
|
165
|
-
notes="Generated for missing Qualys data",
|
|
166
|
-
)
|
|
189
|
+
# Fall back to data parameter
|
|
190
|
+
return data
|
|
167
191
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
192
|
+
def _create_placeholder_asset(self):
|
|
193
|
+
"""
|
|
194
|
+
Create a placeholder asset when no valid host data is provided.
|
|
171
195
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
196
|
+
:return: IntegrationAsset with placeholder data
|
|
197
|
+
:rtype: IntegrationAsset
|
|
198
|
+
"""
|
|
199
|
+
logger.warning("No host data provided to parse_asset")
|
|
200
|
+
return IntegrationAsset(
|
|
201
|
+
name="Unknown-Qualys-Asset",
|
|
202
|
+
identifier=str(int(time.time())), # Use timestamp as fallback ID
|
|
203
|
+
asset_type="Server",
|
|
204
|
+
asset_category="IT",
|
|
205
|
+
status=AssetStatus.Active,
|
|
206
|
+
parent_id=self.plan_id,
|
|
207
|
+
parent_module="securityplans",
|
|
208
|
+
notes="Generated for missing Qualys data",
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
def _create_asset_from_dict(self, host_data):
|
|
212
|
+
"""
|
|
213
|
+
Create an IntegrationAsset from dictionary host data.
|
|
214
|
+
|
|
215
|
+
:param host_data: Dictionary containing host information
|
|
216
|
+
:return: IntegrationAsset object
|
|
217
|
+
:rtype: IntegrationAsset
|
|
218
|
+
"""
|
|
219
|
+
# Navigate to host data if we have the full structure
|
|
220
|
+
processed_host = self._extract_host_from_structure(host_data)
|
|
221
|
+
|
|
222
|
+
# Extract host information
|
|
223
|
+
host_info = self._extract_host_information(processed_host)
|
|
224
|
+
|
|
225
|
+
# Create and return the asset
|
|
226
|
+
return IntegrationAsset(
|
|
227
|
+
name=host_info["name"],
|
|
228
|
+
identifier=host_info["host_id"],
|
|
229
|
+
asset_type="Server",
|
|
230
|
+
asset_category="IT",
|
|
231
|
+
ip_address=host_info["ip"],
|
|
232
|
+
fqdn=host_info["fqdn"],
|
|
233
|
+
operating_system=host_info["os"],
|
|
234
|
+
status=AssetStatus.Active,
|
|
235
|
+
external_id=host_info["host_id"],
|
|
236
|
+
date_last_updated=host_info["last_scan"],
|
|
237
|
+
mac_address=None,
|
|
238
|
+
vlan_id=host_info["network_id"],
|
|
239
|
+
notes=f"Qualys Asset ID: {host_info['host_id']}",
|
|
240
|
+
parent_id=self.plan_id,
|
|
241
|
+
parent_module="securityplans",
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
def _extract_host_from_structure(self, host_data):
|
|
245
|
+
"""
|
|
246
|
+
Extract host data from nested structure if needed.
|
|
247
|
+
|
|
248
|
+
:param host_data: Host data dictionary
|
|
249
|
+
:return: Processed host data
|
|
250
|
+
"""
|
|
251
|
+
# Check if we got the full data structure or just a host dictionary
|
|
252
|
+
if "HOST_LIST_VM_DETECTION_OUTPUT" not in host_data:
|
|
253
|
+
return host_data
|
|
254
|
+
|
|
255
|
+
# Navigate to the HOST data within the nested structure
|
|
256
|
+
try:
|
|
257
|
+
return (
|
|
258
|
+
host_data.get("HOST_LIST_VM_DETECTION_OUTPUT", {})
|
|
259
|
+
.get("RESPONSE", {})
|
|
260
|
+
.get("HOST_LIST", {})
|
|
261
|
+
.get("HOST", {})
|
|
218
262
|
)
|
|
219
|
-
|
|
263
|
+
except (AttributeError, KeyError):
|
|
264
|
+
logger.error("Could not navigate to HOST data in dictionary")
|
|
265
|
+
raise ValueError("Invalid host data structure")
|
|
220
266
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
267
|
+
def _extract_host_information(self, host):
|
|
268
|
+
"""
|
|
269
|
+
Extract host information from host dictionary.
|
|
270
|
+
|
|
271
|
+
:param host: Host dictionary
|
|
272
|
+
:return: Dictionary with extracted host information
|
|
273
|
+
:rtype: dict
|
|
274
|
+
"""
|
|
275
|
+
host_id = host.get("ID", "")
|
|
276
|
+
ip = host.get("IP", "")
|
|
277
|
+
dns = host.get("DNS", "")
|
|
278
|
+
os = host.get("OS", "")
|
|
279
|
+
last_scan = host.get("LAST_SCAN_DATETIME", "")
|
|
280
|
+
network_id = host.get("NETWORK_ID", "")
|
|
281
|
+
|
|
282
|
+
# Try to get FQDN from DNS_DATA if available
|
|
283
|
+
fqdn = self._extract_fqdn(host)
|
|
284
|
+
|
|
285
|
+
# Determine asset name
|
|
286
|
+
name = dns or ip or f"QualysAsset-{host_id}"
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
"host_id": host_id,
|
|
290
|
+
"ip": ip,
|
|
291
|
+
"dns": dns,
|
|
292
|
+
"os": os,
|
|
293
|
+
"last_scan": last_scan,
|
|
294
|
+
"network_id": network_id,
|
|
295
|
+
"fqdn": fqdn,
|
|
296
|
+
"name": name,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
def _extract_fqdn(self, host):
|
|
300
|
+
"""
|
|
301
|
+
Extract FQDN from host DNS_DATA if available.
|
|
302
|
+
|
|
303
|
+
:param host: Host dictionary
|
|
304
|
+
:return: FQDN string or None
|
|
305
|
+
:rtype: Optional[str]
|
|
306
|
+
"""
|
|
307
|
+
dns_data = host.get("DNS_DATA", {})
|
|
308
|
+
if dns_data:
|
|
309
|
+
return dns_data.get("FQDN", "")
|
|
310
|
+
return None
|
|
224
311
|
|
|
225
312
|
def parse_finding(
|
|
226
313
|
self,
|
|
@@ -241,37 +328,66 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
241
328
|
:return: IntegrationFinding object
|
|
242
329
|
:rtype: IntegrationFinding
|
|
243
330
|
"""
|
|
244
|
-
#
|
|
245
|
-
detection_to_use =
|
|
246
|
-
|
|
331
|
+
# Determine which detection and host_id to use
|
|
332
|
+
detection_to_use, host_id_to_use = self._determine_finding_parameters(
|
|
333
|
+
detection, item, host_id, asset_identifier
|
|
334
|
+
)
|
|
247
335
|
|
|
248
336
|
# Handle None input gracefully
|
|
249
337
|
if detection_to_use is None:
|
|
250
|
-
|
|
251
|
-
# Use host_id or generate a placeholder if none provided
|
|
252
|
-
if not host_id_to_use:
|
|
253
|
-
host_id_to_use = f"unknown-host-{int(time.time())}"
|
|
254
|
-
|
|
255
|
-
# Generate a placeholder finding with minimal information
|
|
256
|
-
return IntegrationFinding(
|
|
257
|
-
title="Unknown Qualys Finding",
|
|
258
|
-
description="No detection data was provided",
|
|
259
|
-
severity=IssueSeverity.Low.value,
|
|
260
|
-
status=IssueStatus.Open,
|
|
261
|
-
plugin_name="QID-unknown",
|
|
262
|
-
plugin_id=self.title,
|
|
263
|
-
asset_identifier=host_id_to_use,
|
|
264
|
-
category="Vulnerability",
|
|
265
|
-
scan_date=self.scan_date or get_current_datetime(),
|
|
266
|
-
external_id=f"unknown-finding-{int(time.time())}",
|
|
267
|
-
)
|
|
338
|
+
return self._create_placeholder_finding(host_id_to_use)
|
|
268
339
|
|
|
269
340
|
# Convert XML Element to dict if necessary
|
|
270
|
-
if not isinstance(detection_to_use, dict) and hasattr(detection_to_use, "tag"):
|
|
341
|
+
if not isinstance(detection_to_use, dict) and hasattr(detection_to_use, "tag"):
|
|
271
342
|
detection_to_use = self._xml_element_to_dict(detection_to_use)
|
|
272
343
|
|
|
273
344
|
return self._parse_finding_from_dict(detection_to_use, host_id_to_use)
|
|
274
345
|
|
|
346
|
+
def _determine_finding_parameters(self, detection, item, host_id, asset_identifier):
|
|
347
|
+
"""
|
|
348
|
+
Determine which detection and host_id to use based on provided parameters.
|
|
349
|
+
|
|
350
|
+
:param detection: Detection parameter
|
|
351
|
+
:param item: Item parameter (for compatibility)
|
|
352
|
+
:param host_id: Host ID parameter
|
|
353
|
+
:param asset_identifier: Asset identifier parameter (for compatibility)
|
|
354
|
+
:return: Tuple of (detection_to_use, host_id_to_use)
|
|
355
|
+
:rtype: tuple
|
|
356
|
+
"""
|
|
357
|
+
# For backward compatibility
|
|
358
|
+
detection_to_use = detection if detection is not None else item
|
|
359
|
+
host_id_to_use = host_id if host_id is not None else asset_identifier
|
|
360
|
+
|
|
361
|
+
return detection_to_use, host_id_to_use
|
|
362
|
+
|
|
363
|
+
def _create_placeholder_finding(self, host_id_to_use):
|
|
364
|
+
"""
|
|
365
|
+
Create a placeholder finding when no valid detection data is provided.
|
|
366
|
+
|
|
367
|
+
:param host_id_to_use: Host ID to use for the finding
|
|
368
|
+
:return: IntegrationFinding with placeholder data
|
|
369
|
+
:rtype: IntegrationFinding
|
|
370
|
+
"""
|
|
371
|
+
logger.warning("No detection data provided to parse_finding")
|
|
372
|
+
|
|
373
|
+
# Use host_id or generate a placeholder if none provided
|
|
374
|
+
if not host_id_to_use:
|
|
375
|
+
host_id_to_use = f"unknown-host-{int(time.time())}"
|
|
376
|
+
|
|
377
|
+
# Generate a placeholder finding with minimal information
|
|
378
|
+
return IntegrationFinding(
|
|
379
|
+
title="Unknown Qualys Finding",
|
|
380
|
+
description="No detection data was provided",
|
|
381
|
+
severity=IssueSeverity.Low.value,
|
|
382
|
+
status=IssueStatus.Open,
|
|
383
|
+
plugin_name="QID-unknown",
|
|
384
|
+
plugin_id=self.title,
|
|
385
|
+
asset_identifier=host_id_to_use,
|
|
386
|
+
category="Vulnerability",
|
|
387
|
+
scan_date=self.scan_date or get_current_datetime(),
|
|
388
|
+
external_id=f"unknown-finding-{int(time.time())}",
|
|
389
|
+
)
|
|
390
|
+
|
|
275
391
|
def _parse_finding_from_dict(self, detection, host_id):
|
|
276
392
|
"""
|
|
277
393
|
Parse a finding from a dictionary representation.
|
|
@@ -725,13 +841,22 @@ class QualysTotalCloudJSONLIntegration(JSONLScannerIntegration):
|
|
|
725
841
|
:return: Dictionary representation of the XML
|
|
726
842
|
:rtype: Dict[str, Any]
|
|
727
843
|
"""
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
logger.error(f"Error parsing XML: {str(e)}")
|
|
844
|
+
success, parsed_data, error_message = QualysErrorHandler.parse_xml_safely(xml_string)
|
|
845
|
+
|
|
846
|
+
if not success:
|
|
847
|
+
logger.error(f"Failed to parse XML string: {error_message}")
|
|
733
848
|
return {}
|
|
734
849
|
|
|
850
|
+
# Check for Qualys-specific errors
|
|
851
|
+
if parsed_data:
|
|
852
|
+
error_details = QualysErrorHandler.extract_error_details(parsed_data)
|
|
853
|
+
if error_details.get("has_error"):
|
|
854
|
+
logger.error("XML contains Qualys error response")
|
|
855
|
+
QualysErrorHandler.log_error_details(error_details)
|
|
856
|
+
return {}
|
|
857
|
+
|
|
858
|
+
return parsed_data or {}
|
|
859
|
+
|
|
735
860
|
def _process_xml_dict(self):
|
|
736
861
|
"""Process XML data provided as a dictionary."""
|
|
737
862
|
logger.debug("Using already parsed XML data (dict)")
|
|
@@ -38,7 +38,7 @@ def sync_axonius(regscale_ssp_id: int) -> None:
|
|
|
38
38
|
@click.option(
|
|
39
39
|
"--url",
|
|
40
40
|
type=click.STRING,
|
|
41
|
-
help="
|
|
41
|
+
help="Base URL for the CrowdStrike Falcon Spotlight API.",
|
|
42
42
|
required=False,
|
|
43
43
|
)
|
|
44
44
|
def sync_crowdstrike(regscale_ssp_id: int, url: str) -> None:
|
|
@@ -79,6 +79,16 @@ def sync_servicenow(regscale_ssp_id: int) -> None:
|
|
|
79
79
|
assets_servicenow.run_sync(regscale_ssp_id=regscale_ssp_id)
|
|
80
80
|
|
|
81
81
|
|
|
82
|
+
@assets.command(name="sync_sevco")
|
|
83
|
+
@regscale_ssp_id()
|
|
84
|
+
def sync_sevco(regscale_ssp_id: int) -> None:
|
|
85
|
+
"""Sync Assets from Sevco to RegScale."""
|
|
86
|
+
from regscale.models.integration_models.synqly_models.connectors import Assets
|
|
87
|
+
|
|
88
|
+
assets_sevco = Assets("sevco")
|
|
89
|
+
assets_sevco.run_sync(regscale_ssp_id=regscale_ssp_id)
|
|
90
|
+
|
|
91
|
+
|
|
82
92
|
@assets.command(name="sync_tanium_cloud")
|
|
83
93
|
@regscale_ssp_id()
|
|
84
94
|
def sync_tanium_cloud(regscale_ssp_id: int) -> None:
|
|
@@ -18,7 +18,7 @@ def edr() -> None:
|
|
|
18
18
|
@click.option(
|
|
19
19
|
"--url",
|
|
20
20
|
type=click.STRING,
|
|
21
|
-
help="
|
|
21
|
+
help="Base URL for the CrowdStrike Falcon® API.",
|
|
22
22
|
required=False,
|
|
23
23
|
)
|
|
24
24
|
def sync_crowdstrike(regscale_ssp_id: int, url: str) -> None:
|
|
@@ -44,7 +44,7 @@ def sync_defender(regscale_ssp_id: int) -> None:
|
|
|
44
44
|
@click.option(
|
|
45
45
|
"--url",
|
|
46
46
|
type=click.STRING,
|
|
47
|
-
help="URL for the
|
|
47
|
+
help="Base URL for the ThreatDown EDR API.",
|
|
48
48
|
required=False,
|
|
49
49
|
)
|
|
50
50
|
def sync_malwarebytes(regscale_ssp_id: int, url: str) -> None:
|
|
@@ -60,7 +60,7 @@ def sync_malwarebytes(regscale_ssp_id: int, url: str) -> None:
|
|
|
60
60
|
@click.option(
|
|
61
61
|
"--edr_events_url",
|
|
62
62
|
type=click.STRING,
|
|
63
|
-
help="Base URL for the SentinelOne Singularity Data Lake API. This URL is required
|
|
63
|
+
help="Base URL for the SentinelOne Singularity Data Lake API. This URL is required is required when querying EDR events.",
|
|
64
64
|
required=False,
|
|
65
65
|
)
|
|
66
66
|
def sync_sentinelone(regscale_ssp_id: int, edr_events_url: str) -> None:
|
|
@@ -76,7 +76,7 @@ def sync_sentinelone(regscale_ssp_id: int, edr_events_url: str) -> None:
|
|
|
76
76
|
@click.option(
|
|
77
77
|
"--url",
|
|
78
78
|
type=click.STRING,
|
|
79
|
-
help="
|
|
79
|
+
help="Base URL for the Sophos Endpoint API.",
|
|
80
80
|
required=False,
|
|
81
81
|
)
|
|
82
82
|
def sync_sophos(regscale_ssp_id: int, url: str) -> None:
|
|
@@ -134,7 +134,7 @@ def sync_pagerduty(regscale_id: int, regscale_module: str, project: str, name: s
|
|
|
134
134
|
@click.option(
|
|
135
135
|
"--default_project",
|
|
136
136
|
type=click.STRING,
|
|
137
|
-
help="Default Project for the integration. This maps to the custom table for tickets. This table should be derived from Incident table.
|
|
137
|
+
help="Default Project for the integration. This maps to the custom table for tickets. This table should be derived from Incident table. Defaults to the incident table if not specified.",
|
|
138
138
|
required=False,
|
|
139
139
|
)
|
|
140
140
|
def sync_servicenow(regscale_id: int, regscale_module: str, issue_type: str, default_project: str) -> None:
|
|
@@ -40,7 +40,7 @@ def vulnerabilities() -> None:
|
|
|
40
40
|
@click.option(
|
|
41
41
|
"--url",
|
|
42
42
|
type=click.STRING,
|
|
43
|
-
help="
|
|
43
|
+
help="Base URL for the CrowdStrike Falcon® Spotlight API.",
|
|
44
44
|
required=False,
|
|
45
45
|
)
|
|
46
46
|
def sync_crowdstrike(regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, url: str) -> None:
|
|
@@ -207,7 +207,7 @@ def sync_tanium_cloud(regscale_ssp_id: int, vuln_filter: str, scan_date: datetim
|
|
|
207
207
|
@click.option(
|
|
208
208
|
"--url",
|
|
209
209
|
type=click.STRING,
|
|
210
|
-
help=
|
|
210
|
+
help="Base URL for the Tenable Cloud API.",
|
|
211
211
|
required=False,
|
|
212
212
|
)
|
|
213
213
|
def sync_tenable_cloud(regscale_ssp_id: int, vuln_filter: str, scan_date: datetime, all_scans: bool, url: str) -> None:
|