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.

Files changed (43) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/api.py +8 -1
  3. regscale/core/app/application.py +49 -3
  4. regscale/core/utils/date.py +16 -16
  5. regscale/integrations/commercial/aqua/aqua.py +1 -1
  6. regscale/integrations/commercial/aws/cli.py +1 -1
  7. regscale/integrations/commercial/defender.py +1 -1
  8. regscale/integrations/commercial/ecr.py +1 -1
  9. regscale/integrations/commercial/ibm.py +1 -1
  10. regscale/integrations/commercial/nexpose.py +1 -1
  11. regscale/integrations/commercial/prisma.py +1 -1
  12. regscale/integrations/commercial/qualys/__init__.py +157 -84
  13. regscale/integrations/commercial/qualys/containers.py +2 -1
  14. regscale/integrations/commercial/qualys/scanner.py +5 -3
  15. regscale/integrations/commercial/snyk.py +14 -4
  16. regscale/integrations/commercial/synqly/ticketing.py +23 -11
  17. regscale/integrations/commercial/veracode.py +15 -4
  18. regscale/integrations/commercial/xray.py +1 -1
  19. regscale/integrations/public/cisa.py +7 -1
  20. regscale/integrations/public/nist_catalog.py +8 -2
  21. regscale/integrations/scanner_integration.py +18 -36
  22. regscale/models/app_models/import_validater.py +5 -1
  23. regscale/models/app_models/mapping.py +3 -1
  24. regscale/models/integration_models/cisa_kev_data.json +139 -4
  25. regscale/models/integration_models/flat_file_importer/__init__.py +36 -22
  26. regscale/models/integration_models/qualys.py +24 -4
  27. regscale/models/integration_models/send_reminders.py +8 -2
  28. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  29. regscale/models/regscale_models/control_implementation.py +40 -0
  30. regscale/models/regscale_models/issue.py +7 -4
  31. regscale/models/regscale_models/parameter.py +3 -2
  32. regscale/models/regscale_models/ports_protocol.py +15 -5
  33. regscale/models/regscale_models/vulnerability.py +1 -1
  34. regscale/utils/graphql_client.py +3 -6
  35. regscale/utils/threading/threadhandler.py +12 -2
  36. {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/METADATA +13 -13
  37. {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/RECORD +43 -42
  38. tests/regscale/core/test_version_regscale.py +62 -0
  39. tests/regscale/test_init.py +2 -0
  40. {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/LICENSE +0 -0
  41. {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/WHEEL +0 -0
  42. {regscale_cli-6.20.6.0.dist-info → regscale_cli-6.20.8.0.dist-info}/entry_points.txt +0 -0
  43. {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, timedelta
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, Issue, Vulnerability
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
- if "parent_id" not in kwargs and "regscale_ssp_id" in kwargs:
134
- kwargs["parent_id"] = kwargs["regscale_ssp_id"]
135
- kwargs["parent_module"] = "securityplans"
136
- kwargs["plan_id"] = kwargs["regscale_ssp_id"]
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
- "regscale_ssp_id",
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.regscale_ssp_id or self.attributes.parent_id,
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.parent_id,
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.parent_id,
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=regscale_models.SecurityPlan.get_module_slug(),
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
- regscale_ssp_id: int,
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 regscale_ssp_id: The RegScale SSP ID
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
- if not validate_regscale_object(regscale_ssp_id, "securityplans"):
690
- app.logger.warning("SSP #%i is not a valid RegScale Security Plan.", regscale_ssp_id)
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
- regscale_ssp_id=regscale_ssp_id,
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 create_vuln(self, dat: Optional[dict] = None, **kwargs) -> None:
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
- :rtype: None
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=self.mapping.get_value(dat, "Last Detected"),
156
- firstSeen=self.mapping.get_value(dat, "First Detected"),
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
- api.pool_connections = config["maxThreads"]
558
- api.pool_maxsize = config["maxThreads"]
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)):