regscale-cli 6.23.0.1__py3-none-any.whl → 6.24.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/application.py +2 -0
- regscale/integrations/commercial/__init__.py +1 -0
- regscale/integrations/commercial/jira.py +95 -22
- regscale/integrations/commercial/sarif/sarif_converter.py +1 -1
- regscale/integrations/commercial/wizv2/click.py +132 -2
- regscale/integrations/commercial/wizv2/compliance_report.py +1574 -0
- regscale/integrations/commercial/wizv2/constants.py +72 -2
- regscale/integrations/commercial/wizv2/data_fetcher.py +61 -0
- regscale/integrations/commercial/wizv2/file_cleanup.py +104 -0
- regscale/integrations/commercial/wizv2/issue.py +775 -27
- regscale/integrations/commercial/wizv2/policy_compliance.py +599 -181
- regscale/integrations/commercial/wizv2/reports.py +243 -0
- regscale/integrations/commercial/wizv2/scanner.py +668 -245
- regscale/integrations/compliance_integration.py +534 -56
- regscale/integrations/due_date_handler.py +210 -0
- regscale/integrations/public/cci_importer.py +444 -0
- regscale/integrations/scanner_integration.py +718 -153
- regscale/models/integration_models/CCI_List.xml +1 -0
- regscale/models/integration_models/cisa_kev_data.json +18 -3
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/control_implementation.py +13 -3
- regscale/models/regscale_models/form_field_value.py +1 -1
- regscale/models/regscale_models/milestone.py +1 -0
- regscale/models/regscale_models/regscale_model.py +225 -60
- regscale/models/regscale_models/security_plan.py +3 -2
- regscale/regscale.py +7 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.1.dist-info}/METADATA +17 -17
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.1.dist-info}/RECORD +45 -28
- tests/fixtures/test_fixture.py +13 -8
- tests/regscale/integrations/public/__init__.py +0 -0
- tests/regscale/integrations/public/test_alienvault.py +220 -0
- tests/regscale/integrations/public/test_cci.py +458 -0
- tests/regscale/integrations/public/test_cisa.py +1021 -0
- tests/regscale/integrations/public/test_emass.py +518 -0
- tests/regscale/integrations/public/test_fedramp.py +851 -0
- tests/regscale/integrations/public/test_fedramp_cis_crm.py +3661 -0
- tests/regscale/integrations/public/test_file_uploads.py +506 -0
- tests/regscale/integrations/public/test_oscal.py +453 -0
- tests/regscale/models/test_form_field_value_integration.py +304 -0
- tests/regscale/models/test_module_integration.py +582 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.1.dist-info}/LICENSE +0 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.1.dist-info}/WHEEL +0 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.1.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.23.0.1.dist-info → regscale_cli-6.24.0.1.dist-info}/top_level.txt +0 -0
regscale/_version.py
CHANGED
regscale/core/app/application.py
CHANGED
|
@@ -214,6 +214,7 @@ class Application(metaclass=Singleton):
|
|
|
214
214
|
"low": 365,
|
|
215
215
|
"medium": 90,
|
|
216
216
|
"status": "Open",
|
|
217
|
+
"minimumSeverity": "low",
|
|
217
218
|
},
|
|
218
219
|
"xray": {
|
|
219
220
|
"critical": 30,
|
|
@@ -264,6 +265,7 @@ class Application(metaclass=Singleton):
|
|
|
264
265
|
"token": DEFAULT_POPULATED,
|
|
265
266
|
"userId": "enter RegScale user id here",
|
|
266
267
|
"useMilestones": False,
|
|
268
|
+
"preventAutoClose": True,
|
|
267
269
|
"otx": "enter AlienVault API key here",
|
|
268
270
|
"wizAccessToken": DEFAULT_POPULATED,
|
|
269
271
|
"wizAuthUrl": "https://auth.wiz.io/oauth/token",
|
|
@@ -491,6 +491,7 @@ show_mapping(veracode, "veracode")
|
|
|
491
491
|
"vulnerabilities": "regscale.integrations.commercial.wizv2.click.vulnerabilities",
|
|
492
492
|
"add_report_evidence": "regscale.integrations.commercial.wizv2.click.add_report_evidence",
|
|
493
493
|
"sync_compliance": "regscale.integrations.commercial.wizv2.click.sync_compliance",
|
|
494
|
+
"compliance_report": "regscale.integrations.commercial.wizv2.click.compliance_report",
|
|
494
495
|
},
|
|
495
496
|
name="wiz",
|
|
496
497
|
)
|
|
@@ -719,10 +719,7 @@ def create_and_update_regscale_issues(args: Tuple, thread: int) -> None:
|
|
|
719
719
|
parent_module=parent_module,
|
|
720
720
|
)
|
|
721
721
|
# create the issue in RegScale
|
|
722
|
-
if regscale_issue :=
|
|
723
|
-
app=app,
|
|
724
|
-
issue=issue,
|
|
725
|
-
):
|
|
722
|
+
if regscale_issue := issue.create():
|
|
726
723
|
logger.debug(
|
|
727
724
|
"Created issue #%i-%s in RegScale.",
|
|
728
725
|
regscale_issue.id,
|
|
@@ -812,7 +809,7 @@ def fetch_jira_objects(
|
|
|
812
809
|
jira_client: JIRA, jira_project: str, jira_issue_type: str, jql_str: str = None, sync_tasks_only: bool = False
|
|
813
810
|
) -> list[jiraIssue]:
|
|
814
811
|
"""
|
|
815
|
-
Fetch all issues from Jira for the provided project
|
|
812
|
+
Fetch all issues from Jira for the provided project using the enhanced search API.
|
|
816
813
|
|
|
817
814
|
:param JIRA jira_client: Jira client to use for the request
|
|
818
815
|
:param str jira_project: Name of the project in Jira
|
|
@@ -822,15 +819,77 @@ def fetch_jira_objects(
|
|
|
822
819
|
:return: List of Jira issues
|
|
823
820
|
:rtype: list[jiraIssue]
|
|
824
821
|
"""
|
|
825
|
-
start_pointer = 0
|
|
826
|
-
page_size = 100
|
|
827
|
-
jira_objects = []
|
|
828
822
|
if sync_tasks_only:
|
|
829
823
|
validate_issue_type(jira_client, jira_issue_type)
|
|
830
824
|
output_str = "task"
|
|
831
825
|
else:
|
|
832
826
|
output_str = "issue"
|
|
833
827
|
logger.info("Fetching %s(s) from Jira...", output_str.lower())
|
|
828
|
+
try:
|
|
829
|
+
max_results = 100 # 100 is the max allowed by Jira
|
|
830
|
+
jira_issues = []
|
|
831
|
+
issue_response = jira_client.enhanced_search_issues(
|
|
832
|
+
jql_str=jql_str or f"project = {jira_project}",
|
|
833
|
+
maxResults=max_results,
|
|
834
|
+
)
|
|
835
|
+
jira_issues.extend(issue_response)
|
|
836
|
+
logger.info(
|
|
837
|
+
"%i Jira %s(s) retrieved.",
|
|
838
|
+
len(jira_issues),
|
|
839
|
+
output_str.lower(),
|
|
840
|
+
)
|
|
841
|
+
# Handle pagination if there are more issues to fetch
|
|
842
|
+
while issue_response.nextPageToken:
|
|
843
|
+
issue_response = jira_client.enhanced_search_issues(
|
|
844
|
+
jql_str=jql_str, maxResults=max_results, nextPageToken=issue_response.nextPageToken
|
|
845
|
+
)
|
|
846
|
+
jira_issues.extend(issue_response)
|
|
847
|
+
logger.info(
|
|
848
|
+
"%i Jira %s(s) retrieved.",
|
|
849
|
+
len(jira_issues),
|
|
850
|
+
output_str.lower(),
|
|
851
|
+
)
|
|
852
|
+
# Save artifacts file and log final result if we have issues
|
|
853
|
+
if jira_issues:
|
|
854
|
+
save_jira_issues(jira_issues, jira_project, jira_issue_type)
|
|
855
|
+
logger.info("%i %s(s) retrieved from Jira.", len(jira_issues), output_str.lower())
|
|
856
|
+
return jira_issues
|
|
857
|
+
except Exception as e:
|
|
858
|
+
logger.warning(
|
|
859
|
+
"An error occurred while fetching Jira issues using the enhanced_search_issues method: %s", str(e)
|
|
860
|
+
)
|
|
861
|
+
logger.info("Falling back to the deprecated fetch method...")
|
|
862
|
+
|
|
863
|
+
try:
|
|
864
|
+
return deprecated_fetch_jira_objects(
|
|
865
|
+
jira_client=jira_client,
|
|
866
|
+
jira_project=jira_project,
|
|
867
|
+
jira_issue_type=jira_issue_type,
|
|
868
|
+
jql_str=jql_str,
|
|
869
|
+
output_str=output_str,
|
|
870
|
+
)
|
|
871
|
+
except JIRAError as e:
|
|
872
|
+
error_and_exit(f"Unable to fetch issues from Jira: {e}")
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
def deprecated_fetch_jira_objects(
|
|
876
|
+
jira_client: JIRA, jira_project: str, jira_issue_type: str, jql_str: str = None, output_str: str = "issue"
|
|
877
|
+
) -> list[jiraIssue]:
|
|
878
|
+
"""
|
|
879
|
+
Fetch all issues from Jira for the provided project using the old API method, used as a fallback method.
|
|
880
|
+
|
|
881
|
+
:param JIRA jira_client: Jira client to use for the request
|
|
882
|
+
:param str jira_project: Name of the project in Jira
|
|
883
|
+
:param str jira_issue_type: Type of issue to fetch from Jira
|
|
884
|
+
:param str jql_str: JQL string to use for the request, default None
|
|
885
|
+
:param str output_str: String to use for logging, either "issue" or "task"
|
|
886
|
+
:return: List of Jira issues
|
|
887
|
+
:rtype: list[jiraIssue]
|
|
888
|
+
"""
|
|
889
|
+
start_pointer = 0
|
|
890
|
+
page_size = 100
|
|
891
|
+
jira_objects = []
|
|
892
|
+
logger.info("Fetching %s(s) from Jira...", output_str.lower())
|
|
834
893
|
# get all issues for the Jira project
|
|
835
894
|
while True:
|
|
836
895
|
start = start_pointer * page_size
|
|
@@ -851,24 +910,36 @@ def fetch_jira_objects(
|
|
|
851
910
|
output_str.lower(),
|
|
852
911
|
)
|
|
853
912
|
if jira_objects:
|
|
854
|
-
|
|
855
|
-
file_name = f"{jira_project.lower()}_existingJira{jira_issue_type}.json"
|
|
856
|
-
file_path = Path(f"./artifacts/{file_name}")
|
|
857
|
-
save_data_to(
|
|
858
|
-
file=file_path,
|
|
859
|
-
data=[issue.raw for issue in jira_objects],
|
|
860
|
-
output_log=False,
|
|
861
|
-
)
|
|
862
|
-
logger.info(
|
|
863
|
-
"Saved %i Jira %s(s), see %s",
|
|
864
|
-
len(jira_objects),
|
|
865
|
-
jira_issue_type.lower(),
|
|
866
|
-
str(file_path.absolute()),
|
|
867
|
-
)
|
|
913
|
+
save_jira_issues(jira_objects, jira_project, jira_issue_type)
|
|
868
914
|
logger.info("%i %s(s) retrieved from Jira.", len(jira_objects), output_str.lower())
|
|
869
915
|
return jira_objects
|
|
870
916
|
|
|
871
917
|
|
|
918
|
+
def save_jira_issues(jira_issues: list[jiraIssue], jira_project: str, jira_issue_type: str) -> None:
|
|
919
|
+
"""
|
|
920
|
+
Save Jira issues to a JSON file in the artifacts directory
|
|
921
|
+
|
|
922
|
+
:param list[jiraIssue] jira_issues: List of Jira issues to save
|
|
923
|
+
:param str jira_project: Name of the project in Jira
|
|
924
|
+
:param str jira_issue_type: Type of issue to fetch from Jira
|
|
925
|
+
:rtype: None
|
|
926
|
+
"""
|
|
927
|
+
check_file_path("artifacts")
|
|
928
|
+
file_name = f"{jira_project.lower()}_existingJira{jira_issue_type}.json"
|
|
929
|
+
file_path = Path(f"./artifacts/{file_name}")
|
|
930
|
+
save_data_to(
|
|
931
|
+
file=file_path,
|
|
932
|
+
data=[issue.raw for issue in jira_issues],
|
|
933
|
+
output_log=False,
|
|
934
|
+
)
|
|
935
|
+
logger.info(
|
|
936
|
+
"Saved %i Jira %s(s), see %s",
|
|
937
|
+
len(jira_issues),
|
|
938
|
+
jira_issue_type.lower(),
|
|
939
|
+
str(file_path.absolute()),
|
|
940
|
+
)
|
|
941
|
+
|
|
942
|
+
|
|
872
943
|
def map_jira_to_regscale_issue(jira_issue: jiraIssue, config: dict, parent_id: int, parent_module: str) -> Issue:
|
|
873
944
|
"""
|
|
874
945
|
Map Jira issues to RegScale issues
|
|
@@ -893,6 +964,8 @@ def map_jira_to_regscale_issue(jira_issue: jiraIssue, config: dict, parent_id: i
|
|
|
893
964
|
),
|
|
894
965
|
status=("Closed" if jira_issue.fields.status.name.lower() == "done" else config["issues"]["jira"]["status"]),
|
|
895
966
|
jiraId=jira_issue.key,
|
|
967
|
+
identification="Jira Sync",
|
|
968
|
+
sourceReport="Jira",
|
|
896
969
|
parentId=parent_id,
|
|
897
970
|
parentModule=parent_module,
|
|
898
971
|
dateCreated=get_current_datetime(),
|
|
@@ -44,7 +44,7 @@ def sarif():
|
|
|
44
44
|
type=click.DateTime(formats=["%Y-%m-%d"]),
|
|
45
45
|
help="The scan date of the file.",
|
|
46
46
|
required=False,
|
|
47
|
-
default=get_current_datetime(),
|
|
47
|
+
default=get_current_datetime("%Y-%m-%d"),
|
|
48
48
|
)
|
|
49
49
|
def import_sarif(file_path: Path, asset_id: int, scan_date: Optional[datetime.datetime] = None) -> None:
|
|
50
50
|
"""Convert a SARIF file(s) to OCSF format using an API converter."""
|
|
@@ -154,7 +154,7 @@ def issues(
|
|
|
154
154
|
scanner = WizIssue(plan_id=regscale_ssp_id)
|
|
155
155
|
scanner.sync_findings(
|
|
156
156
|
plan_id=regscale_ssp_id,
|
|
157
|
-
filter_by_override=
|
|
157
|
+
filter_by_override=filter_by, # Pass the processed dict with project ID
|
|
158
158
|
client_id=client_id, # type: ignore
|
|
159
159
|
client_secret=client_secret, # type: ignore
|
|
160
160
|
wiz_project_id=wiz_project_id,
|
|
@@ -328,7 +328,11 @@ def add_report_evidence(
|
|
|
328
328
|
)
|
|
329
329
|
|
|
330
330
|
|
|
331
|
-
@wiz.command(
|
|
331
|
+
@wiz.command(
|
|
332
|
+
"sync_compliance",
|
|
333
|
+
deprecated=True,
|
|
334
|
+
help="[BETA] This command shows an experimental feature. Use with caution. Use compliance report instead for Compliance sync from Wiz.",
|
|
335
|
+
)
|
|
332
336
|
@click.option( # type: ignore
|
|
333
337
|
"--wiz_project_id",
|
|
334
338
|
"-p",
|
|
@@ -476,3 +480,129 @@ def sync_compliance(
|
|
|
476
480
|
create_issues=create_issues,
|
|
477
481
|
update_control_status=update_control_status,
|
|
478
482
|
)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@wiz.command(name="compliance_report")
|
|
486
|
+
@click.option(
|
|
487
|
+
"--wiz_project_id",
|
|
488
|
+
"-p",
|
|
489
|
+
prompt="Enter the Wiz project ID",
|
|
490
|
+
help="Enter the Wiz Project ID for compliance report processing.",
|
|
491
|
+
required=True,
|
|
492
|
+
)
|
|
493
|
+
@regscale_id(help="RegScale will create and update control assessments as children of this record.")
|
|
494
|
+
@regscale_module(required=True, default="securityplans", prompt=False)
|
|
495
|
+
@click.option(
|
|
496
|
+
"--client_id",
|
|
497
|
+
"-i",
|
|
498
|
+
help="Wiz Client ID, or can be set as environment variable wizClientId",
|
|
499
|
+
default="",
|
|
500
|
+
hide_input=False,
|
|
501
|
+
required=False,
|
|
502
|
+
)
|
|
503
|
+
@click.option(
|
|
504
|
+
"--client_secret",
|
|
505
|
+
"-s",
|
|
506
|
+
help="Wiz Client Secret, or can be set as environment variable wizClientSecret",
|
|
507
|
+
default="",
|
|
508
|
+
hide_input=True,
|
|
509
|
+
required=False,
|
|
510
|
+
)
|
|
511
|
+
@click.option(
|
|
512
|
+
"--report_file_path",
|
|
513
|
+
"-f",
|
|
514
|
+
help="Path to existing CSV compliance report file (optional - will create new report if not provided)",
|
|
515
|
+
default=None,
|
|
516
|
+
required=False,
|
|
517
|
+
)
|
|
518
|
+
@click.option(
|
|
519
|
+
"--create-issues/--no-create-issues",
|
|
520
|
+
"-ci/-ni",
|
|
521
|
+
default=True,
|
|
522
|
+
help="Create issues for failed compliance assessments (default: enabled)",
|
|
523
|
+
)
|
|
524
|
+
@click.option(
|
|
525
|
+
"--update-control-status/--no-update-control-status",
|
|
526
|
+
"-ucs/-nucs",
|
|
527
|
+
default=True,
|
|
528
|
+
help="Update control implementation status based on assessment results (default: enabled)",
|
|
529
|
+
)
|
|
530
|
+
@click.option(
|
|
531
|
+
"--create-poams/--no-create-poams",
|
|
532
|
+
"-cp/-ncp",
|
|
533
|
+
default=False,
|
|
534
|
+
help="Mark created issues as POAMs (default: disabled)",
|
|
535
|
+
)
|
|
536
|
+
@click.option(
|
|
537
|
+
"--reuse-existing-reports/--no-reuse-existing-reports",
|
|
538
|
+
"-rer/-nrer",
|
|
539
|
+
default=True,
|
|
540
|
+
help="Reuse existing Wiz compliance reports instead of creating new ones (default: enabled)",
|
|
541
|
+
)
|
|
542
|
+
@click.option(
|
|
543
|
+
"--force-fresh-report/--no-force-fresh-report",
|
|
544
|
+
"-ffr/-nffr",
|
|
545
|
+
default=False,
|
|
546
|
+
help="Force creation of a fresh compliance report, ignoring existing reports (default: disabled)",
|
|
547
|
+
)
|
|
548
|
+
def compliance_report(
|
|
549
|
+
wiz_project_id,
|
|
550
|
+
regscale_id,
|
|
551
|
+
regscale_module,
|
|
552
|
+
client_id,
|
|
553
|
+
client_secret,
|
|
554
|
+
report_file_path,
|
|
555
|
+
create_issues,
|
|
556
|
+
update_control_status,
|
|
557
|
+
create_poams,
|
|
558
|
+
reuse_existing_reports,
|
|
559
|
+
force_fresh_report,
|
|
560
|
+
):
|
|
561
|
+
"""
|
|
562
|
+
Process Wiz compliance reports and create assessments in RegScale.
|
|
563
|
+
|
|
564
|
+
This command can either:
|
|
565
|
+
1. Create a new compliance report from Wiz and process it
|
|
566
|
+
2. Process an existing compliance report CSV file
|
|
567
|
+
|
|
568
|
+
The command will:
|
|
569
|
+
- Parse compliance assessment data from CSV format
|
|
570
|
+
- Create control assessments based on compliance results
|
|
571
|
+
- Create issues for failed compliance assessments (if --create-issues enabled)
|
|
572
|
+
- Update control implementation status (if --update-control-status enabled)
|
|
573
|
+
- Support POAM creation for compliance issues
|
|
574
|
+
|
|
575
|
+
REPORT MANAGEMENT:
|
|
576
|
+
By default, the command will look for existing compliance reports in Wiz for the
|
|
577
|
+
specified project and rerun them instead of creating new ones. This prevents the
|
|
578
|
+
accumulation of duplicate reports in Wiz. Use --no-reuse-existing-reports to
|
|
579
|
+
always create new reports, or --force-fresh-report to force a new report even
|
|
580
|
+
when reuse is enabled.
|
|
581
|
+
"""
|
|
582
|
+
from regscale.integrations.commercial.wizv2.compliance_report import WizComplianceReportProcessor
|
|
583
|
+
|
|
584
|
+
# Use environment variables if not provided
|
|
585
|
+
if not client_secret:
|
|
586
|
+
client_secret = WizVariables.wizClientSecret
|
|
587
|
+
if not client_id:
|
|
588
|
+
client_id = WizVariables.wizClientId
|
|
589
|
+
|
|
590
|
+
# Create and run the compliance report processor
|
|
591
|
+
# Enable bypass_control_filtering by default for performance with large control sets
|
|
592
|
+
processor = WizComplianceReportProcessor(
|
|
593
|
+
plan_id=regscale_id,
|
|
594
|
+
wiz_project_id=wiz_project_id,
|
|
595
|
+
client_id=client_id,
|
|
596
|
+
client_secret=client_secret,
|
|
597
|
+
regscale_module=regscale_module,
|
|
598
|
+
create_poams=create_poams,
|
|
599
|
+
create_issues=create_issues,
|
|
600
|
+
update_control_status=update_control_status,
|
|
601
|
+
report_file_path=report_file_path,
|
|
602
|
+
bypass_control_filtering=True, # Bypass filtering for performance with large control sets
|
|
603
|
+
reuse_existing_reports=reuse_existing_reports,
|
|
604
|
+
force_fresh_report=force_fresh_report,
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
# Process the compliance report using new ComplianceIntegration pattern
|
|
608
|
+
processor.process_compliance_sync()
|