regscale-cli 6.18.0.0__py3-none-any.whl → 6.19.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.

Files changed (47) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/integrations/api_paginator.py +932 -0
  3. regscale/integrations/api_paginator_example.py +348 -0
  4. regscale/integrations/commercial/__init__.py +11 -10
  5. regscale/integrations/commercial/{qualys.py → qualys/__init__.py} +756 -105
  6. regscale/integrations/commercial/qualys/scanner.py +1051 -0
  7. regscale/integrations/commercial/qualys/variables.py +21 -0
  8. regscale/integrations/commercial/sicura/api.py +1 -0
  9. regscale/integrations/commercial/stigv2/click_commands.py +36 -8
  10. regscale/integrations/commercial/stigv2/stig_integration.py +63 -9
  11. regscale/integrations/commercial/tenablev2/__init__.py +9 -0
  12. regscale/integrations/commercial/tenablev2/authenticate.py +23 -2
  13. regscale/integrations/commercial/tenablev2/commands.py +779 -0
  14. regscale/integrations/commercial/tenablev2/jsonl_scanner.py +1999 -0
  15. regscale/integrations/commercial/tenablev2/sc_scanner.py +600 -0
  16. regscale/integrations/commercial/tenablev2/scanner.py +7 -5
  17. regscale/integrations/commercial/tenablev2/utils.py +21 -4
  18. regscale/integrations/commercial/tenablev2/variables.py +4 -0
  19. regscale/integrations/jsonl_scanner_integration.py +523 -142
  20. regscale/integrations/scanner_integration.py +102 -26
  21. regscale/integrations/transformer/__init__.py +17 -0
  22. regscale/integrations/transformer/data_transformer.py +445 -0
  23. regscale/integrations/transformer/mappings/__init__.py +8 -0
  24. regscale/integrations/variables.py +2 -0
  25. regscale/models/__init__.py +5 -2
  26. regscale/models/integration_models/cisa_kev_data.json +6 -6
  27. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  28. regscale/models/regscale_models/asset.py +5 -2
  29. regscale/models/regscale_models/file.py +5 -2
  30. regscale/models/regscale_models/group.py +2 -1
  31. regscale/models/regscale_models/user_group.py +1 -1
  32. regscale/regscale.py +3 -1
  33. {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/METADATA +1 -1
  34. {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/RECORD +46 -30
  35. tests/regscale/core/test_version.py +22 -0
  36. tests/regscale/integrations/__init__.py +0 -0
  37. tests/regscale/integrations/test_api_paginator.py +597 -0
  38. tests/regscale/integrations/test_integration_mapping.py +60 -0
  39. tests/regscale/integrations/test_issue_creation.py +317 -0
  40. tests/regscale/integrations/test_issue_due_date.py +46 -0
  41. tests/regscale/integrations/transformer/__init__.py +0 -0
  42. tests/regscale/integrations/transformer/test_data_transformer.py +850 -0
  43. regscale/integrations/commercial/tenablev2/click.py +0 -1641
  44. {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/LICENSE +0 -0
  45. {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/WHEEL +0 -0
  46. {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/entry_points.txt +0 -0
  47. {regscale_cli-6.18.0.0.dist-info → regscale_cli-6.19.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """Qualys Integration Variables"""
4
+
5
+ from regscale.integrations.variables import RsVariablesMeta, RsVariableType
6
+
7
+
8
+ class QualysVariables(metaclass=RsVariablesMeta):
9
+ """
10
+ Qualys Variables class to define class-level attributes with type annotations and examples
11
+ """
12
+
13
+ # API Connection Settings
14
+ qualysUserName: RsVariableType(str, "qualys_username") # type: ignore
15
+ qualysPassword: RsVariableType(str, "qualys_password", sensitive=True) # type: ignore
16
+ qualysUrl: RsVariableType(str, "https://qualysapi.qualys.com", default="https://qualysapi.qualys.com") # type: ignore
17
+
18
+ # Total Cloud Specific Settings
19
+ totalCloudTagFilter: RsVariableType(str, "tag_name", required=False) # type: ignore
20
+ totalCloudIncludeTags: RsVariableType(str, "tag1,tag2,tag3", required=False) # type: ignore
21
+ totalCloudExcludeTags: RsVariableType(str, "tag1,tag2,tag3", required=False) # type: ignore
@@ -203,6 +203,7 @@ class SicuraAPI:
203
203
  FILTER_TYPE = "filter[type]"
204
204
  FILTER_REJECTED = "filter[rejected]"
205
205
  FILTER_TASK_ID = "filter[task_id]"
206
+ csrf_token: Optional[str] = None
206
207
 
207
208
  def __init__(self):
208
209
  """
@@ -7,6 +7,7 @@ RegScale STIG Integration
7
7
  import click
8
8
 
9
9
  from regscale.integrations.commercial.stigv2.stig_integration import StigIntegration
10
+ from regscale.models.app_models.click import NotRequiredIf
10
11
 
11
12
 
12
13
  @click.group(name="stigv2")
@@ -21,7 +22,17 @@ def stigv2():
21
22
  type=click.INT,
22
23
  help="The ID number from RegScale of the System Security Plan",
23
24
  prompt="Enter RegScale System Security Plan ID",
24
- required=True,
25
+ cls=NotRequiredIf,
26
+ not_required_if=["component_id"],
27
+ )
28
+ @click.option(
29
+ "-c",
30
+ "--component_id",
31
+ type=click.INT,
32
+ help="The ID number from RegScale of the Component",
33
+ prompt="Enter RegScale Component ID",
34
+ cls=NotRequiredIf,
35
+ not_required_if=["regscale_ssp_id"],
25
36
  )
26
37
  @click.option(
27
38
  "-d",
@@ -31,9 +42,12 @@ def stigv2():
31
42
  prompt="Enter STIG directory",
32
43
  required=True,
33
44
  )
34
- def sync_findings(regscale_ssp_id, stig_directory):
45
+ def sync_findings(regscale_ssp_id, component_id, stig_directory):
35
46
  """Sync GCP Findings to RegScale."""
36
- StigIntegration.sync_findings(plan_id=regscale_ssp_id, path=stig_directory)
47
+ if component_id:
48
+ StigIntegration.sync_findings(plan_id=component_id, path=stig_directory, is_component=True)
49
+ else:
50
+ StigIntegration.sync_findings(plan_id=regscale_ssp_id, path=stig_directory, is_component=False)
37
51
 
38
52
 
39
53
  @stigv2.command(name="sync_assets")
@@ -41,9 +55,18 @@ def sync_findings(regscale_ssp_id, stig_directory):
41
55
  "-p",
42
56
  "--regscale_ssp_id",
43
57
  type=click.INT,
44
- help="The ID number from RegScale of the System Security Plan",
45
- prompt="Enter RegScale System Security Plan ID",
46
- required=True,
58
+ help="The ID number from RegScale of the System Security Plan to sync assets to.",
59
+ cls=NotRequiredIf,
60
+ not_required_if=["component_id"],
61
+ )
62
+ @click.option(
63
+ "-c",
64
+ "--component_id",
65
+ type=click.INT,
66
+ help="The ID number from RegScale of the Component to sync assets to.",
67
+ cls=NotRequiredIf,
68
+ not_required_if=["regscale_ssp_id"],
69
+ default=None,
47
70
  )
48
71
  @click.option(
49
72
  "-d",
@@ -53,9 +76,14 @@ def sync_findings(regscale_ssp_id, stig_directory):
53
76
  prompt="Enter STIG directory",
54
77
  required=True,
55
78
  )
56
- def sync_assets(regscale_ssp_id, stig_directory):
79
+ def sync_assets(regscale_ssp_id, component_id, stig_directory):
57
80
  """Sync GCP Assets to RegScale."""
58
- StigIntegration.sync_assets(plan_id=regscale_ssp_id, path=stig_directory)
81
+ if component_id:
82
+ StigIntegration.sync_assets(plan_id=component_id, path=stig_directory, is_component=True)
83
+ elif regscale_ssp_id:
84
+ StigIntegration.sync_assets(plan_id=regscale_ssp_id, path=stig_directory, is_component=False)
85
+ else:
86
+ raise click.UsageError("Either --regscale_ssp_id or --component_id must be provided.")
59
87
 
60
88
 
61
89
  @stigv2.command(name="process_checklist")
@@ -4,7 +4,13 @@
4
4
  RegScale STIG Integration
5
5
  """
6
6
  import datetime
7
- from typing import Iterator, Optional, Generator
7
+ import logging
8
+ from typing import Iterator, Optional, Generator, TYPE_CHECKING
9
+
10
+ from regscale.core.app.utils.app_utils import error_and_exit
11
+
12
+ if TYPE_CHECKING:
13
+ from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
8
14
 
9
15
  from pathlib import Path
10
16
 
@@ -24,6 +30,9 @@ from regscale.integrations.scanner_integration import (
24
30
  from regscale.models import regscale_models
25
31
 
26
32
 
33
+ logger = logging.getLogger("regscale")
34
+
35
+
27
36
  class StigIntegration(ScannerIntegration):
28
37
  options_map_assets_to_components = True
29
38
 
@@ -59,10 +68,10 @@ class StigIntegration(ScannerIntegration):
59
68
  logger.info(f"Processing '{stig_file}'")
60
69
  checklist = parse_checklist(stig_file)
61
70
  logger.info(f"Found {len(checklist.stigs[0].vulns)} Vulnerabilities in '{stig_file}'")
71
+ self.num_findings_to_process += len(checklist.stigs[0].vulns)
62
72
  for stig in checklist.stigs:
63
73
  for vuln in stig.vulns:
64
74
  for finding in self.process_vulnerabilities(checklist, vuln, stig):
65
- self.num_findings_to_process += 1
66
75
  yield finding
67
76
 
68
77
  def process_vulnerabilities(
@@ -149,7 +158,44 @@ class StigIntegration(ScannerIntegration):
149
158
  impact=vuln.potential_impact or "",
150
159
  )
151
160
 
152
- def fetch_assets(self, path: Optional[Path] = None) -> Iterator["IntegrationAsset"]:
161
+ @staticmethod
162
+ def get_component_names(checklist: Checklist, component_title: Optional[str] = None) -> list[str]:
163
+ """
164
+ Extracts component names from the checklist and STIG.
165
+
166
+ :param Checklist checklist: The checklist containing assets to process.
167
+ :param Optional[str] component_title: The title of the component to filter by, defaults to None
168
+ :return: A list of component names associated with the STIG.
169
+ :rtype: list[str]
170
+ """
171
+ component_names = []
172
+ if component_title:
173
+ component_names.append(component_title)
174
+ else:
175
+ for stig in checklist.stigs:
176
+ component_names.append(stig.component_title)
177
+ return component_names
178
+
179
+ def should_process_asset(
180
+ self, stig_asset: regscale_models.Asset, is_component: bool, component_names: Optional[list[str]] = None
181
+ ) -> bool:
182
+ """
183
+ Determines if an asset should be processed based on the provided parameters.
184
+
185
+ :param Asset stig_asset: The asset to be processed.
186
+ :param bool is_component: Whether the asset is a component.
187
+ :param Optional[list[str]] component_names: The list of component names to filter by, defaults to None
188
+ :return: True if the asset should be processed, False otherwise.
189
+ :rtype: bool
190
+ """
191
+ if not stig_asset.host_name and not stig_asset.host_fqdn:
192
+ self.log_error(f"Failed to extract asset from {stig_asset}")
193
+ return False
194
+ elif is_component and not component_names:
195
+ return False
196
+ return True
197
+
198
+ def fetch_assets(self, path: Optional[Path] = None, **kwargs) -> Iterator["IntegrationAsset"]:
153
199
  """
154
200
  Fetches GCP assets using the AssetServiceClient
155
201
 
@@ -166,7 +212,14 @@ class StigIntegration(ScannerIntegration):
166
212
  raise ValueError("Path to STIG files is required.")
167
213
  logger.info("Fetching assets...")
168
214
  stig_files = find_stig_files(path)
215
+ component_title = ""
216
+ if is_component := kwargs.get("is_component", False):
217
+ from regscale.validation.record import validate_regscale_object
169
218
 
219
+ if not validate_regscale_object(parent_id=self.plan_id, parent_module="components"):
220
+ raise error_and_exit("The provided Component ID is not valid.")
221
+ component = regscale_models.Component.get_object(self.plan_id)
222
+ component_title = component.title
170
223
  self.num_assets_to_process = len(stig_files)
171
224
 
172
225
  loading_stig_files = self.asset_progress.add_task(
@@ -177,12 +230,9 @@ class StigIntegration(ScannerIntegration):
177
230
  logger.info(f"Processing '{stig_file}'")
178
231
  checklist = parse_checklist(stig_file)
179
232
  for stig_asset in checklist.assets:
180
- component_names = []
181
- for stig in checklist.stigs:
182
- component_names.append(stig.component_title)
233
+ component_names = self.get_component_names(checklist, component_title)
183
234
 
184
- if not stig_asset.host_name and not stig_asset.host_fqdn:
185
- self.log_error(f"Failed to extract asset from {stig_asset}")
235
+ if not self.should_process_asset(stig_asset, is_component, component_names):
186
236
  continue
187
237
 
188
238
  yield IntegrationAsset(
@@ -193,7 +243,11 @@ class StigIntegration(ScannerIntegration):
193
243
  asset_type=stig_asset.asset_type,
194
244
  asset_owner_id=self.assessor_id,
195
245
  parent_id=self.plan_id,
196
- parent_module=regscale_models.SecurityPlan.get_module_slug(),
246
+ parent_module=(
247
+ regscale_models.Component.get_module_slug()
248
+ if kwargs.get("is_component", False)
249
+ else regscale_models.SecurityPlan.get_module_slug()
250
+ ),
197
251
  asset_category=regscale_models.AssetCategory.Hardware,
198
252
  component_names=component_names,
199
253
  # TODO: Determine correct component type
@@ -0,0 +1,9 @@
1
+ """
2
+ Tenable integration for RegScale CLI.
3
+
4
+ This module provides functionality for scanning assets and findings from Tenable.io and Tenable SC.
5
+ """
6
+
7
+ from regscale.integrations.commercial.tenablev2.commands import tenable, sync_vulns, sync_jsonl
8
+
9
+ __all__ = ["tenable", "sync_vulns", "sync_jsonl"]
@@ -6,6 +6,7 @@ from regscale.integrations.commercial.tenablev2.variables import TenableVariable
6
6
  # Delay import of Tenable libraries
7
7
  if TYPE_CHECKING:
8
8
  from tenable.io import TenableIO # type: ignore
9
+ from tenable.sc import TenableSC # type: ignore
9
10
 
10
11
  REGSCALE_INC = "RegScale, Inc."
11
12
  REGSCALE_CLI = "RegScale CLI"
@@ -13,9 +14,9 @@ REGSCALE_CLI = "RegScale CLI"
13
14
 
14
15
  def gen_tio() -> "TenableIO":
15
16
  """
16
- Generate Tenable Object
17
+ Generate Tenable IO Object
17
18
 
18
- :return: Tenable client
19
+ :return: Tenable IO client
19
20
  :rtype: "TenableIO"
20
21
  """
21
22
 
@@ -29,3 +30,23 @@ def gen_tio() -> "TenableIO":
29
30
  product=REGSCALE_CLI,
30
31
  build=__version__,
31
32
  )
33
+
34
+
35
+ def gen_tsc() -> "TenableSC":
36
+ """
37
+ Generate Tenable SC Object
38
+
39
+ :return: Tenable SC client
40
+ :rtype: "TenableSC"
41
+ """
42
+
43
+ from tenable.sc import TenableSC
44
+
45
+ return TenableSC(
46
+ url=TenableVariables.tenableUrl,
47
+ access_key=TenableVariables.tenableAccessKey,
48
+ secret_key=TenableVariables.tenableSecretKey,
49
+ vendor=REGSCALE_INC,
50
+ product=REGSCALE_CLI,
51
+ build=__version__,
52
+ )