regscale-cli 6.19.1.0__py3-none-any.whl → 6.20.0.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 (36) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/airflow/config.py +2 -0
  3. regscale/airflow/tasks/groups.py +11 -47
  4. regscale/core/app/internal/login.py +49 -43
  5. regscale/core/app/internal/model_editor.py +2 -1
  6. regscale/dev/code_gen.py +2 -5
  7. regscale/integrations/commercial/amazon/common.py +5 -4
  8. regscale/integrations/commercial/aws/scanner.py +3 -2
  9. regscale/integrations/commercial/synqly/assets.py +20 -0
  10. regscale/integrations/commercial/synqly/ticketing.py +25 -0
  11. regscale/integrations/commercial/wizv2/click.py +3 -3
  12. regscale/integrations/public/fedramp/appendix_parser.py +499 -104
  13. regscale/integrations/public/fedramp/fedramp_five.py +89 -43
  14. regscale/integrations/scanner_integration.py +1 -1
  15. regscale/models/app_models/import_validater.py +2 -0
  16. regscale/models/integration_models/cisa_kev_data.json +355 -27
  17. regscale/models/integration_models/flat_file_importer/__init__.py +26 -9
  18. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  19. regscale/models/regscale_models/__init__.py +5 -0
  20. regscale/models/regscale_models/business_impact_assessment.py +71 -0
  21. regscale/models/regscale_models/control_implementation.py +15 -0
  22. regscale/models/regscale_models/master_assessment.py +19 -0
  23. regscale/models/regscale_models/policy.py +90 -0
  24. regscale/models/regscale_models/question.py +30 -2
  25. regscale/models/regscale_models/questionnaire.py +4 -3
  26. regscale/models/regscale_models/questionnaire_instance.py +37 -14
  27. regscale/models/regscale_models/rbac.py +0 -1
  28. regscale/models/regscale_models/regscale_model.py +16 -15
  29. regscale/models/regscale_models/risk_trend.py +67 -0
  30. regscale/utils/graphql_client.py +2 -1
  31. {regscale_cli-6.19.1.0.dist-info → regscale_cli-6.20.0.0.dist-info}/METADATA +130 -71
  32. {regscale_cli-6.19.1.0.dist-info → regscale_cli-6.20.0.0.dist-info}/RECORD +36 -33
  33. {regscale_cli-6.19.1.0.dist-info → regscale_cli-6.20.0.0.dist-info}/LICENSE +0 -0
  34. {regscale_cli-6.19.1.0.dist-info → regscale_cli-6.20.0.0.dist-info}/WHEEL +0 -0
  35. {regscale_cli-6.19.1.0.dist-info → regscale_cli-6.20.0.0.dist-info}/entry_points.txt +0 -0
  36. {regscale_cli-6.19.1.0.dist-info → regscale_cli-6.20.0.0.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,6 @@ from tempfile import gettempdir
11
11
  from typing import Any, Dict, List, Optional, Tuple, Union
12
12
 
13
13
  from dateutil.relativedelta import relativedelta
14
- from packaging.version import Version
15
14
 
16
15
  from regscale.core.app.api import Api
17
16
  from regscale.core.app.application import Application
@@ -28,7 +27,6 @@ from regscale.models import (
28
27
  ControlParameter,
29
28
  File,
30
29
  ImplementationObjective,
31
- ImplementationObjectiveResponsibility,
32
30
  ImplementationOption,
33
31
  LeveragedAuthorization,
34
32
  Parameter,
@@ -40,6 +38,7 @@ from regscale.models import (
40
38
  StakeHolder,
41
39
  SystemRole,
42
40
  User,
41
+ ImplementationControlOrigin,
43
42
  )
44
43
  from regscale.utils.version import RegscaleVersion
45
44
 
@@ -1400,8 +1399,7 @@ def handle_parts(
1400
1399
  else control_objectives
1401
1400
  )
1402
1401
  logger.debug(f"Matching Objectives: {matching_objectives}")
1403
- regscale_version = RegscaleVersion.get_platform_version()
1404
- if len(regscale_version) >= 10 or Version(regscale_version) >= Version("6.13.0.0"):
1402
+ if RegscaleVersion.meets_minimum_version("6.13.0.0"):
1405
1403
  status = status_map.get(status, status)
1406
1404
 
1407
1405
  # Status should never be None
@@ -1510,44 +1508,33 @@ def map_responsibility(responsibility: str) -> str:
1510
1508
  :return: The mapped responsibility.
1511
1509
  :rtype: str
1512
1510
  """
1513
- # This should be server code, sorry
1511
+ if not responsibility:
1512
+ return "" # Return empty string instead of None
1513
+
1514
+ # Handle comma-separated values
1515
+ if "," in responsibility:
1516
+ responsibility_values = [r.strip() for r in responsibility.split(",")]
1517
+ return ",".join([map_responsibility(r) for r in responsibility_values])
1518
+
1519
+ # This should be server code with proper enums, but this is the best we can do for now
1514
1520
  responsibility_map = {
1515
- "Provider": "Service Provider Corporate",
1516
- "Provider (System Specific)": "Service Provider System Specific",
1517
- "Customer": "Provided by Customer (Customer System Specific)",
1518
- "Hybrid": "Service Provider Hybrid (Corporate and System Specific)",
1519
- "Customer Configured": "Configured by Customer (Customer System Specific)",
1520
- "Shared": "Shared (Service Provider and Customer Responsibility)",
1521
- "Inherited": "Inherited from pre-existing FedRAMP Authorization",
1521
+ "Service Provider Corporate": ImplementationControlOrigin.SERVICE_PROVIDER_CORPORATE.value,
1522
+ "Service Provider System Specific": ImplementationControlOrigin.SERVICE_PROVIDER_SYSTEM.value,
1523
+ "Service Provider Hybrid (Corporate and System Specific)": ImplementationControlOrigin.SERVICE_PROVIDER_HYBRID.value, # Map to closest value
1524
+ "Configured by Customer (Customer System Specific)": ImplementationControlOrigin.CONFIGURED_BY_CUSTOMER.value,
1525
+ "Provided by Customer (Customer System Specific)": ImplementationControlOrigin.PROVIDED_BY_CUSTOMER.value,
1526
+ "Shared (Service Provider and Customer Responsibility)": ImplementationControlOrigin.SHARED.value,
1527
+ "Inherited from pre-existing FedRAMP Authorization": ImplementationControlOrigin.INHERITED_FROM_PRE_EXISTING_FEDRAMP_AUTHORIZATION.value,
1522
1528
  }
1523
- res = ""
1524
- if responsibility == "Service Provider System Specific":
1525
- res = ImplementationObjectiveResponsibility.PROVIDER_SYSTEM_SPECIFIC.value
1526
- if responsibility == SERVICE_PROVIDER_CORPORATE:
1527
- res = ImplementationObjectiveResponsibility.PROVIDER.value
1528
- if responsibility == "Provided by Customer (Customer System Specific)":
1529
- res = ImplementationObjectiveResponsibility.CUSTOMER.value
1530
- if responsibility == "Configured by Customer (Customer System Specific)":
1531
- res = ImplementationObjectiveResponsibility.CUSTOMER_CONFIGURED.value
1532
- if responsibility == "Service Provider Hybrid (Corporate and System Specific)":
1533
- res = ImplementationObjectiveResponsibility.HYBRID.value
1534
- if responsibility == "Inherited from pre-existing FedRAMP Authorization":
1535
- res = ImplementationObjectiveResponsibility.INHERITED.value
1536
- if responsibility == ImplementationObjectiveResponsibility.NOT_APPLICABLE.value:
1537
- res = ImplementationObjectiveResponsibility.NOT_APPLICABLE.value
1538
- if responsibility == "Shared (Service Provider and Customer Responsibility)":
1539
- res = ImplementationObjectiveResponsibility.SHARED.value
1540
- regscale_version = RegscaleVersion.get_platform_version()
1541
- if len(regscale_version) >= 10 or Version(regscale_version) >= Version("6.13.0.0"):
1542
- return responsibility_map.get(res, res)
1543
- return res
1529
+
1530
+ return responsibility_map.get(responsibility, responsibility or "")
1544
1531
 
1545
1532
 
1546
1533
  def handle_implementation_objectives(
1547
1534
  objective: ControlObjective,
1548
1535
  part_statement: str,
1549
1536
  status: Optional[str],
1550
- control_implementation: ControlImplementation,
1537
+ control_implementation: Union[ControlImplementation, int, None],
1551
1538
  imp_objectives: List[ImplementationObjective],
1552
1539
  control: SecurityControl,
1553
1540
  duplicate: bool,
@@ -1555,26 +1542,30 @@ def handle_implementation_objectives(
1555
1542
  ):
1556
1543
  """
1557
1544
  Handle the implementation objectives for the given objective, option, and control implementation.
1558
- :param ControlObjective objective:
1559
- :param str part_statement:
1560
- :param Optional[str] status:
1561
- :param ControlImplementation control_implementation:
1562
- :param List[ImplementationObjective] imp_objectives:
1563
- :param SecurityControl control:
1545
+ :param ControlObjective objective: The control objective.
1546
+ :param str part_statement: The statement text for this part.
1547
+ :param Optional[str] status: The implementation status.
1548
+ :param Union[ControlImplementation, int, None] control_implementation: The control implementation object or ID.
1549
+ :param List[ImplementationObjective] imp_objectives: List to collect implementation objectives.
1550
+ :param SecurityControl control: The security control.
1564
1551
  :param bool duplicate: Whether the option is a duplicate will add note if True.
1565
1552
  :param Optional[str] origination: The origination of the implementation.
1566
1553
  """
1567
1554
  if isinstance(status, ControlImplementationStatus):
1568
1555
  status = status.value
1556
+
1557
+ # Ensure part_statement is valid
1558
+ statement = part_statement if part_statement else ""
1559
+
1569
1560
  imp_obj = ImplementationObjective(
1570
1561
  securityControlId=control.id,
1571
- implementationId=control_implementation.id,
1562
+ implementationId=control_implementation.id if hasattr(control_implementation, "id") else control_implementation,
1572
1563
  objectiveId=objective.id,
1573
1564
  optionId=None,
1574
1565
  status=status,
1575
- statement=part_statement,
1566
+ statement=statement,
1576
1567
  notes="#replicated-data-part" if duplicate else "",
1577
- responsibility=origination if origination else None,
1568
+ responsibility=origination,
1578
1569
  )
1579
1570
  if imp_obj not in imp_objectives:
1580
1571
  imp_objectives.append(imp_obj)
@@ -2341,3 +2332,58 @@ def get_max_version(entries: List[Dict]) -> Optional[str]:
2341
2332
  max_version = max(max_version, version_str, key=parse_version)
2342
2333
  logger.debug(f"Version: {max_version}")
2343
2334
  return max_version
2335
+
2336
+
2337
+ def process_objective(
2338
+ objective: ControlObjective,
2339
+ processed_objective_ids: set,
2340
+ existing_objectives_by_objective_id: Dict,
2341
+ control: SecurityControl,
2342
+ part_statement: Optional[str],
2343
+ status: Optional[str],
2344
+ origination: Optional[str] = None,
2345
+ imp_objectives: List[ImplementationObjective] = None,
2346
+ ):
2347
+ """
2348
+ Process a single control objective.
2349
+
2350
+ :param ControlObjective objective: The objective to process.
2351
+ :param set processed_objective_ids: Set of already processed objective IDs.
2352
+ :param Dict existing_objectives_by_objective_id: Dictionary of existing objectives by ID.
2353
+ :param SecurityControl control: The security control.
2354
+ :param Optional[str] part_statement: The statement for this part, may be None.
2355
+ :param Optional[str] status: The implementation status for this objective.
2356
+ :param Optional[str] origination: The implementation origination for this objective.
2357
+ :param List[ImplementationObjective] imp_objectives: List to collect implementation objectives.
2358
+ :return: None
2359
+ """
2360
+ logger.debug(f"Processing objective: {objective.id} - {objective.name}")
2361
+
2362
+ # Skip if already processed
2363
+ if objective.id in processed_objective_ids:
2364
+ logger.debug(f"Objective {objective.id} already processed")
2365
+ return
2366
+
2367
+ processed_objective_ids.add(objective.id)
2368
+ existing_objective = existing_objectives_by_objective_id.get(objective.id)
2369
+
2370
+ statement = part_statement if part_statement is not None else ""
2371
+
2372
+ # Update existing objective if found
2373
+ if existing_objective is not None:
2374
+ logger.debug(f"Updating existing objective: {existing_objective.id}")
2375
+ existing_objective.status = status
2376
+ existing_objective.statement = statement
2377
+ if origination is not None:
2378
+ existing_objective.responsibility = origination
2379
+ existing_objective.save()
2380
+ # Create new objective if implementation objectives list provided
2381
+ elif imp_objectives is not None:
2382
+ logger.debug(f"Creating new objective for {objective.id}")
2383
+ imp_obj = ImplementationObjective(
2384
+ objectiveId=objective.id,
2385
+ status=status,
2386
+ statement=statement,
2387
+ responsibility=origination,
2388
+ )
2389
+ imp_objectives.append(imp_obj)
@@ -2259,7 +2259,7 @@ class ScannerIntegration(ABC):
2259
2259
  self.handle_passing_checklist(finding=finding, plan_id=self.plan_id)
2260
2260
 
2261
2261
  # Process vulnerability if applicable
2262
- if finding.status != regscale_models.IssueStatus.Closed:
2262
+ if finding.status != regscale_models.IssueStatus.Closed or ScannerVariables.ingestClosedIssues:
2263
2263
  if asset := self.get_asset_by_identifier(finding.asset_identifier):
2264
2264
  if vulnerability_id := self.handle_vulnerability(finding, asset, scan_history):
2265
2265
  current_vulnerabilities[asset.id].add(vulnerability_id)
@@ -183,6 +183,8 @@ class ImportValidater:
183
183
  df = pandas.read_csv(file_path, skiprows=self.skip_rows - 1, on_bad_lines="warn")
184
184
  else:
185
185
  df = pandas.read_csv(file_path, on_bad_lines="warn")
186
+ if self.ignore_unnamed:
187
+ df = df.loc[:, ~df.columns.str.contains("^Unnamed")]
186
188
  except pandas.errors.ParserError:
187
189
  raise ValidationException(f"Unable to parse the {CSV} file: {file_path}")
188
190
  self.validate_headers(df.columns)