regscale-cli 6.26.0.0__py3-none-any.whl → 6.27.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 (95) hide show
  1. regscale/_version.py +1 -1
  2. regscale/core/app/application.py +1 -1
  3. regscale/core/app/internal/evidence.py +419 -2
  4. regscale/dev/code_gen.py +24 -20
  5. regscale/integrations/commercial/jira.py +367 -126
  6. regscale/integrations/commercial/qualys/__init__.py +7 -8
  7. regscale/integrations/commercial/qualys/scanner.py +8 -3
  8. regscale/integrations/commercial/synqly/assets.py +17 -0
  9. regscale/integrations/commercial/synqly/vulnerabilities.py +45 -28
  10. regscale/integrations/commercial/tenablev2/cis_parsers.py +453 -0
  11. regscale/integrations/commercial/tenablev2/cis_scanner.py +447 -0
  12. regscale/integrations/commercial/tenablev2/commands.py +142 -1
  13. regscale/integrations/commercial/tenablev2/scanner.py +0 -1
  14. regscale/integrations/commercial/tenablev2/stig_parsers.py +113 -57
  15. regscale/integrations/commercial/wizv2/WizDataMixin.py +1 -1
  16. regscale/integrations/commercial/wizv2/click.py +44 -59
  17. regscale/integrations/commercial/wizv2/compliance/__init__.py +15 -0
  18. regscale/integrations/commercial/wizv2/{policy_compliance_helpers.py → compliance/helpers.py} +78 -60
  19. regscale/integrations/commercial/wizv2/compliance_report.py +10 -9
  20. regscale/integrations/commercial/wizv2/core/__init__.py +133 -0
  21. regscale/integrations/commercial/wizv2/{async_client.py → core/client.py} +3 -3
  22. regscale/integrations/commercial/wizv2/{constants.py → core/constants.py} +1 -17
  23. regscale/integrations/commercial/wizv2/core/file_operations.py +237 -0
  24. regscale/integrations/commercial/wizv2/fetchers/__init__.py +11 -0
  25. regscale/integrations/commercial/wizv2/{data_fetcher.py → fetchers/policy_assessment.py} +5 -9
  26. regscale/integrations/commercial/wizv2/issue.py +1 -1
  27. regscale/integrations/commercial/wizv2/models/__init__.py +0 -0
  28. regscale/integrations/commercial/wizv2/parsers/__init__.py +34 -0
  29. regscale/integrations/commercial/wizv2/{parsers.py → parsers/main.py} +1 -1
  30. regscale/integrations/commercial/wizv2/processors/__init__.py +11 -0
  31. regscale/integrations/commercial/wizv2/{finding_processor.py → processors/finding.py} +1 -1
  32. regscale/integrations/commercial/wizv2/reports.py +1 -1
  33. regscale/integrations/commercial/wizv2/sbom.py +1 -1
  34. regscale/integrations/commercial/wizv2/scanner.py +40 -100
  35. regscale/integrations/commercial/wizv2/utils/__init__.py +48 -0
  36. regscale/integrations/commercial/wizv2/{utils.py → utils/main.py} +116 -61
  37. regscale/integrations/commercial/wizv2/variables.py +89 -3
  38. regscale/integrations/compliance_integration.py +0 -46
  39. regscale/integrations/control_matcher.py +22 -3
  40. regscale/integrations/due_date_handler.py +14 -8
  41. regscale/integrations/public/fedramp/docx_parser.py +10 -1
  42. regscale/integrations/public/fedramp/fedramp_cis_crm.py +393 -340
  43. regscale/integrations/public/fedramp/fedramp_five.py +1 -1
  44. regscale/integrations/scanner_integration.py +127 -57
  45. regscale/models/integration_models/cisa_kev_data.json +132 -9
  46. regscale/models/integration_models/qualys.py +3 -4
  47. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  48. regscale/models/integration_models/synqly_models/connectors/vulnerabilities.py +24 -7
  49. regscale/models/integration_models/synqly_models/synqly_model.py +8 -1
  50. regscale/models/regscale_models/control_implementation.py +1 -1
  51. regscale/models/regscale_models/issue.py +0 -1
  52. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/METADATA +1 -17
  53. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/RECORD +93 -60
  54. tests/regscale/integrations/commercial/test_jira.py +481 -91
  55. tests/regscale/integrations/commercial/test_wiz.py +96 -200
  56. tests/regscale/integrations/commercial/wizv2/__init__.py +1 -1
  57. tests/regscale/integrations/commercial/wizv2/compliance/__init__.py +1 -0
  58. tests/regscale/integrations/commercial/wizv2/compliance/test_helpers.py +903 -0
  59. tests/regscale/integrations/commercial/wizv2/core/__init__.py +1 -0
  60. tests/regscale/integrations/commercial/wizv2/core/test_auth.py +701 -0
  61. tests/regscale/integrations/commercial/wizv2/core/test_client.py +1037 -0
  62. tests/regscale/integrations/commercial/wizv2/core/test_file_operations.py +989 -0
  63. tests/regscale/integrations/commercial/wizv2/fetchers/__init__.py +1 -0
  64. tests/regscale/integrations/commercial/wizv2/fetchers/test_policy_assessment.py +805 -0
  65. tests/regscale/integrations/commercial/wizv2/parsers/__init__.py +1 -0
  66. tests/regscale/integrations/commercial/wizv2/parsers/test_main.py +1153 -0
  67. tests/regscale/integrations/commercial/wizv2/processors/__init__.py +1 -0
  68. tests/regscale/integrations/commercial/wizv2/processors/test_finding.py +671 -0
  69. tests/regscale/integrations/commercial/wizv2/test_WizDataMixin.py +537 -0
  70. tests/regscale/integrations/commercial/wizv2/test_click_comprehensive.py +851 -0
  71. tests/regscale/integrations/commercial/wizv2/test_compliance_report_comprehensive.py +910 -0
  72. tests/regscale/integrations/commercial/wizv2/test_file_cleanup.py +283 -0
  73. tests/regscale/integrations/commercial/wizv2/test_file_operations.py +260 -0
  74. tests/regscale/integrations/commercial/wizv2/test_issue.py +1 -1
  75. tests/regscale/integrations/commercial/wizv2/test_issue_comprehensive.py +1203 -0
  76. tests/regscale/integrations/commercial/wizv2/test_reports.py +497 -0
  77. tests/regscale/integrations/commercial/wizv2/test_sbom.py +643 -0
  78. tests/regscale/integrations/commercial/wizv2/test_scanner_comprehensive.py +805 -0
  79. tests/regscale/integrations/commercial/wizv2/test_wiz_click_client_id.py +1 -1
  80. tests/regscale/integrations/commercial/wizv2/test_wiz_compliance_report.py +72 -29
  81. tests/regscale/integrations/commercial/wizv2/test_wiz_findings_comprehensive.py +364 -0
  82. tests/regscale/integrations/commercial/wizv2/test_wiz_inventory_comprehensive.py +644 -0
  83. tests/regscale/integrations/commercial/wizv2/test_wizv2.py +946 -78
  84. tests/regscale/integrations/commercial/wizv2/test_wizv2_utils.py +97 -202
  85. tests/regscale/integrations/commercial/wizv2/utils/__init__.py +1 -0
  86. tests/regscale/integrations/commercial/wizv2/utils/test_main.py +1523 -0
  87. tests/regscale/integrations/public/test_fedramp.py +301 -0
  88. tests/regscale/integrations/test_control_matcher.py +83 -0
  89. regscale/integrations/commercial/wizv2/policy_compliance.py +0 -3543
  90. tests/regscale/integrations/commercial/wizv2/test_wiz_policy_compliance.py +0 -750
  91. /regscale/integrations/commercial/wizv2/{wiz_auth.py → core/auth.py} +0 -0
  92. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/LICENSE +0 -0
  93. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/WHEEL +0 -0
  94. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/entry_points.txt +0 -0
  95. {regscale_cli-6.26.0.0.dist-info → regscale_cli-6.27.0.0.dist-info}/top_level.txt +0 -0
@@ -49,6 +49,7 @@ class ControlMatcher:
49
49
  Handles various formats:
50
50
  - NIST format: AC-1, AC-1(1), AC-1.1
51
51
  - With leading zeros: AC-01, AC-17(02)
52
+ - With spaces: AC-1 (1), AC-02 (04)
52
53
  - With text: "Access Control AC-1"
53
54
  - Multiple controls: "AC-1, AC-2"
54
55
 
@@ -63,14 +64,32 @@ class ControlMatcher:
63
64
  control_string = control_string.strip().upper()
64
65
 
65
66
  # Common NIST control ID pattern
66
- # Matches: AC-1, AC-01, AC-1(1), AC-1(01), AC-1.1, AC-1.01, etc.
67
- pattern = r"([A-Z]{2,3}-\d+(?:\(\d+\)|\.\d+)?)"
67
+ # Matches: AC-1, AC-01, AC-1(1), AC-1(01), AC-1 (1), AC-1.1, AC-1.01, etc.
68
+ # Allows optional whitespace before and inside parentheses
69
+ pattern = r"([A-Z]{2,3}-\d+(?:\s*\(\s*\d+\s*\)|\.\d+)?)"
68
70
 
69
71
  matches = re.findall(pattern, control_string)
70
72
  if matches:
71
- # Normalize parentheses to dots for consistency
73
+ # Normalize parentheses to dots for consistency and remove spaces
72
74
  control_id = matches[0]
75
+ control_id = control_id.replace(" ", "") # Remove all spaces
73
76
  control_id = control_id.replace("(", ".").replace(")", "")
77
+
78
+ # Normalize leading zeros (e.g., AC-01.02 -> AC-1.2)
79
+ parts = control_id.split("-")
80
+ if len(parts) == 2:
81
+ family = parts[0]
82
+ number_part = parts[1]
83
+
84
+ if "." in number_part:
85
+ main_num, enhancement = number_part.split(".", 1)
86
+ main_num = str(int(main_num))
87
+ enhancement = str(int(enhancement))
88
+ control_id = f"{family}-{main_num}.{enhancement}"
89
+ else:
90
+ main_num = str(int(number_part))
91
+ control_id = f"{family}-{main_num}"
92
+
74
93
  return control_id
75
94
 
76
95
  return None
@@ -4,7 +4,7 @@
4
4
 
5
5
  import logging
6
6
  from datetime import datetime
7
- from typing import Any, Dict, Optional
7
+ from typing import Any, Dict, Optional, Union
8
8
 
9
9
  from regscale.core.app.application import Application
10
10
  from regscale.core.utils.date import get_day_increment
@@ -53,9 +53,13 @@ class DueDateHandler:
53
53
  # Default due date timelines (days)
54
54
  self.default_timelines = {
55
55
  regscale_models.IssueSeverity.Critical: 30,
56
+ "critical": 30,
56
57
  regscale_models.IssueSeverity.High: 60,
58
+ "high": 60,
57
59
  regscale_models.IssueSeverity.Moderate: 120,
60
+ "moderate": 120,
58
61
  regscale_models.IssueSeverity.Low: 364,
62
+ "low": 364,
59
63
  }
60
64
 
61
65
  # Load integration-specific timelines from config
@@ -66,10 +70,10 @@ class DueDateHandler:
66
70
 
67
71
  def _load_integration_timelines(self) -> Dict[regscale_models.IssueSeverity, int]:
68
72
  """
69
- Load timeline configurations for this integration from init.yaml
70
-
71
- :return: Dictionary mapping severity to days
72
- :rtype: Dict[regscale_models.IssueSeverity, int]
73
+ Load timeline configurations for this integration from init.yaml
74
+ mv
75
+ :return: Dictionary mapping severity to days
76
+ :rtype: Dict[regscale_models.IssueSeverity, int]
73
77
  """
74
78
  timelines = self.default_timelines.copy()
75
79
 
@@ -184,7 +188,7 @@ class DueDateHandler:
184
188
 
185
189
  def calculate_due_date(
186
190
  self,
187
- severity: regscale_models.IssueSeverity,
191
+ severity: Union[regscale_models.IssueSeverity, str],
188
192
  created_date: str,
189
193
  cve: Optional[str] = None,
190
194
  title: Optional[str] = None,
@@ -192,7 +196,7 @@ class DueDateHandler:
192
196
  """
193
197
  Calculate the due date for an issue based on severity, KEV status, and integration config
194
198
 
195
- :param regscale_models.IssueSeverity severity: The severity of the issue
199
+ :param Union[regscale_models.IssueSeverity, str] severity: The severity of the issue
196
200
  :param str created_date: The creation date of the issue
197
201
  :param Optional[str] cve: The CVE identifier (if applicable)
198
202
  :param Optional[str] title: The title of the issue (for additional context)
@@ -244,7 +248,9 @@ class DueDateHandler:
244
248
  # Ensure due date is never in the past (allow yesterday for timezone differences)
245
249
  due_date = self._ensure_future_due_date(calculated_due_date, days)
246
250
 
247
- logger.debug(f"Using {self.integration_name} timeline: {severity.name} = {days} days, due date = {due_date}")
251
+ logger.debug(
252
+ f"Using {self.integration_name} timeline: {severity.name if isinstance(severity, regscale_models.IssueSeverity) else severity} = {days} days, due date = {due_date}"
253
+ )
248
254
 
249
255
  return due_date
250
256
 
@@ -246,7 +246,16 @@ class SSPDocParser:
246
246
  headers = self.extract_table_headers(table, namespaces)
247
247
  table_data = []
248
248
  tables_dicts_list["preceding_text"] = preceding_text[i] if i < len(preceding_text) else ""
249
- if len(headers) == 1 and headers[0].lower() in vertical_tables:
249
+ # Check if this is a vertical table:
250
+ is_vertical = False
251
+ # - Single header that matches vertical_tables list
252
+ first_option = len(headers) == 1 and headers[0].lower() in vertical_tables
253
+ # - OR two headers where first matches and second is empty/whitespace
254
+ second_option = len(headers) == 2 and headers[0].lower() in vertical_tables and not headers[1].strip()
255
+ if first_option or second_option:
256
+ is_vertical = True
257
+
258
+ if is_vertical:
250
259
  table_data = self.extract_vertical_row_data(table, namespaces)
251
260
  tables_dicts_list["table_data"] = table_data
252
261
  # tables_dicts_list.append({headers[0].lower(): table_data})