regscale-cli 6.19.2.0__py3-none-any.whl → 6.20.1.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 (31) 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/synqly/assets.py +26 -0
  8. regscale/integrations/public/fedramp/appendix_parser.py +499 -104
  9. regscale/integrations/public/fedramp/fedramp_cis_crm.py +5 -3
  10. regscale/integrations/public/fedramp/fedramp_five.py +89 -43
  11. regscale/models/integration_models/cisa_kev_data.json +277 -22
  12. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  13. regscale/models/regscale_models/__init__.py +7 -0
  14. regscale/models/regscale_models/business_impact_assessment.py +71 -0
  15. regscale/models/regscale_models/control_implementation.py +15 -0
  16. regscale/models/regscale_models/evidence.py +72 -4
  17. regscale/models/regscale_models/evidence_mapping.py +1 -1
  18. regscale/models/regscale_models/master_assessment.py +19 -0
  19. regscale/models/regscale_models/policy.py +90 -0
  20. regscale/models/regscale_models/question.py +30 -2
  21. regscale/models/regscale_models/questionnaire.py +4 -3
  22. regscale/models/regscale_models/questionnaire_instance.py +37 -14
  23. regscale/models/regscale_models/rbac.py +0 -1
  24. regscale/models/regscale_models/risk_trend.py +67 -0
  25. regscale/models/regscale_models/task.py +14 -1
  26. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/METADATA +114 -55
  27. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/RECORD +31 -28
  28. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/LICENSE +0 -0
  29. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/WHEEL +0 -0
  30. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/entry_points.txt +0 -0
  31. {regscale_cli-6.19.2.0.dist-info → regscale_cli-6.20.1.0.dist-info}/top_level.txt +0 -0
@@ -360,13 +360,15 @@ def update_imp_objective(
360
360
  )
361
361
  existing_pairs = {(obj.objectiveId, obj.implementationId) for obj in existing_imp_obj}
362
362
  responsibility = responsibility_map.get(responsibility, responsibility)
363
+ logger.debug(f"CRM Record: {crm_record}")
364
+ can_be_inherited_from_csp: str = crm_record.get("can_be_inherited_from_csp") or ""
363
365
  for objective in objectives:
364
366
  current_pair = (objective.id, imp.id)
365
367
  if current_pair not in existing_pairs:
366
368
  imp_obj = ImplementationObjective(
367
369
  id=0,
368
370
  uuid="",
369
- inherited=crm_record.get("can_be_inherited_from_csp") in ["Yes", "Partial"],
371
+ inherited=can_be_inherited_from_csp in ["Yes", "Partial"],
370
372
  implementationId=imp.id,
371
373
  status=status_map.get(cis_record.get("implementation_status", NOT_IMPLEMENTED), NOT_IMPLEMENTED),
372
374
  objectiveId=objective.id,
@@ -374,8 +376,8 @@ def update_imp_objective(
374
376
  securityControlId=objective.securityControlId,
375
377
  securityPlanId=REGSCALE_SSP_ID,
376
378
  responsibility=responsibility,
377
- cloudResponsibility=customer_responsibility,
378
- customerResponsibility=customer_responsibility,
379
+ cloudResponsibility=customer_responsibility if can_be_inherited_from_csp.lower() == "yes" else "",
380
+ customerResponsibility=(customer_responsibility if can_be_inherited_from_csp.lower() != "yes" else ""),
379
381
  authorizationId=leverage_auth_id,
380
382
  parentObjectiveId=objective.parentObjectiveId,
381
383
  )
@@ -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)