regscale-cli 6.25.0.1__py3-none-any.whl → 6.26.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 +18 -3
- regscale/core/app/internal/login.py +0 -1
- regscale/core/app/utils/catalog_utils/common.py +1 -1
- 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/wizv2/click.py +26 -26
- regscale/integrations/commercial/wizv2/compliance_report.py +152 -157
- regscale/integrations/commercial/wizv2/constants.py +20 -71
- regscale/integrations/commercial/wizv2/scanner.py +3 -3
- regscale/integrations/compliance_integration.py +67 -2
- regscale/integrations/control_matcher.py +358 -0
- regscale/integrations/due_date_handler.py +118 -6
- 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/poam_export_v5.py +888 -0
- regscale/integrations/scanner_integration.py +199 -130
- regscale/models/integration_models/cisa_kev_data.json +199 -4
- regscale/models/integration_models/nexpose.py +36 -10
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/locking.py +12 -8
- regscale/models/platform.py +1 -2
- regscale/models/regscale_models/control_implementation.py +46 -21
- regscale/models/regscale_models/issue.py +256 -94
- regscale/models/regscale_models/milestone.py +1 -1
- regscale/models/regscale_models/regscale_model.py +6 -1
- regscale/templates/__init__.py +0 -0
- regscale/utils/threading/threadhandler.py +20 -15
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.26.0.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.26.0.0.dist-info}/RECORD +84 -37
- 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 +1814 -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 +1469 -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/test_compliance_report_normalization.py +138 -0
- tests/regscale/integrations/commercial/wizv2/test_issue.py +343 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +165 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +1351 -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_policy_compliance.py +750 -0
- tests/regscale/integrations/commercial/wizv2/test_wiz_status_mapping.py +149 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2.py +264 -0
- tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +624 -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/test_control_matcher.py +1314 -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_cli-6.25.0.1.dist-info → regscale_cli-6.26.0.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.26.0.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.26.0.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.25.0.1.dist-info → regscale_cli-6.26.0.0.dist-info}/top_level.txt +0 -0
regscale/_version.py
CHANGED
regscale/airflow/hierarchy.py
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from regscale.regscale import cli
|
|
6
|
-
from regscale.models.click_models import ClickCommand
|
|
7
5
|
from regscale.airflow.click_mixins import AirflowClickGroup
|
|
6
|
+
from regscale.models.click_models import ClickCommand
|
|
7
|
+
from regscale.regscale import cli
|
|
8
8
|
|
|
9
9
|
AIRFLOW_CLICK_GROUP = AirflowClickGroup.from_group(cli, prefix="regscale")
|
|
10
10
|
AIRFLOW_CLICK_OPERATORS = AIRFLOW_CLICK_GROUP.flatten_operator()
|
regscale/core/app/application.py
CHANGED
|
@@ -664,7 +664,8 @@ class Application(metaclass=Singleton):
|
|
|
664
664
|
|
|
665
665
|
def save_config(self, conf: dict) -> None:
|
|
666
666
|
"""
|
|
667
|
-
Save Configuration to init.yaml
|
|
667
|
+
Save Configuration to init.yaml using atomic file operations to prevent corruption
|
|
668
|
+
during parallel writes.
|
|
668
669
|
|
|
669
670
|
:param dict conf: Application configuration
|
|
670
671
|
:rtype: None
|
|
@@ -681,8 +682,22 @@ class Application(metaclass=Singleton):
|
|
|
681
682
|
try:
|
|
682
683
|
self.logger.debug(f"Saving config to {self.config_file}.")
|
|
683
684
|
with self._config_lock:
|
|
684
|
-
|
|
685
|
-
|
|
685
|
+
# Use atomic file operations: write to temp file, then rename
|
|
686
|
+
# This prevents corruption when multiple processes write simultaneously
|
|
687
|
+
import tempfile
|
|
688
|
+
|
|
689
|
+
config_dir = os.path.dirname(self.config_file) or "."
|
|
690
|
+
temp_fd, temp_path = tempfile.mkstemp(dir=config_dir, prefix=".tmp_", suffix=".yaml", text=True)
|
|
691
|
+
try:
|
|
692
|
+
with os.fdopen(temp_fd, "w", encoding="utf-8") as temp_file:
|
|
693
|
+
yaml.dump(conf, temp_file)
|
|
694
|
+
# Atomic rename - this is atomic on POSIX systems
|
|
695
|
+
os.replace(temp_path, self.config_file)
|
|
696
|
+
except Exception:
|
|
697
|
+
# Clean up temp file if something goes wrong
|
|
698
|
+
with contextlib.suppress(OSError):
|
|
699
|
+
os.unlink(temp_path)
|
|
700
|
+
raise
|
|
686
701
|
except OSError:
|
|
687
702
|
self.logger.error(f"Could not save config to {self.config_file}.")
|
|
688
703
|
|
|
@@ -136,7 +136,6 @@ def login(
|
|
|
136
136
|
logger.info("New RegScale Token has been updated and saved in init.yaml")
|
|
137
137
|
# Truncate token for logging purposes
|
|
138
138
|
logger.debug("Token: %s", regscale_auth.token[:20])
|
|
139
|
-
config["domain"] = host
|
|
140
139
|
app.save_config(config)
|
|
141
140
|
return regscale_auth.token
|
|
142
141
|
|
|
@@ -87,5 +87,5 @@ def objective_to_control_dot(input_string: str) -> str:
|
|
|
87
87
|
if match:
|
|
88
88
|
return match.group(1)
|
|
89
89
|
else:
|
|
90
|
-
logger.
|
|
90
|
+
logger.debug(f"Failed to convert objective to control: {input_string}")
|
|
91
91
|
return input_string
|
|
@@ -356,7 +356,7 @@ class SicuraAPI:
|
|
|
356
356
|
try:
|
|
357
357
|
response = self._make_request(
|
|
358
358
|
"GET",
|
|
359
|
-
"/
|
|
359
|
+
"/api/jaeger/v1/nodes",
|
|
360
360
|
params={
|
|
361
361
|
"verbose": "true",
|
|
362
362
|
"attributes": "platforms,scannable_profiles,most_recent_scan",
|
|
@@ -425,7 +425,7 @@ class SicuraAPI:
|
|
|
425
425
|
"scanAttributes": {"platform": platform, "profile": profile},
|
|
426
426
|
}
|
|
427
427
|
|
|
428
|
-
result = self._make_request("POST", "/
|
|
428
|
+
result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
|
|
429
429
|
|
|
430
430
|
if result:
|
|
431
431
|
logger.info(f"Successfully created scan task with ID: {result}")
|
|
@@ -448,7 +448,7 @@ class SicuraAPI:
|
|
|
448
448
|
"""
|
|
449
449
|
try:
|
|
450
450
|
response = self._make_request(
|
|
451
|
-
"GET", "/
|
|
451
|
+
"GET", "/api/jaeger/v1/jobs", params={"verbose": "true", self.FILTER_TASK_ID: task_id}
|
|
452
452
|
)
|
|
453
453
|
|
|
454
454
|
# Handle 404 or empty response
|
|
@@ -503,7 +503,7 @@ class SicuraAPI:
|
|
|
503
503
|
if profile:
|
|
504
504
|
params["profile"] = profile
|
|
505
505
|
|
|
506
|
-
response = self._make_request("GET", "/
|
|
506
|
+
response = self._make_request("GET", "/api/jaeger/v1/nodes", params=params)
|
|
507
507
|
|
|
508
508
|
# Handle 404 or empty response
|
|
509
509
|
if not response or (isinstance(response, list) and not response):
|
|
@@ -524,16 +524,17 @@ class SicuraAPI:
|
|
|
524
524
|
return None # Return None if no scans available
|
|
525
525
|
|
|
526
526
|
# Calculate summary stats
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
527
|
+
scan_results = device.get("scans", {}).get("results", [])
|
|
528
|
+
pass_count = sum(1 for scan in scan_results if scan.get("result") == "pass")
|
|
529
|
+
fail_count = sum(1 for scan in scan_results if scan.get("result") == "fail")
|
|
530
|
+
total_count = len(scan_results)
|
|
530
531
|
|
|
531
532
|
# Create the raw result data
|
|
532
533
|
result_data = {
|
|
533
534
|
"device_id": device.get("id"),
|
|
534
535
|
"fqdn": device.get("fqdn"),
|
|
535
536
|
"ip_address": device.get("ip_address"),
|
|
536
|
-
"scans":
|
|
537
|
+
"scans": scan_results,
|
|
537
538
|
"summary": {
|
|
538
539
|
"total": total_count,
|
|
539
540
|
"pass": pass_count,
|
|
@@ -638,7 +639,7 @@ class SicuraAPI:
|
|
|
638
639
|
if ip_address:
|
|
639
640
|
params[self.FILTER_IP_ADDRESS] = ip_address
|
|
640
641
|
|
|
641
|
-
response = self._make_request("GET", "/
|
|
642
|
+
response = self._make_request("GET", "/api/jaeger/v1/node_templates/", params=params)
|
|
642
643
|
|
|
643
644
|
# Handle 404 or empty response
|
|
644
645
|
if not response or (isinstance(response, dict) and "detail" in response):
|
|
@@ -670,7 +671,7 @@ class SicuraAPI:
|
|
|
670
671
|
# Use PUT method with the correct endpoint and payload
|
|
671
672
|
result = self._make_request(
|
|
672
673
|
"PUT",
|
|
673
|
-
f"/
|
|
674
|
+
f"/api/jaeger/v1/node_templates/{device_id}",
|
|
674
675
|
params={"verbose": "true", "include_controls": "true", "action": "promote"},
|
|
675
676
|
)
|
|
676
677
|
|
|
@@ -697,7 +698,7 @@ class SicuraAPI:
|
|
|
697
698
|
# Use PUT method with the correct endpoint and payload
|
|
698
699
|
result = self._make_request(
|
|
699
700
|
"PUT",
|
|
700
|
-
f"/
|
|
701
|
+
f"/api/jaeger/v1/node_templates/{device_id}",
|
|
701
702
|
params={"verbose": "true", "include_controls": "true", "action": "reject"},
|
|
702
703
|
)
|
|
703
704
|
|
|
@@ -721,7 +722,7 @@ class SicuraAPI:
|
|
|
721
722
|
:rtype: bool
|
|
722
723
|
"""
|
|
723
724
|
try:
|
|
724
|
-
result = self._make_request("DELETE", f"/
|
|
725
|
+
result = self._make_request("DELETE", f"/api/jaeger/v1/nodes/{device_id}")
|
|
725
726
|
|
|
726
727
|
# For DELETE operations, an empty result typically indicates success
|
|
727
728
|
if result is None or result == "" or (isinstance(result, dict) and not result):
|
|
@@ -854,7 +855,7 @@ class SicuraAPI:
|
|
|
854
855
|
}
|
|
855
856
|
|
|
856
857
|
logger.info(f"Creating enforcement task for device {device_id} with {len(ce_names)} CE names")
|
|
857
|
-
result = self._make_request("POST", "/
|
|
858
|
+
result = self._make_request("POST", "/api/jaeger/v1/tasks/", data=payload)
|
|
858
859
|
|
|
859
860
|
if result:
|
|
860
861
|
logger.info(f"Successfully created enforcement task with ID: {result}")
|
|
@@ -46,7 +46,13 @@ def sync_assets(regscale_id: int):
|
|
|
46
46
|
|
|
47
47
|
@sicura.command(name="sync_findings")
|
|
48
48
|
@regscale_id(help="RegScale will create and update findings as children of this record.")
|
|
49
|
-
|
|
49
|
+
@click.option(
|
|
50
|
+
"--trigger_scan",
|
|
51
|
+
"-s",
|
|
52
|
+
is_flag=True,
|
|
53
|
+
help="Trigger a new scan on Sicura assets before syncing.",
|
|
54
|
+
)
|
|
55
|
+
def sync_findings(regscale_id: int, trigger_scan: bool):
|
|
50
56
|
"""
|
|
51
57
|
Sync Sicura findings to RegScale.
|
|
52
58
|
|
|
@@ -60,7 +66,7 @@ def sync_findings(regscale_id: int):
|
|
|
60
66
|
)
|
|
61
67
|
|
|
62
68
|
# Using import_findings method which handles the synchronization
|
|
63
|
-
integration.sync_findings(plan_id=regscale_id)
|
|
69
|
+
integration.sync_findings(plan_id=regscale_id, trigger_scan=trigger_scan)
|
|
64
70
|
|
|
65
71
|
logger.info("[bold green]Finding synchronization complete.")
|
|
66
72
|
|
|
@@ -74,6 +74,10 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
74
74
|
logger.warning("No devices found in Sicura")
|
|
75
75
|
return
|
|
76
76
|
|
|
77
|
+
if kwargs.pop("trigger_scan", False):
|
|
78
|
+
logger.info(f"Triggering scans on Sicura {len(devices)} devices...")
|
|
79
|
+
self.trigger_scans(devices)
|
|
80
|
+
|
|
77
81
|
self.num_findings_to_process = 0
|
|
78
82
|
findings_count = 0
|
|
79
83
|
|
|
@@ -206,7 +210,8 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
206
210
|
mitigation=mitigation,
|
|
207
211
|
)
|
|
208
212
|
|
|
209
|
-
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _extract_scan_data(scan: Any) -> dict:
|
|
210
215
|
"""
|
|
211
216
|
Extract scan data from the scan object
|
|
212
217
|
|
|
@@ -235,7 +240,8 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
235
240
|
"controls": scan.controls if hasattr(scan, "controls") else {},
|
|
236
241
|
}
|
|
237
242
|
|
|
238
|
-
|
|
243
|
+
@staticmethod
|
|
244
|
+
def _extract_control_refs(controls: dict) -> tuple:
|
|
239
245
|
"""
|
|
240
246
|
Extract CCI and SRG references from controls
|
|
241
247
|
|
|
@@ -353,8 +359,9 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
353
359
|
baseline=platform,
|
|
354
360
|
)
|
|
355
361
|
|
|
362
|
+
@staticmethod
|
|
356
363
|
def _create_results_text(
|
|
357
|
-
|
|
364
|
+
title, ce_name, result, description, state, state_reason, cci_ref, srg_refs, is_cci_finding
|
|
358
365
|
) -> str:
|
|
359
366
|
"""
|
|
360
367
|
Create the results text for a finding
|
|
@@ -401,42 +408,6 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
401
408
|
:rtype: Iterator[IntegrationAsset]
|
|
402
409
|
"""
|
|
403
410
|
logger.info("Fetching assets from Sicura...")
|
|
404
|
-
|
|
405
|
-
# Get all devices
|
|
406
|
-
pending_devices = self.api.get_pending_devices()
|
|
407
|
-
for pending_device in pending_devices:
|
|
408
|
-
print(f"Pending device: {pending_device}")
|
|
409
|
-
self.api.accept_pending_device(pending_device.id)
|
|
410
|
-
task_id = self.api.create_scan_task(
|
|
411
|
-
device_id=pending_device.id,
|
|
412
|
-
platform=pending_device.platform,
|
|
413
|
-
profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
414
|
-
)
|
|
415
|
-
if task_id:
|
|
416
|
-
self.api.wait_for_scan_results(
|
|
417
|
-
task_id=task_id,
|
|
418
|
-
fqdn=pending_device.fqdn,
|
|
419
|
-
platform=pending_device.platform,
|
|
420
|
-
profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
421
|
-
)
|
|
422
|
-
else:
|
|
423
|
-
logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
|
|
424
|
-
continue # Continue to next device if creating scan task failed
|
|
425
|
-
|
|
426
|
-
task_id = self.api.create_scan_task(
|
|
427
|
-
device_id=pending_device.id, platform=pending_device.platform, profile=SicuraProfile.LEVEL_1_SERVER
|
|
428
|
-
)
|
|
429
|
-
if task_id:
|
|
430
|
-
self.api.wait_for_scan_results(
|
|
431
|
-
task_id=task_id,
|
|
432
|
-
fqdn=pending_device.fqdn,
|
|
433
|
-
platform=pending_device.platform,
|
|
434
|
-
profile=SicuraProfile.LEVEL_1_SERVER,
|
|
435
|
-
)
|
|
436
|
-
else:
|
|
437
|
-
logger.warning(f"Failed to create scan task for device {pending_device.fqdn}")
|
|
438
|
-
# No continue needed here as we're at the end of the loop iteration
|
|
439
|
-
|
|
440
411
|
devices = self.api.get_devices()
|
|
441
412
|
|
|
442
413
|
if not devices:
|
|
@@ -479,3 +450,42 @@ class SicuraIntegration(ScannerIntegration):
|
|
|
479
450
|
)
|
|
480
451
|
|
|
481
452
|
self.asset_progress.update(loading_devices, advance=1)
|
|
453
|
+
|
|
454
|
+
def trigger_and_wait_for_scan(self, device: Device) -> None:
|
|
455
|
+
"""
|
|
456
|
+
Trigger a scan and wait for the results
|
|
457
|
+
|
|
458
|
+
:param Device device: The device to trigger a scan for
|
|
459
|
+
:return: None
|
|
460
|
+
"""
|
|
461
|
+
task_id = self.api.create_scan_task(
|
|
462
|
+
device_id=device.id,
|
|
463
|
+
platform=device.platforms,
|
|
464
|
+
profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
465
|
+
)
|
|
466
|
+
if task_id:
|
|
467
|
+
self.api.wait_for_scan_results(
|
|
468
|
+
task_id=task_id,
|
|
469
|
+
fqdn=device.fqdn,
|
|
470
|
+
platform=device.platforms,
|
|
471
|
+
profile=SicuraProfile.I_MISSION_CRITICAL_CLASSIFIED,
|
|
472
|
+
)
|
|
473
|
+
else:
|
|
474
|
+
logger.warning(f"Failed to create scan task for device {device.fqdn}")
|
|
475
|
+
|
|
476
|
+
def trigger_scans(self, devices: list[Device]) -> None:
|
|
477
|
+
"""
|
|
478
|
+
Trigger scans for a list of devices
|
|
479
|
+
|
|
480
|
+
:param list[Device] devices: The devices to trigger scans for
|
|
481
|
+
:return: None
|
|
482
|
+
"""
|
|
483
|
+
if len(devices) > 1:
|
|
484
|
+
from regscale.utils.threading import ThreadManager
|
|
485
|
+
|
|
486
|
+
# use multithreading to trigger scans for multiple devices
|
|
487
|
+
thread_manager = ThreadManager(max_workers=10)
|
|
488
|
+
thread_manager.submit_tasks_from_list(self.trigger_and_wait_for_scan, devices)
|
|
489
|
+
thread_manager.execute_and_verify()
|
|
490
|
+
else:
|
|
491
|
+
self.trigger_and_wait_for_scan(devices[0])
|
|
@@ -82,15 +82,15 @@ class STIGInfo(BaseModel):
|
|
|
82
82
|
"""Data model for STIG Information with optional and required attributes."""
|
|
83
83
|
|
|
84
84
|
version: str
|
|
85
|
-
classification: str
|
|
85
|
+
classification: Optional[str] = None
|
|
86
86
|
customname: Optional[str] = None
|
|
87
87
|
stigid: str
|
|
88
88
|
description: Optional[str] = None
|
|
89
89
|
filename: Optional[str] = None
|
|
90
90
|
releaseinfo: str
|
|
91
91
|
title: str
|
|
92
|
-
uuid: str
|
|
93
|
-
notice: str
|
|
92
|
+
uuid: Optional[str] = None
|
|
93
|
+
notice: Optional[str] = None
|
|
94
94
|
source: Optional[str] = None
|
|
95
95
|
|
|
96
96
|
|
|
@@ -115,10 +115,10 @@ class Vuln(BaseModel):
|
|
|
115
115
|
check_content: Optional[str] = None
|
|
116
116
|
fix_text: str
|
|
117
117
|
check_content_ref: Optional[str] = None
|
|
118
|
-
weight: str
|
|
118
|
+
weight: Optional[str] = None
|
|
119
119
|
stigref: Optional[str] = None
|
|
120
120
|
targetkey: Optional[str] = None
|
|
121
|
-
stig_uuid: str
|
|
121
|
+
stig_uuid: Optional[str] = None
|
|
122
122
|
vuln_discuss: Optional[str] = None
|
|
123
123
|
ia_controls: Optional[str] = None
|
|
124
124
|
class_: Optional[str] = None
|
|
@@ -97,6 +97,23 @@ def sync_crowdstrike(regscale_ssp_id: int, filter: str, url: str) -> None:
|
|
|
97
97
|
assets_crowdstrike.run_sync(regscale_ssp_id=regscale_ssp_id, filter=filter.split(";") if filter else [], url=url)
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
@assets.command(name="sync_ivanti_neurons")
|
|
101
|
+
@regscale_ssp_id()
|
|
102
|
+
@click.option(
|
|
103
|
+
"--filter",
|
|
104
|
+
help='STRING: Apply filters to the query. Can be a single filter "field[operator]value" or semicolon-separated filters "field1[op]value1;field2[op]value2"',
|
|
105
|
+
required=False,
|
|
106
|
+
type=str,
|
|
107
|
+
default=None,
|
|
108
|
+
)
|
|
109
|
+
def sync_ivanti_neurons(regscale_ssp_id: int, filter: str) -> None:
|
|
110
|
+
"""Sync Assets from Ivanti Neurons to RegScale."""
|
|
111
|
+
from regscale.models.integration_models.synqly_models.connectors import Assets
|
|
112
|
+
|
|
113
|
+
assets_ivanti_neurons = Assets("ivanti_neurons")
|
|
114
|
+
assets_ivanti_neurons.run_sync(regscale_ssp_id=regscale_ssp_id, filter=filter.split(";") if filter else [])
|
|
115
|
+
|
|
116
|
+
|
|
100
117
|
@assets.command(name="sync_nozomi_vantage")
|
|
101
118
|
@regscale_ssp_id()
|
|
102
119
|
@click.option(
|
|
@@ -45,7 +45,7 @@ def authenticate(client_id, client_secret):
|
|
|
45
45
|
"--client_id",
|
|
46
46
|
"-ci",
|
|
47
47
|
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
48
|
-
default=
|
|
48
|
+
default=None,
|
|
49
49
|
hide_input=False,
|
|
50
50
|
required=False,
|
|
51
51
|
)
|
|
@@ -53,7 +53,7 @@ def authenticate(client_id, client_secret):
|
|
|
53
53
|
"--client_secret",
|
|
54
54
|
"-cs",
|
|
55
55
|
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
56
|
-
default=
|
|
56
|
+
default=None,
|
|
57
57
|
hide_input=False,
|
|
58
58
|
required=False,
|
|
59
59
|
)
|
|
@@ -77,9 +77,9 @@ def inventory(
|
|
|
77
77
|
"""Process inventory from Wiz and create assets in RegScale."""
|
|
78
78
|
from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
|
|
79
79
|
|
|
80
|
-
if
|
|
80
|
+
if client_secret is None:
|
|
81
81
|
client_secret = WizVariables.wizClientSecret
|
|
82
|
-
if
|
|
82
|
+
if client_id is None:
|
|
83
83
|
client_id = WizVariables.wizClientId
|
|
84
84
|
|
|
85
85
|
scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
|
|
@@ -104,14 +104,14 @@ def inventory(
|
|
|
104
104
|
@click.option(
|
|
105
105
|
"--client_id",
|
|
106
106
|
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
107
|
-
default=
|
|
107
|
+
default=None,
|
|
108
108
|
hide_input=False,
|
|
109
109
|
required=False,
|
|
110
110
|
)
|
|
111
111
|
@click.option(
|
|
112
112
|
"--client_secret",
|
|
113
113
|
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
114
|
-
default=
|
|
114
|
+
default=None,
|
|
115
115
|
hide_input=True,
|
|
116
116
|
required=False,
|
|
117
117
|
)
|
|
@@ -140,9 +140,9 @@ def issues(
|
|
|
140
140
|
from regscale.integrations.commercial.wizv2.issue import WizIssue
|
|
141
141
|
import json
|
|
142
142
|
|
|
143
|
-
if
|
|
143
|
+
if client_secret is None:
|
|
144
144
|
client_secret = WizVariables.wizClientSecret
|
|
145
|
-
if
|
|
145
|
+
if client_id is None:
|
|
146
146
|
client_id = WizVariables.wizClientId
|
|
147
147
|
|
|
148
148
|
check_license()
|
|
@@ -166,7 +166,7 @@ def issues(
|
|
|
166
166
|
"--client_id",
|
|
167
167
|
"-ci",
|
|
168
168
|
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
169
|
-
default=
|
|
169
|
+
default=None,
|
|
170
170
|
hide_input=False,
|
|
171
171
|
required=False,
|
|
172
172
|
)
|
|
@@ -174,7 +174,7 @@ def issues(
|
|
|
174
174
|
"--client_secret",
|
|
175
175
|
"-cs",
|
|
176
176
|
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
177
|
-
default=
|
|
177
|
+
default=None,
|
|
178
178
|
hide_input=True,
|
|
179
179
|
required=False,
|
|
180
180
|
)
|
|
@@ -194,9 +194,9 @@ def attach_sbom(
|
|
|
194
194
|
from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
|
|
195
195
|
from regscale.integrations.commercial.wizv2.utils import fetch_sbom_report
|
|
196
196
|
|
|
197
|
-
if
|
|
197
|
+
if client_secret is None:
|
|
198
198
|
client_secret = WizVariables.wizClientSecret
|
|
199
|
-
if
|
|
199
|
+
if client_id is None:
|
|
200
200
|
client_id = WizVariables.wizClientId
|
|
201
201
|
|
|
202
202
|
wiz_authenticate(
|
|
@@ -234,7 +234,7 @@ def threats():
|
|
|
234
234
|
"--client_id",
|
|
235
235
|
"-ci",
|
|
236
236
|
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
237
|
-
default=
|
|
237
|
+
default=None,
|
|
238
238
|
hide_input=False,
|
|
239
239
|
required=False,
|
|
240
240
|
)
|
|
@@ -242,7 +242,7 @@ def threats():
|
|
|
242
242
|
"--client_secret",
|
|
243
243
|
"-cs",
|
|
244
244
|
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
245
|
-
default=
|
|
245
|
+
default=None,
|
|
246
246
|
hide_input=True,
|
|
247
247
|
required=False,
|
|
248
248
|
)
|
|
@@ -266,9 +266,9 @@ def vulnerabilities(
|
|
|
266
266
|
"""Process vulnerabilities from Wiz"""
|
|
267
267
|
from regscale.integrations.commercial.wizv2.scanner import WizVulnerabilityIntegration
|
|
268
268
|
|
|
269
|
-
if
|
|
269
|
+
if client_secret is None:
|
|
270
270
|
client_secret = WizVariables.wizClientSecret
|
|
271
|
-
if
|
|
271
|
+
if client_id is None:
|
|
272
272
|
client_id = WizVariables.wizClientId
|
|
273
273
|
|
|
274
274
|
scanner = WizVulnerabilityIntegration(plan_id=regscale_ssp_id)
|
|
@@ -286,7 +286,7 @@ def vulnerabilities(
|
|
|
286
286
|
"--client_id",
|
|
287
287
|
"-ci",
|
|
288
288
|
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
289
|
-
default=
|
|
289
|
+
default=None,
|
|
290
290
|
hide_input=False,
|
|
291
291
|
required=False,
|
|
292
292
|
)
|
|
@@ -294,7 +294,7 @@ def vulnerabilities(
|
|
|
294
294
|
"--client_secret",
|
|
295
295
|
"-cs",
|
|
296
296
|
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
297
|
-
default=
|
|
297
|
+
default=None,
|
|
298
298
|
hide_input=True,
|
|
299
299
|
required=False,
|
|
300
300
|
)
|
|
@@ -314,9 +314,9 @@ def add_report_evidence(
|
|
|
314
314
|
from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
|
|
315
315
|
from regscale.integrations.commercial.wizv2.utils import fetch_report_by_id
|
|
316
316
|
|
|
317
|
-
if
|
|
317
|
+
if client_secret is None:
|
|
318
318
|
client_secret = WizVariables.wizClientSecret
|
|
319
|
-
if
|
|
319
|
+
if client_id is None:
|
|
320
320
|
client_id = WizVariables.wizClientId
|
|
321
321
|
|
|
322
322
|
wiz_authenticate(
|
|
@@ -346,7 +346,7 @@ def add_report_evidence(
|
|
|
346
346
|
"--client_id",
|
|
347
347
|
"-i",
|
|
348
348
|
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
349
|
-
default=
|
|
349
|
+
default=None,
|
|
350
350
|
hide_input=False,
|
|
351
351
|
required=False,
|
|
352
352
|
)
|
|
@@ -354,7 +354,7 @@ def add_report_evidence(
|
|
|
354
354
|
"--client_secret",
|
|
355
355
|
"-s",
|
|
356
356
|
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
357
|
-
default=
|
|
357
|
+
default=None,
|
|
358
358
|
hide_input=True,
|
|
359
359
|
required=False,
|
|
360
360
|
)
|
|
@@ -444,9 +444,9 @@ def sync_compliance(
|
|
|
444
444
|
return
|
|
445
445
|
|
|
446
446
|
# Use environment variables if not provided
|
|
447
|
-
if
|
|
447
|
+
if client_secret is None:
|
|
448
448
|
client_secret = WizVariables.wizClientSecret
|
|
449
|
-
if
|
|
449
|
+
if client_id is None:
|
|
450
450
|
client_id = WizVariables.wizClientId
|
|
451
451
|
|
|
452
452
|
# Resolve framework ID using the enhanced framework resolution
|
|
@@ -582,9 +582,9 @@ def compliance_report(
|
|
|
582
582
|
from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
|
|
583
583
|
|
|
584
584
|
# Use environment variables if not provided
|
|
585
|
-
if
|
|
585
|
+
if client_secret is None:
|
|
586
586
|
client_secret = WizVariables.wizClientSecret
|
|
587
|
-
if
|
|
587
|
+
if client_id is None:
|
|
588
588
|
client_id = WizVariables.wizClientId
|
|
589
589
|
|
|
590
590
|
# Create and run the compliance report processor
|