regscale-cli 6.20.10.0__py3-none-any.whl → 6.21.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 (64) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +12 -5
  3. regscale/core/app/internal/set_permissions.py +58 -27
  4. regscale/integrations/commercial/__init__.py +1 -2
  5. regscale/integrations/commercial/amazon/common.py +79 -2
  6. regscale/integrations/commercial/aws/cli.py +183 -9
  7. regscale/integrations/commercial/aws/scanner.py +544 -9
  8. regscale/integrations/commercial/cpe.py +18 -1
  9. regscale/integrations/commercial/nessus/scanner.py +2 -0
  10. regscale/integrations/commercial/sonarcloud.py +35 -36
  11. regscale/integrations/commercial/synqly/ticketing.py +51 -0
  12. regscale/integrations/commercial/tenablev2/jsonl_scanner.py +2 -1
  13. regscale/integrations/commercial/wizv2/async_client.py +10 -3
  14. regscale/integrations/commercial/wizv2/click.py +102 -26
  15. regscale/integrations/commercial/wizv2/constants.py +249 -1
  16. regscale/integrations/commercial/wizv2/issue.py +2 -2
  17. regscale/integrations/commercial/wizv2/parsers.py +3 -2
  18. regscale/integrations/commercial/wizv2/policy_compliance.py +1858 -0
  19. regscale/integrations/commercial/wizv2/scanner.py +15 -21
  20. regscale/integrations/commercial/wizv2/utils.py +258 -85
  21. regscale/integrations/commercial/wizv2/variables.py +4 -3
  22. regscale/integrations/compliance_integration.py +1455 -0
  23. regscale/integrations/integration_override.py +15 -6
  24. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  25. regscale/integrations/public/fedramp/markdown_parser.py +7 -1
  26. regscale/integrations/scanner_integration.py +193 -37
  27. regscale/models/app_models/__init__.py +1 -0
  28. regscale/models/integration_models/amazon_models/inspector_scan.py +32 -57
  29. regscale/models/integration_models/aqua.py +92 -78
  30. regscale/models/integration_models/cisa_kev_data.json +117 -5
  31. regscale/models/integration_models/defenderimport.py +64 -59
  32. regscale/models/integration_models/ecr_models/ecr.py +100 -147
  33. regscale/models/integration_models/flat_file_importer/__init__.py +52 -38
  34. regscale/models/integration_models/ibm.py +29 -47
  35. regscale/models/integration_models/nexpose.py +156 -68
  36. regscale/models/integration_models/prisma.py +46 -66
  37. regscale/models/integration_models/qualys.py +99 -93
  38. regscale/models/integration_models/snyk.py +229 -158
  39. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  40. regscale/models/integration_models/veracode.py +15 -20
  41. regscale/{integrations/commercial/wizv2/models.py → models/integration_models/wizv2.py} +4 -12
  42. regscale/models/integration_models/xray.py +276 -82
  43. regscale/models/regscale_models/control_implementation.py +14 -12
  44. regscale/models/regscale_models/file.py +4 -0
  45. regscale/models/regscale_models/issue.py +123 -0
  46. regscale/models/regscale_models/milestone.py +1 -1
  47. regscale/models/regscale_models/rbac.py +22 -0
  48. regscale/models/regscale_models/regscale_model.py +4 -2
  49. regscale/models/regscale_models/security_plan.py +1 -1
  50. regscale/utils/graphql_client.py +3 -1
  51. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/METADATA +9 -9
  52. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/RECORD +64 -60
  53. tests/fixtures/test_fixture.py +58 -2
  54. tests/regscale/core/test_app.py +5 -3
  55. tests/regscale/core/test_version_regscale.py +5 -3
  56. tests/regscale/integrations/test_integration_mapping.py +522 -40
  57. tests/regscale/integrations/test_issue_due_date.py +1 -1
  58. tests/regscale/integrations/test_update_finding_dates.py +336 -0
  59. tests/regscale/integrations/test_wiz_policy_compliance_affected_controls.py +154 -0
  60. tests/regscale/models/test_asset.py +406 -50
  61. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/LICENSE +0 -0
  62. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/WHEEL +0 -0
  63. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/entry_points.txt +0 -0
  64. {regscale_cli-6.20.10.0.dist-info → regscale_cli-6.21.1.0.dist-info}/top_level.txt +0 -0
@@ -1270,6 +1270,129 @@ class Issue(RegScaleModel):
1270
1270
  raise ValueError(f"riskAdjustment must be one of {allowed_values}")
1271
1271
  return v
1272
1272
 
1273
+ # New method to determine and set isPoam based on NIST/FedRAMP criteria
1274
+ def set_is_poam(
1275
+ self,
1276
+ config: Optional[Dict[str, Any]] = None,
1277
+ standard: str = "fedramp",
1278
+ current_date: Optional[datetime] = None,
1279
+ ) -> None:
1280
+ """
1281
+ Sets the isPoam field based on NIST 800-53 or FedRAMP criteria, preserving historical POAM status.
1282
+
1283
+ Criteria:
1284
+ - Preserves isPoam=True for imported data, even if closed, for reporting purposes.
1285
+ - For new issues:
1286
+ - Skips if false positive, operational requirement, or deviation rationale exists.
1287
+ - FedRAMP: High/Critical issues are POAMs if open; scan-based issues are POAMs if overdue; non-scan issues are POAMs if open.
1288
+ - NIST: POAM for any open deficiency unless accepted as residual risk.
1289
+ - Uses config thresholds (e.g., {'critical': 30, 'high': 90, 'medium': 90, 'low': 365, 'status': 'Open'}).
1290
+
1291
+ Args:
1292
+ config: Optional dictionary with severity thresholds and status from init.yaml.
1293
+ Defaults to FedRAMP: {'critical': 30, 'high': 30, 'medium': 90, 'low': 180, 'status': 'Open'}.
1294
+ For NIST, uses {'critical': 30, 'high': 90, 'medium': 90, 'low': 180, 'status': 'Open'}.
1295
+ standard: 'fedramp' (default) or 'nist'.
1296
+ current_date: Optional datetime for calculation (defaults to current time).
1297
+
1298
+ Returns:
1299
+ None: Sets the isPoam attribute directly.
1300
+ """
1301
+ # Use current time if not provided
1302
+ current_date = current_date or datetime.datetime.now()
1303
+
1304
+ # Preserve historical POAM status for imported data
1305
+ if self.isPoam:
1306
+ return
1307
+
1308
+ # Define open statuses
1309
+ open_statuses = {
1310
+ IssueStatus.Open,
1311
+ IssueStatus.Delayed,
1312
+ IssueStatus.PendingVerification,
1313
+ IssueStatus.VendorDependency,
1314
+ IssueStatus.PendingApproval,
1315
+ }
1316
+
1317
+ # Skip if issue is accepted as residual risk
1318
+ if self.falsePositive or self.operationalRequirement or self.deviationRationale:
1319
+ self.isPoam = False
1320
+ return
1321
+
1322
+ # Load default thresholds based on standard if config is not provided
1323
+ config = config or (
1324
+ {"critical": 30, "high": 30, "medium": 90, "low": 180, "status": "Open"}
1325
+ if standard == "fedramp"
1326
+ else {"critical": 30, "high": 90, "medium": 90, "low": 180, "status": "Open"}
1327
+ )
1328
+
1329
+ # Map severity to remediation days
1330
+ severity_map = {
1331
+ IssueSeverity.Critical: config.get("critical", 30),
1332
+ IssueSeverity.High: config.get("high", 90),
1333
+ IssueSeverity.Moderate: config.get("medium", 90),
1334
+ IssueSeverity.Low: config.get("low", 365),
1335
+ IssueSeverity.NotAssigned: config.get("low", 365),
1336
+ }
1337
+
1338
+ # Normalize severity
1339
+ severity = (
1340
+ IssueSeverity(self.severityLevel)
1341
+ if self.severityLevel in {s.value for s in IssueSeverity}
1342
+ else IssueSeverity.NotAssigned
1343
+ )
1344
+ threshold_days = severity_map[severity]
1345
+
1346
+ # Get detection date
1347
+ detection_date_str = self.dateFirstDetected or self.dateCreated
1348
+ if not detection_date_str:
1349
+ self.isPoam = False
1350
+ return
1351
+
1352
+ try:
1353
+ detection_date = datetime.datetime.strptime(detection_date_str, "%Y-%m-%dT%H:%M:%S")
1354
+ except ValueError:
1355
+ try:
1356
+ detection_date = datetime.datetime.strptime(detection_date_str, "%Y-%m-%d")
1357
+ except ValueError:
1358
+ self.isPoam = False
1359
+ return
1360
+
1361
+ days_since_detection = (current_date - detection_date).days
1362
+
1363
+ # Define scan sources
1364
+ scan_sources = {"Vulnerability Assessment", "FDCC/USGCB", "Penetration Test"}
1365
+ is_scan = self.identification in scan_sources
1366
+
1367
+ # Apply standard-specific logic
1368
+ if standard == "fedramp":
1369
+ # FedRAMP: High/Critical are always POAMs if open
1370
+ if severity in {IssueSeverity.High, IssueSeverity.Critical}:
1371
+ self.isPoam = self.status in open_statuses
1372
+ # Scan-based: POAM if overdue
1373
+ elif is_scan:
1374
+ self.isPoam = days_since_detection > threshold_days
1375
+ # Non-scan: POAM if open
1376
+ else:
1377
+ self.isPoam = self.status in open_statuses
1378
+
1379
+ # Handle vendor dependencies
1380
+ if self.vendorDependency and self.vendorLastUpdate:
1381
+ try:
1382
+ vendor_date = datetime.datetime.strptime(self.vendorLastUpdate, "%Y-%m-%dT%H:%M:%S")
1383
+ days_since_vendor = (current_date - vendor_date).days
1384
+ self.isPoam = days_since_vendor > threshold_days
1385
+ except ValueError:
1386
+ pass # Fall back to detection date logic
1387
+
1388
+ else: # NIST 800-53
1389
+ # NIST: POAM for any open deficiency
1390
+ self.isPoam = self.status in open_statuses
1391
+
1392
+ # Apply status filter from config if specified
1393
+ if "status" in config:
1394
+ self.isPoam = self.isPoam and self.status == config["status"]
1395
+
1273
1396
 
1274
1397
  def build_issue_dict_from_query(a: Dict[str, Any]) -> Dict[str, Any]:
1275
1398
  """
@@ -24,7 +24,7 @@ class Milestone(RegScaleModel):
24
24
  completed: Optional[bool] = False
25
25
  dateCompleted: Optional[str] = Field(default_factory=get_current_datetime)
26
26
  notes: Optional[str] = ""
27
- parentID: Optional[str] = None
27
+ parentID: Optional[int] = None
28
28
  parentModule: str = ""
29
29
 
30
30
  @staticmethod
@@ -28,6 +28,7 @@ class RBAC(RegScaleModel):
28
28
  add="/api/{model_slug}/add/{moduleId}/{parentId}/{groupId}/{permissionType}",
29
29
  public="/api/{model_slug}/public/{moduleId}/{parentId}/{public}",
30
30
  none_standard_delete="/api/{model_slug}/{moduleId}/{parentId}/{rbacId}",
31
+ reset="/api/{model_slug}/reset/{moduleId}/{parentId}",
31
32
  )
32
33
 
33
34
  @classmethod
@@ -129,3 +130,24 @@ class RBAC(RegScaleModel):
129
130
  else:
130
131
  cls.log_response_error(response=response)
131
132
  return False
133
+
134
+ @classmethod
135
+ def reset(cls, module_id: int, parent_id: int) -> bool:
136
+ """
137
+ Proliferates the RBAC entry to all its children
138
+
139
+ :param int module_id: The ID of the module
140
+ :param int parent_id: The ID of the parent
141
+ :return: True if the RBAC entry was added, False otherwise
142
+ :rtype: bool
143
+ """
144
+ response = cls._get_api_handler().get(
145
+ endpoint=cls.get_endpoint("reset").format(
146
+ model_slug=cls._module_slug, moduleId=module_id, parentId=parent_id
147
+ )
148
+ )
149
+ if response and response.ok:
150
+ return True
151
+ else:
152
+ cls.log_response_error(response=response)
153
+ return False
@@ -1279,7 +1279,9 @@ class RegScaleModel(BaseModel, ABC):
1279
1279
  exc_info=True,
1280
1280
  )
1281
1281
  if response and not response.ok:
1282
- logger.error(f"Response Error: Code #{response.status_code}: {response.reason}\n{response.text}")
1282
+ logger.error(
1283
+ f"Response Error: Code #{response.status_code}: {response.reason}\n{response.text}", exc_info=True
1284
+ )
1283
1285
  if response is None:
1284
1286
  error_msg = "Response was None"
1285
1287
  logger.error(error_msg)
@@ -1522,7 +1524,7 @@ class RegScaleModel(BaseModel, ABC):
1522
1524
  :return: A list of objects
1523
1525
  :rtype: List[T]
1524
1526
  """
1525
- response = cls._get_api_handler().get(endpoint=cls.get_endpoint("list"))
1527
+ response = cls._get_api_handler().get(endpoint=cls.get_endpoint("list").format(module_slug=cls._module_slug))
1526
1528
  if response.ok:
1527
1529
  return cast(List[T], [cls.get_object(object_id=sp["id"]) for sp in response.json()])
1528
1530
  else:
@@ -176,7 +176,7 @@ class SecurityPlan(RegScaleModel):
176
176
  return {}
177
177
 
178
178
  @classmethod
179
- def get_list(cls) -> list:
179
+ def get_ssp_list(cls) -> list:
180
180
  """
181
181
  Get a list of objects.
182
182
 
@@ -5,6 +5,8 @@ A module for making paginated GraphQL queries.
5
5
  import logging
6
6
  from typing import List, Dict, Optional, Any
7
7
 
8
+ import graphql
9
+
8
10
  from regscale.core.app.utils.app_utils import create_progress_object, error_and_exit
9
11
 
10
12
  logger = logging.getLogger(__name__)
@@ -90,7 +92,7 @@ class PaginatedGraphQLClient:
90
92
  return result
91
93
  except Exception as e:
92
94
  logger.error(f"An error occurred while executing the query: {str(e)}", exc_info=True)
93
- logger.error(f"Query: {self.query}")
95
+ logger.error(f"Query: {graphql.print_ast(self.query)}")
94
96
  error_and_exit(f"Variable: {variables}")
95
97
 
96
98
  def fetch_results(self, variables: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: regscale-cli
3
- Version: 6.20.10.0
3
+ Version: 6.21.1.0
4
4
  Summary: Command Line Interface (CLI) for bulk processing/loading data into RegScale
5
5
  Home-page: https://github.com/RegScale/regscale-cli
6
6
  Author: Travis Howerton
@@ -56,7 +56,7 @@ Requires-Dist: pydantic ~=2.11.0
56
56
  Requires-Dist: pypandoc
57
57
  Requires-Dist: pypandoc-binary
58
58
  Requires-Dist: pytest
59
- Requires-Dist: python-dateutil ~=2.8.2
59
+ Requires-Dist: python-dateutil ~=2.9.0
60
60
  Requires-Dist: python-docx
61
61
  Requires-Dist: python-jwt ==4.1.0
62
62
  Requires-Dist: pyxnat ==1.5.*
@@ -137,7 +137,7 @@ Requires-Dist: pydantic ~=2.11.0 ; extra == 'airflow'
137
137
  Requires-Dist: pypandoc ; extra == 'airflow'
138
138
  Requires-Dist: pypandoc-binary ; extra == 'airflow'
139
139
  Requires-Dist: pytest ; extra == 'airflow'
140
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'airflow'
140
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'airflow'
141
141
  Requires-Dist: python-docx ; extra == 'airflow'
142
142
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'airflow'
143
143
  Requires-Dist: pyxnat ==1.5.* ; extra == 'airflow'
@@ -222,7 +222,7 @@ Requires-Dist: pydantic ~=2.11.0 ; extra == 'airflow-azure'
222
222
  Requires-Dist: pypandoc ; extra == 'airflow-azure'
223
223
  Requires-Dist: pypandoc-binary ; extra == 'airflow-azure'
224
224
  Requires-Dist: pytest ; extra == 'airflow-azure'
225
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'airflow-azure'
225
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'airflow-azure'
226
226
  Requires-Dist: python-docx ; extra == 'airflow-azure'
227
227
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'airflow-azure'
228
228
  Requires-Dist: pyxnat ==1.5.* ; extra == 'airflow-azure'
@@ -307,7 +307,7 @@ Requires-Dist: pyodbc ; extra == 'airflow-sqlserver'
307
307
  Requires-Dist: pypandoc ; extra == 'airflow-sqlserver'
308
308
  Requires-Dist: pypandoc-binary ; extra == 'airflow-sqlserver'
309
309
  Requires-Dist: pytest ; extra == 'airflow-sqlserver'
310
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'airflow-sqlserver'
310
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'airflow-sqlserver'
311
311
  Requires-Dist: python-docx ; extra == 'airflow-sqlserver'
312
312
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'airflow-sqlserver'
313
313
  Requires-Dist: pyxnat ==1.5.* ; extra == 'airflow-sqlserver'
@@ -397,7 +397,7 @@ Requires-Dist: pydantic ~=2.11.0 ; extra == 'all'
397
397
  Requires-Dist: pypandoc ; extra == 'all'
398
398
  Requires-Dist: pypandoc-binary ; extra == 'all'
399
399
  Requires-Dist: pytest ; extra == 'all'
400
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'all'
400
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'all'
401
401
  Requires-Dist: python-docx ; extra == 'all'
402
402
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'all'
403
403
  Requires-Dist: pyxnat ==1.5.* ; extra == 'all'
@@ -460,7 +460,7 @@ Requires-Dist: pydantic ~=2.11.0 ; extra == 'ansible'
460
460
  Requires-Dist: pypandoc ; extra == 'ansible'
461
461
  Requires-Dist: pypandoc-binary ; extra == 'ansible'
462
462
  Requires-Dist: pytest ; extra == 'ansible'
463
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'ansible'
463
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'ansible'
464
464
  Requires-Dist: python-docx ; extra == 'ansible'
465
465
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'ansible'
466
466
  Requires-Dist: pyxnat ==1.5.* ; extra == 'ansible'
@@ -539,7 +539,7 @@ Requires-Dist: pytest-rerunfailures ; extra == 'dev'
539
539
  Requires-Dist: pytest-timeout ; extra == 'dev'
540
540
  Requires-Dist: pytest-xdist ; extra == 'dev'
541
541
  Requires-Dist: pytest >=5 ; extra == 'dev'
542
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'dev'
542
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'dev'
543
543
  Requires-Dist: python-docx ; extra == 'dev'
544
544
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'dev'
545
545
  Requires-Dist: pyxnat ==1.5.* ; extra == 'dev'
@@ -612,7 +612,7 @@ Requires-Dist: pydantic ~=2.11.0 ; extra == 'server'
612
612
  Requires-Dist: pypandoc ; extra == 'server'
613
613
  Requires-Dist: pypandoc-binary ; extra == 'server'
614
614
  Requires-Dist: pytest ; extra == 'server'
615
- Requires-Dist: python-dateutil ~=2.8.2 ; extra == 'server'
615
+ Requires-Dist: python-dateutil ~=2.9.0 ; extra == 'server'
616
616
  Requires-Dist: python-docx ; extra == 'server'
617
617
  Requires-Dist: python-jwt ==4.1.0 ; extra == 'server'
618
618
  Requires-Dist: pyxnat ==1.5.* ; extra == 'server'