regscale-cli 6.16.0.0__py3-none-any.whl → 6.16.2.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/core/app/application.py +1 -0
- regscale/core/app/internal/login.py +1 -1
- regscale/core/app/internal/poam_editor.py +1 -1
- regscale/core/app/utils/app_utils.py +1 -1
- regscale/core/app/utils/parser_utils.py +2 -2
- regscale/integrations/commercial/__init__.py +2 -2
- regscale/integrations/commercial/ad.py +1 -1
- regscale/integrations/commercial/azure/intune.py +1 -0
- regscale/integrations/commercial/grype/__init__.py +3 -0
- regscale/integrations/commercial/grype/commands.py +72 -0
- regscale/integrations/commercial/grype/scanner.py +390 -0
- regscale/integrations/commercial/import_all/import_all_cmd.py +2 -2
- regscale/integrations/commercial/nessus/scanner.py +3 -0
- regscale/integrations/commercial/opentext/__init__.py +6 -0
- regscale/integrations/commercial/opentext/commands.py +77 -0
- regscale/integrations/commercial/opentext/scanner.py +449 -85
- regscale/integrations/commercial/sap/sysdig/sysdig_scanner.py +4 -0
- regscale/integrations/commercial/sap/tenable/click.py +1 -1
- regscale/integrations/commercial/sap/tenable/scanner.py +8 -2
- regscale/integrations/commercial/tenablev2/click.py +39 -16
- regscale/integrations/commercial/trivy/__init__.py +5 -0
- regscale/integrations/commercial/trivy/commands.py +74 -0
- regscale/integrations/commercial/trivy/scanner.py +276 -0
- regscale/integrations/commercial/wizv2/click.py +9 -21
- regscale/integrations/commercial/wizv2/scanner.py +2 -1
- regscale/integrations/commercial/wizv2/utils.py +146 -70
- regscale/integrations/jsonl_scanner_integration.py +869 -0
- regscale/integrations/public/fedramp/fedramp_common.py +4 -4
- regscale/integrations/public/fedramp/import_workbook.py +1 -1
- regscale/integrations/public/fedramp/inventory_items.py +3 -3
- regscale/integrations/public/fedramp/poam/scanner.py +51 -44
- regscale/integrations/public/fedramp/ssp_logger.py +6 -6
- regscale/integrations/scanner_integration.py +268 -64
- regscale/models/app_models/mapping.py +3 -3
- regscale/models/integration_models/amazon_models/inspector.py +15 -17
- regscale/models/integration_models/aqua.py +1 -5
- regscale/models/integration_models/cisa_kev_data.json +100 -10
- regscale/models/integration_models/ecr_models/ecr.py +2 -6
- regscale/models/integration_models/{flat_file_importer.py → flat_file_importer/__init__.py} +7 -4
- regscale/models/integration_models/grype_import.py +3 -3
- regscale/models/integration_models/prisma.py +3 -3
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/integration_models/synqly_models/connectors/assets.py +1 -0
- regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +2 -0
- regscale/models/integration_models/tenable_models/integration.py +46 -10
- regscale/models/integration_models/trivy_import.py +1 -1
- regscale/models/integration_models/xray.py +1 -1
- regscale/models/regscale_models/__init__.py +2 -0
- regscale/models/regscale_models/control_implementation.py +18 -44
- regscale/models/regscale_models/inherited_control.py +61 -0
- regscale/models/regscale_models/issue.py +3 -2
- regscale/models/regscale_models/mixins/parent_cache.py +1 -1
- regscale/models/regscale_models/regscale_model.py +73 -7
- regscale/models/regscale_models/vulnerability.py +61 -8
- {regscale_cli-6.16.0.0.dist-info → regscale_cli-6.16.2.0.dist-info}/METADATA +3 -3
- {regscale_cli-6.16.0.0.dist-info → regscale_cli-6.16.2.0.dist-info}/RECORD +62 -56
- tests/regscale/core/test_logz.py +8 -0
- regscale/integrations/commercial/grype.py +0 -165
- regscale/integrations/commercial/opentext/click.py +0 -99
- regscale/integrations/commercial/trivy.py +0 -162
- {regscale_cli-6.16.0.0.dist-info → regscale_cli-6.16.2.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.16.0.0.dist-info → regscale_cli-6.16.2.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.16.0.0.dist-info → regscale_cli-6.16.2.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.16.0.0.dist-info → regscale_cli-6.16.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Wiz V2 Utils
|
|
3
|
+
"""
|
|
4
|
+
|
|
1
5
|
import codecs
|
|
2
6
|
import csv
|
|
3
7
|
import datetime
|
|
@@ -32,10 +36,10 @@ from regscale.integrations.commercial.wizv2.constants import (
|
|
|
32
36
|
MAX_RETRIES,
|
|
33
37
|
CHECK_INTERVAL_FOR_DOWNLOAD_REPORT,
|
|
34
38
|
)
|
|
39
|
+
from regscale.integrations.commercial.wizv2.models import ComplianceReport, ComplianceCheckStatus
|
|
35
40
|
from regscale.integrations.commercial.wizv2.variables import WizVariables
|
|
36
41
|
from regscale.integrations.commercial.wizv2.wiz_auth import wiz_authenticate
|
|
37
42
|
from regscale.models import File, Sbom, SecurityPlan, Catalog, ControlImplementation, Assessment, regscale_models
|
|
38
|
-
from regscale.integrations.commercial.wizv2.models import ComplianceReport, ComplianceCheckStatus
|
|
39
43
|
from regscale.utils import PaginatedGraphQLClient
|
|
40
44
|
from regscale.utils.decorators import deprecated
|
|
41
45
|
|
|
@@ -51,41 +55,29 @@ def get_notes_from_wiz_props(wiz_entity_properties: Dict, external_id: str) -> s
|
|
|
51
55
|
:return: Notes
|
|
52
56
|
:rtype: str
|
|
53
57
|
"""
|
|
58
|
+
# Define property mappings with display names and keys
|
|
59
|
+
property_mappings = [
|
|
60
|
+
("External ID", lambda: external_id, lambda x: x),
|
|
61
|
+
("Cloud Platform", "cloudPlatform", str),
|
|
62
|
+
("Provider Unique ID", "providerUniqueId", str),
|
|
63
|
+
("cloudProviderURL", "cloudProviderURL", lambda x: f'<a href="{x}" target="_blank">{x}</a>'),
|
|
64
|
+
("Vertex ID", "_vertexID", str),
|
|
65
|
+
("Severity Name", "severity_name", str),
|
|
66
|
+
("Severity Description", "severity_description", str),
|
|
67
|
+
]
|
|
68
|
+
|
|
54
69
|
notes = []
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
else
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
(
|
|
67
|
-
notes.append(
|
|
68
|
-
f"""cloudProviderURL:<a href="{wiz_entity_properties.get("cloudProviderURL")}"
|
|
69
|
-
target="_blank">{wiz_entity_properties.get("cloudProviderURL")}</a>"""
|
|
70
|
-
)
|
|
71
|
-
if wiz_entity_properties.get("cloudProviderURL")
|
|
72
|
-
else None
|
|
73
|
-
)
|
|
74
|
-
(
|
|
75
|
-
notes.append(f"Vertex ID: {wiz_entity_properties.get('_vertexID')}")
|
|
76
|
-
if wiz_entity_properties.get("_vertexID")
|
|
77
|
-
else None
|
|
78
|
-
)
|
|
79
|
-
(
|
|
80
|
-
notes.append(f"Severity Name: {wiz_entity_properties.get('severity_name')}")
|
|
81
|
-
if wiz_entity_properties.get("severity_name")
|
|
82
|
-
else None
|
|
83
|
-
)
|
|
84
|
-
(
|
|
85
|
-
notes.append(f"Severity Description: {wiz_entity_properties.get('severity_description')}")
|
|
86
|
-
if wiz_entity_properties.get("severity_description")
|
|
87
|
-
else None
|
|
88
|
-
)
|
|
70
|
+
for display_name, key_or_func, formatter in property_mappings:
|
|
71
|
+
# Handle external_id special case
|
|
72
|
+
if callable(key_or_func):
|
|
73
|
+
value = key_or_func()
|
|
74
|
+
else:
|
|
75
|
+
value = wiz_entity_properties.get(key_or_func)
|
|
76
|
+
|
|
77
|
+
if value:
|
|
78
|
+
formatted_value = formatter(value)
|
|
79
|
+
notes.append(f"{display_name}: {formatted_value}")
|
|
80
|
+
|
|
89
81
|
return "<br>".join(notes)
|
|
90
82
|
|
|
91
83
|
|
|
@@ -108,7 +100,6 @@ def create_asset_type(asset_type: str) -> str:
|
|
|
108
100
|
:return: Asset type
|
|
109
101
|
:rtype: str
|
|
110
102
|
"""
|
|
111
|
-
#
|
|
112
103
|
asset_type = asset_type.title().replace("_", " ")
|
|
113
104
|
meta_data_list = regscale_models.Metadata.get_metadata_by_module_field(module="assets", field="Asset Type")
|
|
114
105
|
if not any(meta_data.value == asset_type for meta_data in meta_data_list):
|
|
@@ -360,8 +351,9 @@ def create_report_if_needed(
|
|
|
360
351
|
)
|
|
361
352
|
logger.info(f"Wiz compliance report created with ID {wiz_report_id}")
|
|
362
353
|
return [wiz_report_id]
|
|
363
|
-
|
|
364
|
-
|
|
354
|
+
logger.debug(f"Returning report ids for these reports {(report['name'] for report in reports)}")
|
|
355
|
+
reports = [report["id"] for report in reports if any(frame in report["name"] for frame in frames)]
|
|
356
|
+
return reports
|
|
365
357
|
|
|
366
358
|
|
|
367
359
|
def fetch_and_process_report_data(wiz_report_ids: List) -> List:
|
|
@@ -384,6 +376,95 @@ def fetch_and_process_report_data(wiz_report_ids: List) -> List:
|
|
|
384
376
|
return report_data
|
|
385
377
|
|
|
386
378
|
|
|
379
|
+
def get_or_create_report_id(
|
|
380
|
+
project_id: str,
|
|
381
|
+
frameworks: List[str],
|
|
382
|
+
wiz_frameworks: List[Dict],
|
|
383
|
+
existing_reports: List[Dict],
|
|
384
|
+
target_framework: str,
|
|
385
|
+
) -> str:
|
|
386
|
+
"""
|
|
387
|
+
Get an existing report ID or create a new one for the target framework.
|
|
388
|
+
|
|
389
|
+
:param project_id: Project identifier
|
|
390
|
+
:param frameworks: List of framework names
|
|
391
|
+
:param wiz_frameworks: List of framework details with IDs
|
|
392
|
+
:param existing_reports: List of existing reports
|
|
393
|
+
:param target_framework: Target framework name with underscores
|
|
394
|
+
:return: Single report ID
|
|
395
|
+
"""
|
|
396
|
+
report_name = f"{target_framework}_project_{project_id}"
|
|
397
|
+
|
|
398
|
+
# Check for existing report with exact name
|
|
399
|
+
for report in existing_reports:
|
|
400
|
+
if report.get("name") == report_name:
|
|
401
|
+
logger.info(f"Found existing report '{report_name}' with ID {report['id']}")
|
|
402
|
+
return report["id"]
|
|
403
|
+
|
|
404
|
+
# Create new report if no exact match found
|
|
405
|
+
try:
|
|
406
|
+
framework_index = frameworks.index(target_framework)
|
|
407
|
+
framework_id = wiz_frameworks[framework_index].get("id")
|
|
408
|
+
|
|
409
|
+
report_id = create_compliance_report(
|
|
410
|
+
wiz_project_id=project_id, report_name=report_name, framework_id=framework_id
|
|
411
|
+
)
|
|
412
|
+
logger.info(f"Created new report '{report_name}' with ID {report_id}")
|
|
413
|
+
return report_id
|
|
414
|
+
except ValueError:
|
|
415
|
+
logger.error(f"Framework '{target_framework}' not found in frameworks list")
|
|
416
|
+
raise
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def fetch_report_data(report_id: str) -> List[Dict]:
|
|
420
|
+
"""
|
|
421
|
+
Fetch and process data for a single report ID.
|
|
422
|
+
|
|
423
|
+
:param report_id: Report identifier
|
|
424
|
+
:return: List of report data rows
|
|
425
|
+
"""
|
|
426
|
+
try:
|
|
427
|
+
download_url = get_report_url_and_status(report_id)
|
|
428
|
+
logger.info(f"Fetching report {report_id} from: {download_url}")
|
|
429
|
+
|
|
430
|
+
with closing(requests.get(url=download_url, stream=True, timeout=10)) as response:
|
|
431
|
+
response.raise_for_status()
|
|
432
|
+
logger.info(f"Streaming and parsing report {report_id}")
|
|
433
|
+
|
|
434
|
+
reader = csv.DictReader(codecs.iterdecode(response.iter_lines(), encoding="utf-8"), delimiter=",")
|
|
435
|
+
return list(reader)
|
|
436
|
+
except requests.RequestException as e:
|
|
437
|
+
error_and_exit(f"Failed to fetch report {report_id}: {str(e)}")
|
|
438
|
+
except csv.Error as e:
|
|
439
|
+
error_and_exit(f"Failed to parse CSV for report {report_id}: {str(e)}")
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
# Usage example
|
|
443
|
+
def process_single_report(
|
|
444
|
+
project_id: str,
|
|
445
|
+
frameworks: List[str],
|
|
446
|
+
wiz_frameworks: List[Dict],
|
|
447
|
+
existing_reports: List[Dict],
|
|
448
|
+
target_framework: str,
|
|
449
|
+
) -> List[Dict]:
|
|
450
|
+
"""Process a single report and return its data.
|
|
451
|
+
:param project_id: Project identifier
|
|
452
|
+
:param frameworks: List of framework names
|
|
453
|
+
:param wiz_frameworks: List of framework details with IDs
|
|
454
|
+
:param existing_reports: List of existing reports
|
|
455
|
+
:param target_framework: Target framework name with underscores
|
|
456
|
+
:return: List of report data rows
|
|
457
|
+
"""
|
|
458
|
+
report_id = get_or_create_report_id(
|
|
459
|
+
project_id=project_id,
|
|
460
|
+
frameworks=frameworks,
|
|
461
|
+
wiz_frameworks=wiz_frameworks,
|
|
462
|
+
existing_reports=existing_reports,
|
|
463
|
+
target_framework=target_framework,
|
|
464
|
+
)
|
|
465
|
+
return fetch_report_data(report_id)
|
|
466
|
+
|
|
467
|
+
|
|
387
468
|
def fetch_framework_report(wiz_project_id: str, snake_framework: str) -> List[Any]:
|
|
388
469
|
"""
|
|
389
470
|
Fetch Framework Report from Wiz.
|
|
@@ -395,10 +476,17 @@ def fetch_framework_report(wiz_project_id: str, snake_framework: str) -> List[An
|
|
|
395
476
|
"""
|
|
396
477
|
wiz_frameworks = fetch_frameworks()
|
|
397
478
|
frames = get_framework_names(wiz_frameworks)
|
|
398
|
-
reports = list(query_reports())
|
|
479
|
+
reports = list(query_reports(wiz_project_id))
|
|
480
|
+
|
|
481
|
+
report_data = process_single_report(
|
|
482
|
+
project_id=wiz_project_id,
|
|
483
|
+
frameworks=frames,
|
|
484
|
+
wiz_frameworks=wiz_frameworks,
|
|
485
|
+
existing_reports=reports,
|
|
486
|
+
target_framework=snake_framework,
|
|
487
|
+
)
|
|
399
488
|
|
|
400
|
-
|
|
401
|
-
return fetch_and_process_report_data(wiz_report_ids)
|
|
489
|
+
return report_data
|
|
402
490
|
|
|
403
491
|
|
|
404
492
|
def fetch_frameworks() -> list:
|
|
@@ -425,23 +513,21 @@ def fetch_frameworks() -> list:
|
|
|
425
513
|
"""
|
|
426
514
|
variables = {
|
|
427
515
|
"policyTypes": "CLOUD",
|
|
428
|
-
"first": 500,
|
|
429
516
|
}
|
|
430
517
|
resp = send_request(
|
|
431
518
|
query=query,
|
|
432
519
|
variables=variables,
|
|
433
520
|
api_endpoint_url=WizVariables.wizUrl,
|
|
434
521
|
)
|
|
435
|
-
|
|
436
|
-
if resp.ok:
|
|
437
|
-
# ["data"]["securityFrameworks"]["nodes"]
|
|
522
|
+
logger.debug(f"Response: {resp}")
|
|
523
|
+
if resp and resp.ok:
|
|
438
524
|
data = resp.json()
|
|
439
525
|
return data.get("data", {}).get("securityFrameworks", {}).get("nodes")
|
|
440
526
|
else:
|
|
441
527
|
error_and_exit(f"Wiz Error: {resp.status_code if resp else None} - {resp.text if resp else 'No response'}")
|
|
442
528
|
|
|
443
529
|
|
|
444
|
-
def query_reports() -> list:
|
|
530
|
+
def query_reports(wiz_project_id: str) -> list:
|
|
445
531
|
"""
|
|
446
532
|
Query Report table from Wiz
|
|
447
533
|
|
|
@@ -450,7 +536,7 @@ def query_reports() -> list:
|
|
|
450
536
|
"""
|
|
451
537
|
|
|
452
538
|
# The variables sent along with the above query
|
|
453
|
-
variables = {"first": 100, "filterBy": {}}
|
|
539
|
+
variables = {"first": 100, "filterBy": {"projectId": f"{wiz_project_id}"}}
|
|
454
540
|
|
|
455
541
|
res = send_request(
|
|
456
542
|
query=REPORTS_QUERY,
|
|
@@ -462,6 +548,7 @@ def query_reports() -> list:
|
|
|
462
548
|
if "errors" in res.json().keys():
|
|
463
549
|
error_and_exit(f'Wiz Error: {res.json()["errors"]}')
|
|
464
550
|
json_result = res.json()
|
|
551
|
+
logger.debug("JSON Result: %s", json_result)
|
|
465
552
|
result = json_result.get("data", {}).get("reports", {}).get("nodes")
|
|
466
553
|
except requests.JSONDecodeError:
|
|
467
554
|
error_and_exit(f"Unable to fetch reports from Wiz: {res.status_code}, {res.reason}")
|
|
@@ -583,7 +670,6 @@ def _sync_compliance(
|
|
|
583
670
|
wiz_project_id: str,
|
|
584
671
|
regscale_id: int,
|
|
585
672
|
regscale_module: str,
|
|
586
|
-
include_not_implemented: bool,
|
|
587
673
|
client_id: str,
|
|
588
674
|
client_secret: str,
|
|
589
675
|
catalog_id: int,
|
|
@@ -595,7 +681,6 @@ def _sync_compliance(
|
|
|
595
681
|
:param str wiz_project_id: Wiz Project ID
|
|
596
682
|
:param int regscale_id: RegScale ID
|
|
597
683
|
:param str regscale_module: RegScale module
|
|
598
|
-
:param bool include_not_implemented: Include not implemented controls
|
|
599
684
|
:param str client_id: Wiz Client ID
|
|
600
685
|
:param str client_secret: Wiz Client Secret
|
|
601
686
|
:param int catalog_id: Catalog ID, defaults to None
|
|
@@ -623,7 +708,7 @@ def _sync_compliance(
|
|
|
623
708
|
}
|
|
624
709
|
sync_framework = framework_mapping.get(framework)
|
|
625
710
|
snake_framework = sync_framework.replace(" ", "_")
|
|
626
|
-
logger.
|
|
711
|
+
logger.debug(f"{snake_framework=}")
|
|
627
712
|
logger.info("Fetching Wiz compliance report for project ID %s", wiz_project_id)
|
|
628
713
|
report_data = fetch_framework_report(wiz_project_id, snake_framework)
|
|
629
714
|
report_models = []
|
|
@@ -631,12 +716,12 @@ def _sync_compliance(
|
|
|
631
716
|
|
|
632
717
|
catalog = Catalog.get_with_all_details(catalog_id=catalog_id)
|
|
633
718
|
controls = catalog.get("controls") if catalog else []
|
|
634
|
-
passing_controls =
|
|
635
|
-
failing_controls =
|
|
636
|
-
controls_to_reports =
|
|
637
|
-
|
|
719
|
+
passing_controls = {}
|
|
720
|
+
failing_controls = {}
|
|
721
|
+
controls_to_reports = {}
|
|
722
|
+
|
|
638
723
|
compliance_job_progress.update(fetch_regscale_data_job, completed=True, advance=1)
|
|
639
|
-
logger.info(
|
|
724
|
+
logger.info("Analyzing ComplianceReport for framework %s from Wiz" % sync_framework)
|
|
640
725
|
running_compliance_job = compliance_job_progress.add_task(
|
|
641
726
|
"[#f68d1f]Building compliance posture from wiz report...",
|
|
642
727
|
total=len(report_data),
|
|
@@ -654,20 +739,11 @@ def _sync_compliance(
|
|
|
654
739
|
)
|
|
655
740
|
report_models.append(cr)
|
|
656
741
|
compliance_job_progress.update(running_compliance_job, advance=1)
|
|
657
|
-
except ValidationError
|
|
658
|
-
|
|
742
|
+
except ValidationError:
|
|
743
|
+
error_message = traceback.format_exc()
|
|
744
|
+
logger.error(f"Error creating ComplianceReport: {error_message}")
|
|
659
745
|
try:
|
|
660
746
|
saving_regscale_data_job = compliance_job_progress.add_task("[#f68d1f]Saving RegScale data...", total=1)
|
|
661
|
-
ControlImplementation.create_control_implementations(
|
|
662
|
-
controls=controls,
|
|
663
|
-
parent_id=regscale_id,
|
|
664
|
-
parent_module=regscale_module,
|
|
665
|
-
existing_implementation_dict=existing_implementations,
|
|
666
|
-
full_controls=passing_controls,
|
|
667
|
-
partial_controls={},
|
|
668
|
-
failing_controls=failing_controls,
|
|
669
|
-
include_not_implemented=include_not_implemented,
|
|
670
|
-
)
|
|
671
747
|
create_assessment_from_compliance_report(
|
|
672
748
|
controls_to_reports=controls_to_reports,
|
|
673
749
|
regscale_id=regscale_id,
|
|
@@ -676,9 +752,9 @@ def _sync_compliance(
|
|
|
676
752
|
)
|
|
677
753
|
compliance_job_progress.update(saving_regscale_data_job, completed=True, advance=1)
|
|
678
754
|
|
|
679
|
-
except Exception
|
|
680
|
-
|
|
681
|
-
|
|
755
|
+
except Exception:
|
|
756
|
+
error_message = traceback.format_exc()
|
|
757
|
+
logger.error(f"Error creating ControlImplementations from compliance report: {error_message}")
|
|
682
758
|
return report_models
|
|
683
759
|
|
|
684
760
|
|