regscale-cli 6.20.6.0__py3-none-any.whl → 6.20.8.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/core/app/api.py +8 -1
- regscale/core/app/application.py +49 -3
- regscale/core/utils/date.py +16 -16
- regscale/integrations/commercial/aqua/aqua.py +1 -1
- regscale/integrations/commercial/aws/cli.py +1 -1
- regscale/integrations/commercial/defender.py +1 -1
- regscale/integrations/commercial/ecr.py +1 -1
- regscale/integrations/commercial/ibm.py +1 -1
- regscale/integrations/commercial/nexpose.py +1 -1
- regscale/integrations/commercial/prisma.py +1 -1
- regscale/integrations/commercial/qualys/__init__.py +157 -84
- regscale/integrations/commercial/qualys/containers.py +2 -1
- regscale/integrations/commercial/qualys/scanner.py +5 -3
- regscale/integrations/commercial/snyk.py +14 -4
- regscale/integrations/commercial/synqly/ticketing.py +23 -11
- regscale/integrations/commercial/veracode.py +15 -4
- regscale/integrations/commercial/xray.py +1 -1
- regscale/integrations/public/cisa.py +7 -1
- regscale/integrations/public/nist_catalog.py +8 -2
- regscale/integrations/scanner_integration.py +18 -36
- regscale/models/app_models/import_validater.py +5 -1
- regscale/models/app_models/mapping.py +3 -1
- regscale/models/integration_models/cisa_kev_data.json +139 -4
- regscale/models/integration_models/flat_file_importer/__init__.py +36 -22
- regscale/models/integration_models/qualys.py +24 -4
- regscale/models/integration_models/send_reminders.py +8 -2
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/control_implementation.py +40 -0
- regscale/models/regscale_models/issue.py +7 -4
- regscale/models/regscale_models/parameter.py +3 -2
- regscale/models/regscale_models/ports_protocol.py +15 -5
- regscale/models/regscale_models/vulnerability.py +1 -1
- regscale/utils/graphql_client.py +3 -6
- regscale/utils/threading/threadhandler.py +12 -2
- {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/METADATA +13 -13
- {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/RECORD +43 -42
- tests/regscale/core/test_version_regscale.py +62 -0
- tests/regscale/test_init.py +2 -0
- {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"""Container Scan Abstract"""
|
|
2
2
|
|
|
3
3
|
import ast
|
|
4
|
-
import csv
|
|
5
4
|
import json
|
|
6
5
|
import logging
|
|
7
6
|
import re
|
|
8
7
|
import shutil
|
|
9
8
|
from abc import ABC, abstractmethod
|
|
10
9
|
from collections import namedtuple
|
|
11
|
-
from datetime import datetime
|
|
10
|
+
from datetime import datetime
|
|
12
11
|
from os import PathLike
|
|
13
12
|
from typing import TYPE_CHECKING, Any, Callable, Generator, Iterator, List, Optional, Sequence, TextIO, Tuple, Union
|
|
14
13
|
|
|
@@ -18,7 +17,6 @@ if TYPE_CHECKING:
|
|
|
18
17
|
from pathlib import Path
|
|
19
18
|
|
|
20
19
|
import click
|
|
21
|
-
import requests
|
|
22
20
|
import xmltodict
|
|
23
21
|
from openpyxl.reader.excel import load_workbook
|
|
24
22
|
|
|
@@ -32,11 +30,10 @@ from regscale.core.app.utils.app_utils import (
|
|
|
32
30
|
get_current_datetime,
|
|
33
31
|
)
|
|
34
32
|
from regscale.core.app.utils.parser_utils import safe_datetime_str
|
|
35
|
-
from regscale.core.app.utils.report_utils import ReportGenerator
|
|
36
33
|
from regscale.integrations.scanner_integration import ScannerIntegration
|
|
37
34
|
from regscale.models import IssueStatus, Metadata, regscale_models
|
|
38
35
|
from regscale.models.app_models.mapping import Mapping
|
|
39
|
-
from regscale.models.regscale_models import Asset, File,
|
|
36
|
+
from regscale.models.regscale_models import Asset, File, Vulnerability
|
|
40
37
|
|
|
41
38
|
logger = logging.getLogger(__name__)
|
|
42
39
|
|
|
@@ -74,6 +71,7 @@ class FlatFileIntegration(ScannerIntegration):
|
|
|
74
71
|
"Low": regscale_models.IssueSeverity.Low,
|
|
75
72
|
}
|
|
76
73
|
super().__init__(plan_id=plan_id, **kwargs)
|
|
74
|
+
self.is_component = kwargs.get("is_component", False)
|
|
77
75
|
|
|
78
76
|
def set_asset_identifier_field(self, asset_identifier_field: str) -> None:
|
|
79
77
|
"""
|
|
@@ -130,10 +128,16 @@ class FlatFileImporter(ABC):
|
|
|
130
128
|
"Medium": regscale_models.IssueSeverity.Moderate,
|
|
131
129
|
"Low": regscale_models.IssueSeverity.Low,
|
|
132
130
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
kwargs["
|
|
131
|
+
|
|
132
|
+
# Set the parent_id, parent_module, and plan_id if they are not provided
|
|
133
|
+
if "parent_id" not in kwargs and "object_id" in kwargs:
|
|
134
|
+
kwargs["parent_id"] = kwargs["object_id"]
|
|
135
|
+
kwargs["plan_id"] = kwargs["object_id"]
|
|
136
|
+
if kwargs.get("is_component", False):
|
|
137
|
+
kwargs["parent_module"] = regscale_models.Component.get_module_string()
|
|
138
|
+
else:
|
|
139
|
+
kwargs["parent_module"] = regscale_models.SecurityPlan.get_module_string()
|
|
140
|
+
|
|
137
141
|
if "app" not in kwargs:
|
|
138
142
|
kwargs["app"] = Application()
|
|
139
143
|
# empty generator
|
|
@@ -156,7 +160,8 @@ class FlatFileImporter(ABC):
|
|
|
156
160
|
"mapping",
|
|
157
161
|
"ignore_validation",
|
|
158
162
|
"header_line_number",
|
|
159
|
-
"
|
|
163
|
+
"is_component",
|
|
164
|
+
"object_id",
|
|
160
165
|
"plan_id",
|
|
161
166
|
"disable_mapping",
|
|
162
167
|
"mappings_path",
|
|
@@ -188,7 +193,8 @@ class FlatFileImporter(ABC):
|
|
|
188
193
|
}
|
|
189
194
|
self.create_epoch = str(int(creation_date(self.attributes.file_path)))
|
|
190
195
|
flat_int = FlatFileIntegration(
|
|
191
|
-
plan_id=self.attributes.plan_id or self.attributes.
|
|
196
|
+
plan_id=self.attributes.plan_id or self.attributes.object_id or self.attributes.parent_id,
|
|
197
|
+
is_component=self.attributes.is_component,
|
|
192
198
|
asset_identifier_field=self.asset_identifier_field,
|
|
193
199
|
finding_severity_map=self.finding_severity_map,
|
|
194
200
|
)
|
|
@@ -206,13 +212,15 @@ class FlatFileImporter(ABC):
|
|
|
206
212
|
elif isinstance(self.data["vulns"], list):
|
|
207
213
|
flat_int.num_findings_to_process = len(self.data["vulns"])
|
|
208
214
|
flat_int.sync_assets(
|
|
209
|
-
plan_id=self.attributes.
|
|
215
|
+
plan_id=self.attributes.plan_id,
|
|
216
|
+
is_component=self.attributes.is_component,
|
|
210
217
|
integration_assets=self.integration_assets,
|
|
211
218
|
title=self.attributes.name,
|
|
212
219
|
asset_count=flat_int.num_assets_to_process,
|
|
213
220
|
)
|
|
214
221
|
flat_int.sync_findings(
|
|
215
|
-
plan_id=self.attributes.
|
|
222
|
+
plan_id=self.attributes.plan_id,
|
|
223
|
+
is_component=self.attributes.is_component,
|
|
216
224
|
integration_findings=self.integration_findings,
|
|
217
225
|
title=self.attributes.name,
|
|
218
226
|
finding_count=flat_int.num_findings_to_process,
|
|
@@ -319,7 +327,7 @@ class FlatFileImporter(ABC):
|
|
|
319
327
|
asset_type=asset.assetType,
|
|
320
328
|
asset_owner_id=asset.assetOwnerId,
|
|
321
329
|
parent_id=self.attributes.parent_id,
|
|
322
|
-
parent_module=
|
|
330
|
+
parent_module=self.attributes.parent_module,
|
|
323
331
|
asset_category=asset.assetCategory,
|
|
324
332
|
date_last_updated=asset.dateLastUpdated,
|
|
325
333
|
status=asset.status,
|
|
@@ -391,11 +399,10 @@ class FlatFileImporter(ABC):
|
|
|
391
399
|
# Skip lines until the start line is reached
|
|
392
400
|
for _ in range(start_line_number):
|
|
393
401
|
next(file)
|
|
394
|
-
if file.name.endswith(".csv"):
|
|
402
|
+
if file.name.endswith((".csv", ".xlsx")):
|
|
403
|
+
# Use the validater data for CSV and XLSX files to ensure proper mapping validation
|
|
395
404
|
data = self.validater.data.to_dict("records")
|
|
396
405
|
header = list(self.validater.parsed_headers)
|
|
397
|
-
elif file.name.endswith(".xlsx"):
|
|
398
|
-
data, header = self.convert_xlsx_to_dict(file, start_line_number)
|
|
399
406
|
elif file.name.endswith(".json"):
|
|
400
407
|
try:
|
|
401
408
|
# Filter possible null values
|
|
@@ -652,7 +659,7 @@ class FlatFileImporter(ABC):
|
|
|
652
659
|
import_name: str,
|
|
653
660
|
file_types: Union[str, list[str]],
|
|
654
661
|
folder_path: PathLike[str],
|
|
655
|
-
|
|
662
|
+
object_id: int,
|
|
656
663
|
scan_date: datetime,
|
|
657
664
|
mappings_path: Union[PathLike[str], Path],
|
|
658
665
|
disable_mapping: bool,
|
|
@@ -669,7 +676,7 @@ class FlatFileImporter(ABC):
|
|
|
669
676
|
:param str import_name: The name of the import type
|
|
670
677
|
:param Union[str, list[str]] file_types: The file types to glob and import, e.g. ".csv" or [".csv", ".xlsx"]
|
|
671
678
|
:param PathLike[str] folder_path: The folder path to import from
|
|
672
|
-
:param int
|
|
679
|
+
:param int object_id: The RegScale SSP ID
|
|
673
680
|
:param datetime scan_date: The date of the scan
|
|
674
681
|
:param Union[PathLike[str], Path] mappings_path: The path to the mappings file
|
|
675
682
|
:param bool disable_mapping: Whether to disable custom mappings
|
|
@@ -686,9 +693,16 @@ class FlatFileImporter(ABC):
|
|
|
686
693
|
if s3_bucket:
|
|
687
694
|
download_from_s3(s3_bucket, s3_prefix, folder_path, aws_profile)
|
|
688
695
|
app = Application()
|
|
689
|
-
|
|
690
|
-
|
|
696
|
+
|
|
697
|
+
# Validate the parent_id is a valid RegScale object
|
|
698
|
+
is_component = kwargs.get("is_component", False)
|
|
699
|
+
if is_component and not validate_regscale_object(object_id, "components"):
|
|
700
|
+
app.logger.warning("Component #%i is not a valid RegScale Component.", object_id)
|
|
701
|
+
return
|
|
702
|
+
elif not is_component and not validate_regscale_object(object_id, "securityplans"):
|
|
703
|
+
app.logger.warning("SSP #%i is not a valid RegScale Security Plan.", object_id)
|
|
691
704
|
return
|
|
705
|
+
|
|
692
706
|
if not scan_date or not FlatFileImporter.check_date_format(scan_date):
|
|
693
707
|
scan_date = datetime.now()
|
|
694
708
|
if isinstance(file_types, str):
|
|
@@ -706,7 +720,7 @@ class FlatFileImporter(ABC):
|
|
|
706
720
|
import_type(
|
|
707
721
|
name=import_name,
|
|
708
722
|
file_path=str(file),
|
|
709
|
-
|
|
723
|
+
object_id=object_id,
|
|
710
724
|
scan_date=scan_date,
|
|
711
725
|
mappings_path=mappings_path,
|
|
712
726
|
disable_mapping=disable_mapping,
|
|
@@ -7,10 +7,12 @@ import logging
|
|
|
7
7
|
|
|
8
8
|
# pylint: disable=C0415
|
|
9
9
|
import re
|
|
10
|
+
from calendar import firstweekday
|
|
10
11
|
from datetime import datetime
|
|
11
12
|
from typing import Any, Iterator, Optional, TypeVar, TextIO, Union
|
|
12
13
|
|
|
13
14
|
from openpyxl.reader.excel import load_workbook
|
|
15
|
+
from pandas import Timestamp
|
|
14
16
|
|
|
15
17
|
from regscale.core.app import create_logger
|
|
16
18
|
from regscale.core.app.application import Application
|
|
@@ -124,12 +126,27 @@ class Qualys(FlatFileImporter):
|
|
|
124
126
|
}
|
|
125
127
|
)
|
|
126
128
|
|
|
127
|
-
def
|
|
129
|
+
def _convert_datetime_to_str(self, input_date: Union[Timestamp, datetime]) -> str:
|
|
130
|
+
"""
|
|
131
|
+
Convert a datetime or Timestamp object to a string in the specified format.
|
|
132
|
+
|
|
133
|
+
:param Union[Timestamp, datetime] input_date: The date to convert.
|
|
134
|
+
:return: The date as a string in the format '%Y-%m-%d %H:%M:%S'.
|
|
135
|
+
:rtype: str
|
|
136
|
+
"""
|
|
137
|
+
if isinstance(input_date, Timestamp):
|
|
138
|
+
input_date = input_date.to_pydatetime()
|
|
139
|
+
elif isinstance(input_date, str):
|
|
140
|
+
return input_date
|
|
141
|
+
return input_date.strftime(self.dt_format)
|
|
142
|
+
|
|
143
|
+
def create_vuln(self, dat: Optional[dict] = None, **kwargs) -> Optional[Vulnerability]:
|
|
128
144
|
"""
|
|
129
145
|
Create a vuln from a row in the Qualys file
|
|
130
146
|
|
|
131
147
|
:param Optional[dict] dat: Data row from CSV file, defaults to None
|
|
132
|
-
:
|
|
148
|
+
:return: RegScale Vulnerability object or None
|
|
149
|
+
:rtype: Optional[Vulnerability]
|
|
133
150
|
"""
|
|
134
151
|
from regscale.integrations.commercial.qualys import map_qualys_severity_to_regscale
|
|
135
152
|
|
|
@@ -145,6 +162,9 @@ class Qualys(FlatFileImporter):
|
|
|
145
162
|
config = self.attributes.app.config
|
|
146
163
|
asset_match = [asset for asset in self.data["assets"] if asset.name == dns]
|
|
147
164
|
asset = asset_match[0] if asset_match else None
|
|
165
|
+
last_seen = self._convert_datetime_to_str(self.mapping.get_value(dat, "Last Detected"))
|
|
166
|
+
first_seen = self._convert_datetime_to_str(self.mapping.get_value(dat, "First Detected"))
|
|
167
|
+
|
|
148
168
|
if dat and asset_match:
|
|
149
169
|
regscale_vuln = Vulnerability(
|
|
150
170
|
id=0,
|
|
@@ -152,8 +172,8 @@ class Qualys(FlatFileImporter):
|
|
|
152
172
|
parentId=asset.id,
|
|
153
173
|
parentModule="assets",
|
|
154
174
|
ipAddress=self.mapping.get_value(dat, IP),
|
|
155
|
-
lastSeen=
|
|
156
|
-
firstSeen=
|
|
175
|
+
lastSeen=last_seen,
|
|
176
|
+
firstSeen=first_seen,
|
|
157
177
|
daysOpen=None,
|
|
158
178
|
dns=self.mapping.get_value(dat, DNS, other_id),
|
|
159
179
|
mitigated=None,
|
|
@@ -554,8 +554,14 @@ class SendReminders:
|
|
|
554
554
|
)
|
|
555
555
|
|
|
556
556
|
# update api pool limits to max_thread count from init.yaml
|
|
557
|
-
|
|
558
|
-
|
|
557
|
+
max_threads = config.get("maxThreads", 100)
|
|
558
|
+
if not isinstance(max_threads, int):
|
|
559
|
+
try:
|
|
560
|
+
max_threads = int(max_threads)
|
|
561
|
+
except (ValueError, TypeError):
|
|
562
|
+
max_threads = 100
|
|
563
|
+
api.pool_connections = max_threads
|
|
564
|
+
api.pool_maxsize = max_threads
|
|
559
565
|
|
|
560
566
|
# get assigned threads
|
|
561
567
|
for i in range(len(threads)):
|