regscale-cli 6.20.4.1__py3-none-any.whl → 6.20.5.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 (33) hide show
  1. regscale/__init__.py +1 -1
  2. regscale/core/app/internal/model_editor.py +3 -3
  3. regscale/core/app/utils/regscale_utils.py +37 -0
  4. regscale/core/utils/date.py +26 -3
  5. regscale/integrations/commercial/defender.py +3 -0
  6. regscale/integrations/commercial/qualys/__init__.py +40 -14
  7. regscale/integrations/commercial/qualys/containers.py +324 -0
  8. regscale/integrations/commercial/qualys/scanner.py +203 -8
  9. regscale/integrations/commercial/synqly/edr.py +10 -0
  10. regscale/integrations/commercial/wizv2/click.py +2 -2
  11. regscale/integrations/commercial/wizv2/constants.py +13 -0
  12. regscale/integrations/commercial/wizv2/issue.py +3 -2
  13. regscale/integrations/commercial/wizv2/scanner.py +5 -1
  14. regscale/integrations/commercial/wizv2/utils.py +118 -72
  15. regscale/integrations/public/fedramp/fedramp_cis_crm.py +98 -20
  16. regscale/models/integration_models/cisa_kev_data.json +140 -3
  17. regscale/models/integration_models/synqly_models/capabilities.json +1 -1
  18. regscale/models/regscale_models/catalog.py +16 -0
  19. regscale/models/regscale_models/file.py +2 -1
  20. regscale/models/regscale_models/form_field_value.py +59 -1
  21. regscale/models/regscale_models/issue.py +47 -0
  22. regscale/models/regscale_models/organization.py +30 -0
  23. regscale/models/regscale_models/regscale_model.py +13 -5
  24. regscale/models/regscale_models/security_control.py +47 -0
  25. regscale/models/regscale_models/security_plan.py +32 -0
  26. {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.5.0.dist-info}/METADATA +1 -1
  27. {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.5.0.dist-info}/RECORD +33 -31
  28. tests/fixtures/test_fixture.py +33 -4
  29. tests/regscale/core/test_app.py +53 -32
  30. {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.5.0.dist-info}/LICENSE +0 -0
  31. {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.5.0.dist-info}/WHEEL +0 -0
  32. {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.5.0.dist-info}/entry_points.txt +0 -0
  33. {regscale_cli-6.20.4.1.dist-info → regscale_cli-6.20.5.0.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,7 @@ from typing import Optional, Union
12
12
  import pytest
13
13
  import yaml
14
14
 
15
+ from regscale.models import SecurityPlan
15
16
  from regscale.core.app.api import Api
16
17
  from regscale.core.app.application import Application
17
18
  from regscale.core.app.internal.login import login
@@ -28,6 +29,7 @@ class CLITestFixture:
28
29
  config: dict
29
30
  logger: Logger
30
31
  title_prefix: str
32
+ security_plan: SecurityPlan
31
33
 
32
34
  def update_config_with_env(self):
33
35
  """
@@ -103,8 +105,20 @@ class CLITestFixture:
103
105
  cwd = Path(os.getcwd())
104
106
  return cwd / suffix if suffix else cwd
105
107
 
108
+ @pytest.fixture(scope="session")
109
+ def generate_uuid(self) -> str:
110
+ """
111
+ Generate a string with the python version and random UUID
112
+
113
+ :return: A string with the python version and random UUID
114
+ :rtype: str
115
+ """
116
+ python_info = f"Python{sys.version_info.major}.{sys.version_info.minor}"
117
+ random_id = uuid.uuid1()
118
+ return f"{python_info} Test {random_id}"
119
+
106
120
  @pytest.fixture(autouse=True)
107
- def cli_test_fixture(self, request):
121
+ def cli_test_fixture(self, request, generate_uuid):
108
122
  """
109
123
  Test fixture for the CLI application
110
124
 
@@ -124,9 +138,7 @@ class CLITestFixture:
124
138
  test_config = {**config_dict, **self.app.config}
125
139
  self.app.save_config(test_config)
126
140
  self.config = self.app.config
127
- python_info = f"Python{sys.version_info.major}.{sys.version_info.minor}"
128
- random_id = uuid.uuid3(uuid.NAMESPACE_DNS, python_info)
129
- self.title_prefix = f"{python_info} Test {random_id} - "
141
+ self.title_prefix = generate_uuid
130
142
  # login with token if available
131
143
  if token := os.getenv("REGSCALE_TOKEN") or self.config.get("token"):
132
144
  login(token=token, app=self.app)
@@ -142,3 +154,20 @@ class CLITestFixture:
142
154
  logger.info(f"Test Setup Complete: {self.title_prefix}")
143
155
  # Test Execution
144
156
  yield self.app
157
+
158
+ @pytest.fixture(scope="class")
159
+ def create_security_plan(self, request, generate_uuid):
160
+ """Create a security plan for testing"""
161
+ security_plan = None
162
+ try:
163
+ ssp = SecurityPlan(
164
+ systemName=generate_uuid,
165
+ description="Test SSP",
166
+ )
167
+ security_plan = ssp.create()
168
+
169
+ yield security_plan
170
+ finally:
171
+ # Cleanup after all tests in the class are done
172
+ if security_plan:
173
+ security_plan.delete()
@@ -4,6 +4,7 @@ Test the Application class used by the RegScale CLI.
4
4
 
5
5
  import os
6
6
  from unittest.mock import patch, MagicMock
7
+ import pytest
7
8
 
8
9
  from requests import Response
9
10
 
@@ -18,6 +19,13 @@ class TestApplication:
18
19
  test_domain = "https://example.com"
19
20
  test_token = "Bearer test_token"
20
21
 
22
+ @pytest.fixture(autouse=True)
23
+ def save_config(self):
24
+ original_conf = self.app.config
25
+ yield
26
+ self.app.config = original_conf
27
+ self.app.save_config(original_conf)
28
+
21
29
  def teardown_method(self, method):
22
30
  """
23
31
  Remove the test config file after each test
@@ -60,13 +68,15 @@ class TestApplication:
60
68
  mock_response = MagicMock()
61
69
  mock_response.json.return_value = {"cliConfig": "key: value"}
62
70
  mock_get.return_value = mock_response
63
- os.environ["REGSCALE_DOMAIN"] = self.test_domain
64
- os.environ["REGSCALE_TOKEN"] = self.test_token
65
- config = self.app._fetch_config_from_regscale(config={})
66
- assert config["domain"] == self.test_domain
67
- assert config["token"] == self.test_token
68
- assert config["userId"] == "test_user_id"
69
- assert config["key"] == "value"
71
+ envars = os.environ.copy()
72
+ envars["REGSCALE_DOMAIN"] = self.test_domain
73
+ envars["REGSCALE_TOKEN"] = self.test_token
74
+ with patch.dict(os.environ, envars, clear=True):
75
+ config = self.app._fetch_config_from_regscale(config={})
76
+ assert config["domain"] == self.test_domain
77
+ assert config["token"] == self.test_token
78
+ assert config["userId"] == "test_user_id"
79
+ assert config["key"] == "value"
70
80
 
71
81
  @patch("requests.get")
72
82
  def test_fetch_config_from_regscale_failure(self, mock_get):
@@ -74,14 +84,16 @@ class TestApplication:
74
84
  mock_response.json.return_value = "Not found."
75
85
  mock_response.status_code = 404
76
86
  mock_get.return_value = mock_response
77
- os.environ["REGSCALE_DOMAIN"] = self.test_domain
78
- os.environ["REGSCALE_TOKEN"] = self.test_token
79
- empty_config = self.app._fetch_config_from_regscale()
80
- assert empty_config == {}
81
- with patch.object(self.app.logger, "error") as mock_logger_error:
82
- config = self.app._fetch_config_from_regscale(config=self.app.config)
83
- mock_logger_error.assert_called_once()
84
- assert config == {}
87
+ envars = os.environ.copy()
88
+ envars["REGSCALE_DOMAIN"] = self.test_domain
89
+ envars["REGSCALE_TOKEN"] = self.test_token
90
+ with patch.dict(os.environ, envars, clear=True):
91
+ empty_config = self.app._fetch_config_from_regscale()
92
+ assert empty_config == {}
93
+ with patch.object(self.app.logger, "error") as mock_logger_error:
94
+ config = self.app._fetch_config_from_regscale(config=self.app.config)
95
+ mock_logger_error.assert_called_once()
96
+ assert config == {}
85
97
 
86
98
  def test_gen_config(self):
87
99
  self.app.local_config = True
@@ -158,26 +170,35 @@ class TestApplication:
158
170
  assert result == {"key": "value"}
159
171
 
160
172
  def test_get_airflow_config_with_env_vars(self):
161
- os.environ["REGSCALE_TOKEN"] = self.test_token
162
- os.environ["REGSCALE_DOMAIN"] = self.test_domain
163
- with patch.object(self.app, "_fetch_config_from_regscale", return_value={"key": "value"}):
164
- result = self.app._get_airflow_config()
165
- assert result == {"key": "value"}
173
+ envars = os.environ.copy()
174
+ envars["REGSCALE_TOKEN"] = self.test_token
175
+ envars["REGSCALE_DOMAIN"] = self.test_domain
176
+ with patch.dict(os.environ, envars, clear=True):
177
+ with patch.object(self.app, "_fetch_config_from_regscale", return_value={"key": "value"}):
178
+ result = self.app._get_airflow_config()
179
+ assert result == {"key": "value"}
166
180
 
167
181
  def test_get_airflow_config_no_config(self):
168
- os.environ["REGSCALE_TOKEN"] = ""
169
- os.environ["REGSCALE_DOMAIN"] = ""
170
- result = self.app._get_airflow_config()
171
- assert result is None
172
-
173
- os.environ["REGSCALE_TOKEN"] = self.test_token
174
- os.environ["REGSCALE_DOMAIN"] = self.test_domain
175
- with patch.object(
176
- self.app, "_fetch_config_from_regscale", return_value={"token": self.test_token, "domain": self.test_domain}
177
- ):
182
+ envars = os.environ.copy()
183
+ envars.pop("REGSCALE_TOKEN")
184
+ envars.pop("REGSCALE_DOMAIN")
185
+ assert envars.get("REGSCALE_TOKEN") is None
186
+ assert envars.get("REGSCALE_DOMAIN") is None
187
+ with patch.dict(os.environ, envars, clear=True):
178
188
  result = self.app._get_airflow_config()
179
- assert result["token"] == self.test_token
180
- assert result["domain"] == self.test_domain
189
+ assert result is None
190
+
191
+ envars["REGSCALE_TOKEN"] = self.test_token
192
+ envars["REGSCALE_DOMAIN"] = self.test_domain
193
+ with patch.dict(os.environ, envars, clear=True):
194
+ with patch.object(
195
+ self.app,
196
+ "_fetch_config_from_regscale",
197
+ return_value={"token": self.test_token, "domain": self.test_domain},
198
+ ):
199
+ result = self.app._get_airflow_config()
200
+ assert result["token"] == self.test_token
201
+ assert result["domain"] == self.test_domain
181
202
 
182
203
  def test_get_airflow_config_invalid_json(self):
183
204
  config = "{'key': 'value'"