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

Files changed (42) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/core/app/utils/variables.py +5 -3
  3. regscale/integrations/commercial/__init__.py +2 -0
  4. regscale/integrations/commercial/burp.py +14 -0
  5. regscale/integrations/commercial/grype/commands.py +8 -1
  6. regscale/integrations/commercial/grype/scanner.py +2 -1
  7. regscale/integrations/commercial/jira.py +290 -133
  8. regscale/integrations/commercial/opentext/commands.py +14 -5
  9. regscale/integrations/commercial/opentext/scanner.py +3 -2
  10. regscale/integrations/commercial/qualys/__init__.py +3 -3
  11. regscale/integrations/commercial/stigv2/click_commands.py +6 -37
  12. regscale/integrations/commercial/synqly/edr.py +8 -2
  13. regscale/integrations/commercial/synqly/ticketing.py +25 -0
  14. regscale/integrations/commercial/tenablev2/commands.py +12 -4
  15. regscale/integrations/commercial/tenablev2/sc_scanner.py +21 -1
  16. regscale/integrations/commercial/tenablev2/sync_compliance.py +3 -0
  17. regscale/integrations/commercial/trivy/commands.py +11 -4
  18. regscale/integrations/commercial/trivy/scanner.py +2 -1
  19. regscale/integrations/jsonl_scanner_integration.py +8 -1
  20. regscale/integrations/public/cisa.py +58 -63
  21. regscale/integrations/public/fedramp/fedramp_cis_crm.py +88 -93
  22. regscale/integrations/scanner_integration.py +22 -6
  23. regscale/models/app_models/click.py +49 -1
  24. regscale/models/integration_models/burp.py +11 -8
  25. regscale/models/integration_models/cisa_kev_data.json +146 -25
  26. regscale/models/integration_models/flat_file_importer/__init__.py +36 -176
  27. regscale/models/integration_models/jira_task_sync.py +27 -0
  28. regscale/models/integration_models/qualys.py +6 -7
  29. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  30. regscale/models/regscale_models/control_implementation.py +39 -2
  31. regscale/models/regscale_models/regscale_model.py +49 -1
  32. regscale/models/regscale_models/supply_chain.py +1 -1
  33. regscale/models/regscale_models/task.py +1 -0
  34. regscale/regscale.py +1 -4
  35. regscale/utils/string.py +13 -0
  36. {regscale_cli-6.20.1.0.dist-info → regscale_cli-6.20.2.0.dist-info}/METADATA +1 -1
  37. {regscale_cli-6.20.1.0.dist-info → regscale_cli-6.20.2.0.dist-info}/RECORD +41 -41
  38. regscale/integrations/commercial/synqly_jira.py +0 -840
  39. {regscale_cli-6.20.1.0.dist-info → regscale_cli-6.20.2.0.dist-info}/LICENSE +0 -0
  40. {regscale_cli-6.20.1.0.dist-info → regscale_cli-6.20.2.0.dist-info}/WHEEL +0 -0
  41. {regscale_cli-6.20.1.0.dist-info → regscale_cli-6.20.2.0.dist-info}/entry_points.txt +0 -0
  42. {regscale_cli-6.20.1.0.dist-info → regscale_cli-6.20.2.0.dist-info}/top_level.txt +0 -0
@@ -49,11 +49,14 @@ progress = create_progress_object()
49
49
 
50
50
  SERVICE_PROVIDER_CORPORATE = "Service Provider Corporate"
51
51
  SERVICE_PROVIDER_SYSTEM_SPECIFIC = "Service Provider System Specific"
52
- SERVICE_PROVIDER_HYBRID = "Service Provider Hybrid"
52
+ SERVICE_PROVIDER_HYBRID = "Service Provider Hybrid (Corporate and System Specific)"
53
53
  PROVIDER_SYSTEM_SPECIFIC = "Provider (System Specific)"
54
- CUSTOMER_PROVIDED = "Provided by Customer"
54
+ CUSTOMER_PROVIDED = "Customer Provided"
55
55
  CUSTOMER_CONFIGURED = "Customer Configured"
56
- CONFIGURED_BY_CUSTOMER = "Configured by Customer"
56
+ PROVIDED_BY_CUSTOMER = "Provided by Customer (Customer System Specific)"
57
+ CONFIGURED_BY_CUSTOMER = "Configured by Customer (Customer System Specific)"
58
+ INHERITED = "Inherited from pre-existing FedRAMP Authorization"
59
+ SHARED = "Shared (Service Provider and Customer Responsibility)"
57
60
  NOT_IMPLEMENTED = ControlImplementationStatus.NotImplemented.value
58
61
  PARTIALLY_IMPLEMENTED = ControlImplementationStatus.PartiallyImplemented.value
59
62
  CONTROL_ID = "Control ID"
@@ -77,10 +80,10 @@ STATUS_MAPPING = {
77
80
 
78
81
  RESPONSIBILITY_MAP = {
79
82
  # Original keys
80
- "SERVICE_PROVIDER_CORPORATE": "Provider",
83
+ SERVICE_PROVIDER_CORPORATE: SERVICE_PROVIDER_CORPORATE,
81
84
  SERVICE_PROVIDER_SYSTEM_SPECIFIC: PROVIDER_SYSTEM_SPECIFIC,
82
85
  SERVICE_PROVIDER_HYBRID: "Hybrid",
83
- CUSTOMER_PROVIDED: "Customer",
86
+ PROVIDED_BY_CUSTOMER: "Customer",
84
87
  CONFIGURED_BY_CUSTOMER: CUSTOMER_CONFIGURED,
85
88
  "Shared": "Shared",
86
89
  "Inherited": "Inherited",
@@ -241,52 +244,44 @@ def map_implementation_status(control_id: str, cis_data: dict) -> str:
241
244
 
242
245
  def map_origination(control_id: str, cis_data: dict) -> dict:
243
246
  """
244
- Function to map the responsibility for a control implementation from the CRM worksheet
245
-
246
- :param str control_id: RegScale control ID
247
- :param dict cis_data: Data from the CRM worksheet
248
- :return: The responsibility information in regscale format
249
- :rtype: dict
250
- """
251
- origination_bools = {
252
- "bInherited": False,
253
- "bServiceProviderCorporate": False,
254
- "bServiceProviderSystemSpecific": False,
255
- "bServiceProviderHybrid": False,
256
- "bConfiguredByCustomer": False,
257
- "bProvidedByCustomer": False,
258
- "bShared": False,
259
- "record_text": "",
247
+ Map control implementation responsibility from CRM worksheet data.
248
+
249
+ :param control_id: RegScale control ID
250
+ :param cis_data: Data from the CRM worksheet
251
+ :return: Responsibility information in regscale format
252
+ """
253
+ # Define mapping of origination strings to boolean keys
254
+ origination_mapping = {
255
+ SERVICE_PROVIDER_CORPORATE: "bServiceProviderCorporate",
256
+ SERVICE_PROVIDER_SYSTEM_SPECIFIC: "bServiceProviderSystemSpecific",
257
+ SERVICE_PROVIDER_HYBRID: "bServiceProviderHybrid",
258
+ PROVIDED_BY_CUSTOMER: "bProvidedByCustomer",
259
+ CONFIGURED_BY_CUSTOMER: "bConfiguredByCustomer",
260
+ SHARED: "bShared",
261
+ INHERITED: "bInherited",
260
262
  }
261
- cis_records = [
262
- value for _, value in cis_data.items() if gen_key(value["regscale_control_id"]).lower() == control_id.lower()
263
+
264
+ # Initialize result with all flags set to False
265
+ result = {key: False for key in origination_mapping.values()}
266
+ result["record_text"] = ""
267
+
268
+ # Find matching CIS records
269
+ matching_records = [
270
+ record for record in cis_data.values() if gen_key(record["regscale_control_id"]).lower() == control_id.lower()
263
271
  ]
264
- for record in cis_records:
265
- # Create the implementation objective, and save.
272
+
273
+ # Process each matching record
274
+ for record in matching_records:
266
275
  control_origination = record.get("control_origination", "")
267
- if SERVICE_PROVIDER_CORPORATE in control_origination:
268
- # responsibility = "Provider"
269
- origination_bools["bServiceProviderCorporate"] = True
270
- if SERVICE_PROVIDER_SYSTEM_SPECIFIC in control_origination:
271
- # responsibility = "Provider (System Specific)"
272
- origination_bools["bServiceProviderSystemSpecific"] = True
273
- if SERVICE_PROVIDER_HYBRID in control_origination:
274
- # responsibility = "Hybrid"
275
- origination_bools["bServiceProviderHybrid"] = True
276
- if CUSTOMER_PROVIDED in control_origination:
277
- # responsibility = "Customer"
278
- origination_bools["bProvidedByCustomer"] = True
279
- if CONFIGURED_BY_CUSTOMER in control_origination:
280
- # responsibility = "Customer Configured"
281
- origination_bools["bConfiguredByCustomer"] = True
282
- if "Shared" in control_origination:
283
- # responsibility = "Shared"
284
- origination_bools["bShared"] = True
285
- if "Inherited" in control_origination:
286
- # responsibility = "Inherited"
287
- origination_bools["bInherited"] = True
288
- origination_bools["record_text"] += control_origination
289
- return origination_bools
276
+
277
+ # Set flags based on origination string content
278
+ for origination_str, bool_key in origination_mapping.items():
279
+ if origination_str in control_origination:
280
+ result[bool_key] = True
281
+
282
+ result["record_text"] += control_origination
283
+
284
+ return result
290
285
 
291
286
 
292
287
  def clean_customer_responsibility(value: str):
@@ -331,16 +326,6 @@ def update_imp_objective(
331
326
  NOT_IMPLEMENTED: NOT_IMPLEMENTED,
332
327
  }
333
328
 
334
- responsibility_map = {
335
- "Provider": SERVICE_PROVIDER_CORPORATE,
336
- PROVIDER_SYSTEM_SPECIFIC: SERVICE_PROVIDER_SYSTEM_SPECIFIC,
337
- "Customer": "Provided by Customer (Customer System Specific)",
338
- "Hybrid": "Service Provider Hybrid (Corporate and System Specific)",
339
- CUSTOMER_CONFIGURED: "Configured by Customer (Customer System Specific)",
340
- "Shared": "Shared (Service Provider and Customer Responsibility)",
341
- "Inherited": "Inherited from pre-existing FedRAMP Authorization",
342
- }
343
-
344
329
  cis_record = record.get("cis", {})
345
330
  crm_record = record.get("crm", {})
346
331
  # There could be multiples, take the first one as regscale will not allow multiples at the objective level.
@@ -349,17 +334,18 @@ def update_imp_objective(
349
334
  control_originations[ix] = control_origination.strip()
350
335
 
351
336
  try:
352
- responsibility = RESPONSIBILITY_MAP.get(
353
- next(origin for origin in control_originations), SERVICE_PROVIDER_CORPORATE
354
- )
337
+ responsibility = next(origin for origin in control_originations)
338
+
355
339
  except StopIteration:
356
- responsibility = SERVICE_PROVIDER_CORPORATE
340
+ if imp.responsibility:
341
+ responsibility = imp.responsibility.split(",")[0] # only one responsiblity allowed here.
342
+ else:
343
+ responsibility = SERVICE_PROVIDER_CORPORATE
357
344
 
358
345
  customer_responsibility = clean_customer_responsibility(
359
346
  crm_record.get("specific_inheritance_and_customer_agency_csp_responsibilities")
360
347
  )
361
348
  existing_pairs = {(obj.objectiveId, obj.implementationId) for obj in existing_imp_obj}
362
- responsibility = responsibility_map.get(responsibility, responsibility)
363
349
  logger.debug(f"CRM Record: {crm_record}")
364
350
  can_be_inherited_from_csp: str = crm_record.get("can_be_inherited_from_csp") or ""
365
351
  for objective in objectives:
@@ -452,9 +438,14 @@ def parse_control_details(
452
438
  control_imp.bServiceProviderHybrid = origination_bool["bServiceProviderHybrid"]
453
439
  control_imp.bConfiguredByCustomer = origination_bool["bConfiguredByCustomer"]
454
440
  control_imp.bProvidedByCustomer = origination_bool["bProvidedByCustomer"]
455
- # NOTE Dale was concerned with overwriting the responsibility text, so we will only update if empty
456
- if not control_imp.responsibility:
457
- control_imp.responsibility = get_responsibility(origination_bool)
441
+ control_imp.responsibility = get_responsibility(origination_bool)
442
+ logger.debug(f"Control Implementation Responsibility: {control_imp.responsibility}")
443
+ logger.debug(f"Control Implementation Status: {control_imp.status}")
444
+ if status == ControlImplementationStatus.Planned:
445
+ control_imp.stepsToImplement = "PLANNED"
446
+ control_imp.plannedImplementationDate = get_current_datetime("%Y-%m-%d")
447
+ if status in [ControlImplementationStatus.Planned, ControlImplementationStatus.NotImplemented]:
448
+ control_imp.exclusionJustification = "Imported from FedRAMP CIS CRM Workbook"
458
449
  if updated_control := control_imp.save():
459
450
  logger.debug("Control Implementation #%s updated successfully", control_imp.id)
460
451
  return updated_control
@@ -465,29 +456,31 @@ def parse_control_details(
465
456
  def get_responsibility(origination_bool: dict) -> str:
466
457
  """
467
458
  Function to map the responsibility based on origination booleans.
459
+ Returns comma-separated string of all responsibilities for True booleans.
468
460
 
469
461
  :param dict origination_bool: Dictionary containing origination booleans
470
- :return: Responsibility string
462
+ :return: Comma-separated responsibility string
471
463
  :rtype: str
472
464
  """
473
- responsibility = ControlImplementationStatus.NA.value
465
+ responsibilities = []
474
466
 
475
- if origination_bool["bServiceProviderCorporate"]:
476
- responsibility = SERVICE_PROVIDER_CORPORATE
477
- if origination_bool["bServiceProviderSystemSpecific"]:
478
- responsibility = SERVICE_PROVIDER_SYSTEM_SPECIFIC
479
- if origination_bool["bServiceProviderHybrid"]:
480
- responsibility = "Service Provider Hybrid"
481
- if origination_bool["bProvidedByCustomer"]:
482
- responsibility = "Provided by Customer"
483
- if origination_bool["bConfiguredByCustomer"]:
484
- responsibility = "Configured by Customer (Customer System Specific)"
485
- if origination_bool["bInherited"]:
486
- responsibility = "Inherited from pre-existing FedRAMP Authorization"
487
- if origination_bool["bShared"]:
488
- responsibility = "Shared (Service Provider and Customer Responsibility)"
467
+ if origination_bool.get("bServiceProviderCorporate", False):
468
+ responsibilities.append(SERVICE_PROVIDER_CORPORATE)
469
+ if origination_bool.get("bServiceProviderSystemSpecific", False):
470
+ responsibilities.append(SERVICE_PROVIDER_SYSTEM_SPECIFIC)
471
+ if origination_bool.get("bServiceProviderHybrid", False):
472
+ responsibilities.append(SERVICE_PROVIDER_HYBRID)
473
+ if origination_bool.get("bProvidedByCustomer", False):
474
+ responsibilities.append(PROVIDED_BY_CUSTOMER)
475
+ if origination_bool.get("bConfiguredByCustomer", False):
476
+ responsibilities.append(CONFIGURED_BY_CUSTOMER)
477
+ if origination_bool.get("bInherited", False):
478
+ responsibilities.append(INHERITED)
479
+ if origination_bool.get("bShared", False):
480
+ responsibilities.append(SHARED)
489
481
 
490
- return responsibility
482
+ # Return comma-separated string, or NA if no responsibilities found
483
+ return ",".join(responsibilities) if responsibilities else ControlImplementationStatus.NA.value
491
484
 
492
485
 
493
486
  def fetch_and_update_imps(
@@ -706,6 +699,7 @@ def process_implementation(
706
699
  errors.extend(method_errors)
707
700
  if result:
708
701
  processed_objectives.append(result)
702
+ # Update Control Origin at the Implementation Level
709
703
  return errors, processed_objectives
710
704
 
711
705
 
@@ -930,9 +924,9 @@ def parse_cis_worksheet(file_path: click.Path, cis_sheet_name: str) -> dict:
930
924
  SERVICE_PROVIDER_SYSTEM_SPECIFIC,
931
925
  SERVICE_PROVIDER_HYBRID,
932
926
  CONFIGURED_BY_CUSTOMER,
933
- CUSTOMER_PROVIDED,
934
- "Shared Responsibility",
935
- "Inherited Authorization",
927
+ PROVIDED_BY_CUSTOMER,
928
+ SHARED,
929
+ INHERITED,
936
930
  ]
937
931
 
938
932
  # Fill NaN values with an empty string for processing
@@ -973,9 +967,9 @@ def parse_cis_worksheet(file_path: click.Path, cis_sheet_name: str) -> dict:
973
967
  SERVICE_PROVIDER_SYSTEM_SPECIFIC,
974
968
  SERVICE_PROVIDER_HYBRID,
975
969
  CONFIGURED_BY_CUSTOMER,
976
- CUSTOMER_PROVIDED,
977
- "Shared Responsibility",
978
- "Inherited Authorization",
970
+ PROVIDED_BY_CUSTOMER,
971
+ SHARED,
972
+ INHERITED,
979
973
  ]:
980
974
  if data_row[col]:
981
975
  selected_origination.append(col)
@@ -1102,15 +1096,15 @@ def parse_instructions_worksheet(
1102
1096
  return instructions_df.to_dict(orient="records")
1103
1097
 
1104
1098
 
1105
- def update_responsiblity_text():
1099
+ def update_customer_text():
1106
1100
  """
1107
1101
  Update the implementation responsibility texts from the objective data
1108
1102
  """
1109
1103
  with ThreadPoolExecutor() as executor:
1110
- executor.map(_update_imp_responsibility, EXISTING_IMPLEMENTATIONS.values())
1104
+ executor.map(_update_imp_customer, EXISTING_IMPLEMENTATIONS.values())
1111
1105
 
1112
1106
 
1113
- def _update_imp_responsibility(imp: ControlImplementation):
1107
+ def _update_imp_customer(imp: ControlImplementation):
1114
1108
  """
1115
1109
  Update the implementation responsibility text for a given implementation
1116
1110
 
@@ -1207,7 +1201,7 @@ def parse_and_map_data(
1207
1201
  crm_data=crm_data,
1208
1202
  version=version,
1209
1203
  )
1210
- update_responsiblity_text()
1204
+ update_customer_text()
1211
1205
 
1212
1206
  report(error_set)
1213
1207
 
@@ -1339,6 +1333,7 @@ def create_new_security_plan(profile_id: int, system_name: str):
1339
1333
 
1340
1334
  else:
1341
1335
  ret = next((plan for plan in existing_plan), None)
1336
+ logger.info(f"Found existing SSP# {ret.id}")
1342
1337
  existing_imps = ControlImplementation.get_list_by_plan(ret.id)
1343
1338
  for imp in existing_imps:
1344
1339
  EXISTING_IMPLEMENTATIONS[imp.controlID] = imp
@@ -633,6 +633,8 @@ class ScannerIntegration(ABC):
633
633
  self.plan_id: int = plan_id
634
634
  self.tenant_id: int = tenant_id
635
635
  self.is_component: bool = is_component
636
+ if self.is_component:
637
+ self.component = regscale_models.Component.get_object(self.plan_id)
636
638
  self.components: ThreadSafeList[Any] = ThreadSafeList()
637
639
  self.asset_map_by_identifier: ThreadSafeDict[str, regscale_models.Asset] = ThreadSafeDict()
638
640
  self.software_to_create: ThreadSafeList[regscale_models.SoftwareInventory] = ThreadSafeList()
@@ -819,11 +821,12 @@ class ScannerIntegration(ABC):
819
821
  @abstractmethod
820
822
  def fetch_findings(self, *args, **kwargs) -> Iterator[IntegrationFinding]:
821
823
  """
822
- Fetches findings from the integration
824
+ Fetches findings from the integration.
823
825
 
824
- :return: A list of findings
825
- :rtype: List[IntegrationFinding]
826
+ :return: An iterator of findings
827
+ :yield: Iterator[IntegrationFinding]
826
828
  """
829
+ pass
827
830
 
828
831
  @abstractmethod
829
832
  def fetch_assets(self, *args, **kwargs) -> Iterator[IntegrationAsset]:
@@ -831,7 +834,7 @@ class ScannerIntegration(ABC):
831
834
  Fetches assets from the integration
832
835
 
833
836
  :return: An iterator of assets
834
- :rtype: Iterator[IntegrationAsset]
837
+ :yield: Iterator[IntegrationAsset]
835
838
  """
836
839
 
837
840
  def get_finding_status(self, status: Optional[str]) -> regscale_models.IssueStatus:
@@ -1024,10 +1027,10 @@ class ScannerIntegration(ABC):
1024
1027
  logger.warning("Asset has no identifier, skipping")
1025
1028
  return
1026
1029
 
1027
- component = None
1030
+ component = getattr(self, "component") if self.is_component else None
1028
1031
  if component_name:
1029
1032
  logger.debug("Searching for component: %s...", component_name)
1030
- component = self.components_by_title.get(component_name)
1033
+ component = component or self.components_by_title.get(component_name)
1031
1034
  if not component:
1032
1035
  logger.debug("No existing component found with name %s, proceeding to create it...", component_name)
1033
1036
  component = regscale_models.Component(
@@ -2702,6 +2705,7 @@ class ScannerIntegration(ABC):
2702
2705
  logger.info("Syncing %s findings...", kwargs.get("title", cls.title))
2703
2706
  instance = cls(plan_id=plan_id, **kwargs)
2704
2707
  instance.set_keys(**kwargs)
2708
+ instance.ensure_data_types()
2705
2709
  # If a progress object was passed, use it instead of creating a new one
2706
2710
  instance.finding_progress = kwargs.pop("progress") if "progress" in kwargs else create_progress_object()
2707
2711
  instance.enable_finding_date_update = kwargs.get("enable_finding_date_update", False)
@@ -2771,6 +2775,7 @@ class ScannerIntegration(ABC):
2771
2775
  logger.info("Syncing %s assets...", kwargs.get("title", cls.title))
2772
2776
  instance = cls(plan_id=plan_id, **kwargs)
2773
2777
  instance.set_keys(**kwargs)
2778
+ instance.ensure_data_types()
2774
2779
  instance.asset_progress = kwargs.pop("progress") if "progress" in kwargs else create_progress_object()
2775
2780
  if asset_count := kwargs.get("asset_count"):
2776
2781
  instance.num_assets_to_process = asset_count
@@ -2813,6 +2818,17 @@ class ScannerIntegration(ABC):
2813
2818
  else:
2814
2819
  logger.debug("Unable to set the %s attribute", key)
2815
2820
 
2821
+ def ensure_data_types(self) -> None:
2822
+ """
2823
+ A method to enforce kwarg data types.
2824
+
2825
+ :return: None
2826
+ :rtype: None
2827
+ """
2828
+ # Ensure scan_date is a string
2829
+ if not isinstance(self.scan_date, str):
2830
+ self.scan_date = date_str(self.scan_date)
2831
+
2816
2832
  def log_error(self, msg: str, *args) -> None:
2817
2833
  """
2818
2834
  Logs an error message
@@ -2,7 +2,7 @@
2
2
  # -*- coding: utf-8 -*-
3
3
  """Module to allow dynamic click arguments and store commonly used click commands"""
4
4
 
5
- from typing import Tuple, Any, Optional
5
+ from typing import Any, Callable, Optional, Tuple
6
6
 
7
7
  import click
8
8
  from pathlib import Path
@@ -128,6 +128,54 @@ def regscale_ssp_id(
128
128
  )
129
129
 
130
130
 
131
+ def ssp_or_component_id(
132
+ ssp_kwargs: Optional[dict] = None,
133
+ component_kwargs: Optional[dict] = None,
134
+ ) -> Tuple[click.option, click.option]:
135
+ """
136
+ Function to return click.option for RegScale Component ID and SSP ID, user must provide either SSP ID or Component ID
137
+
138
+ :param Optional[dict] ssp_kwargs: kwargs to pass to click.option for RegScale SSP ID
139
+ :param Optional[dict] component_kwargs: kwargs to pass to click.option for RegScale Component ID
140
+ :return: click.option for RegScale Component ID and SSP ID
141
+ :rtype: click.option
142
+ """
143
+ if ssp_kwargs is None:
144
+ ssp_kwargs = {}
145
+ if component_kwargs is None:
146
+ component_kwargs = {}
147
+
148
+ def decorator(this_func) -> Callable[[Callable], click.option]:
149
+ """
150
+ Decorator to return click.option for RegScale Component ID and SSP ID
151
+ """
152
+ this_func = click.option(
153
+ "-id",
154
+ "-p",
155
+ "--regscale_ssp_id",
156
+ "--plan_id",
157
+ type=click.INT,
158
+ help=ssp_kwargs.pop("help", "The ID number from RegScale of the System Security Plan."),
159
+ prompt=ssp_kwargs.pop("prompt", None),
160
+ cls=NotRequiredIf,
161
+ not_required_if=["component_id"],
162
+ **ssp_kwargs,
163
+ )(this_func)
164
+ this_func = click.option(
165
+ "-c",
166
+ "--component_id",
167
+ type=click.INT,
168
+ help=component_kwargs.pop("help", "The ID number from RegScale of the Component."),
169
+ prompt=component_kwargs.pop("prompt", None),
170
+ cls=NotRequiredIf,
171
+ not_required_if=["regscale_ssp_id"],
172
+ **component_kwargs,
173
+ )(this_func)
174
+ return this_func
175
+
176
+ return decorator
177
+
178
+
131
179
  def regscale_id(
132
180
  help: str = "Enter the desired ID # from RegScale.",
133
181
  required: bool = True,
@@ -9,10 +9,10 @@ from pathlib import Path
9
9
  from typing import Any, Generator, List, Optional, TextIO
10
10
  from urllib.parse import urlparse
11
11
  from xml.etree.ElementTree import Element, ParseError, fromstring, parse
12
+ from logging import getLogger
12
13
 
13
14
  from regscale.core.app.api import Api
14
15
  from regscale.core.app.application import Application
15
- from regscale.core.app.logz import create_logger
16
16
  from regscale.core.app.utils.app_utils import check_file_path, get_current_datetime
17
17
  from regscale.integrations.scanner_integration import IntegrationAsset, IntegrationFinding
18
18
  from regscale.models.integration_models.burp_models import BurpRequest, BurpResponse, Issue, RequestResponse
@@ -26,7 +26,7 @@ class Burp:
26
26
  """Burp Scan information"""
27
27
 
28
28
  def __init__(self, app: Application, file_path: str, encoding="utf-8", **kwargs) -> "Burp":
29
- logger = create_logger("Burp")
29
+ logger = getLogger("regscale")
30
30
  logger.info("Now processing %s", file_path)
31
31
  self.integration_assets: Generator[IntegrationAsset, None, None] = (x for x in [])
32
32
  self.integration_findings: Generator[IntegrationFinding, None, None] = (x for x in [])
@@ -293,10 +293,10 @@ class Burp:
293
293
  :rtype: RequestResponse
294
294
  """
295
295
  request_data = item.find(".//request")
296
- if response_data := item.find(".//response"):
297
- base64_dat = bool(request_data.attrib["base64"]) if "base64" in item.attrib else False
296
+ if (response_data := item.find(".//response")) is not None:
297
+ base64_dat = request_data.attrib.get("base64", "false").lower() == "true"
298
298
  response_data_is_base64 = BurpResponse.is_base64(response_data.text)
299
- method = request_data.attrib["method"] if "method" in item.attrib else "GET"
299
+ method = request_data.attrib.get("method", "GET")
300
300
  request = (
301
301
  BurpRequest(dataString=request_data.text, base64=base64_dat, method=method)
302
302
  if BurpRequest.is_base64(request_data.text)
@@ -395,7 +395,8 @@ class Burp:
395
395
  root = fromstring(html)
396
396
  return [link.text.strip() for link in root.iter("a")]
397
397
 
398
- def get_domain_name(self, url: str) -> str:
398
+ @staticmethod
399
+ def get_domain_name(url: str) -> str:
399
400
  """
400
401
  Get the domain name from a URL
401
402
 
@@ -407,7 +408,8 @@ class Burp:
407
408
  domain_name = parsed_url.hostname
408
409
  return domain_name
409
410
 
410
- def strip_html_tags(self, text: str) -> str:
411
+ @staticmethod
412
+ def strip_html_tags(text: str) -> str:
411
413
  """
412
414
  Strip HTML tags from a string.
413
415
 
@@ -420,7 +422,8 @@ class Burp:
420
422
  clean = re.sub(r"<.*?>", "", text)
421
423
  return clean
422
424
 
423
- def extract_cve(self, input_string: str) -> Optional[str]:
425
+ @staticmethod
426
+ def extract_cve(input_string: str) -> Optional[str]:
424
427
  """
425
428
  Extract CVEs from a string.
426
429