regscale-cli 6.20.4.0__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.
- regscale/__init__.py +1 -1
- regscale/core/app/internal/model_editor.py +3 -3
- regscale/core/app/utils/regscale_utils.py +37 -0
- regscale/core/utils/date.py +26 -3
- regscale/integrations/commercial/defender.py +3 -0
- regscale/integrations/commercial/qualys/__init__.py +40 -14
- regscale/integrations/commercial/qualys/containers.py +324 -0
- regscale/integrations/commercial/qualys/scanner.py +203 -8
- regscale/integrations/commercial/synqly/edr.py +10 -0
- regscale/integrations/commercial/wizv2/click.py +2 -2
- regscale/integrations/commercial/wizv2/constants.py +13 -0
- regscale/integrations/commercial/wizv2/issue.py +3 -2
- regscale/integrations/commercial/wizv2/scanner.py +5 -1
- regscale/integrations/commercial/wizv2/utils.py +118 -72
- regscale/integrations/public/fedramp/fedramp_cis_crm.py +107 -22
- regscale/models/integration_models/cisa_kev_data.json +140 -3
- regscale/models/integration_models/synqly_models/capabilities.json +1 -1
- regscale/models/regscale_models/catalog.py +16 -0
- regscale/models/regscale_models/file.py +2 -1
- regscale/models/regscale_models/form_field_value.py +59 -1
- regscale/models/regscale_models/issue.py +47 -0
- regscale/models/regscale_models/organization.py +30 -0
- regscale/models/regscale_models/regscale_model.py +13 -5
- regscale/models/regscale_models/security_control.py +47 -0
- regscale/models/regscale_models/security_plan.py +32 -0
- {regscale_cli-6.20.4.0.dist-info → regscale_cli-6.20.5.0.dist-info}/METADATA +1 -1
- {regscale_cli-6.20.4.0.dist-info → regscale_cli-6.20.5.0.dist-info}/RECORD +33 -31
- tests/fixtures/test_fixture.py +33 -4
- tests/regscale/core/test_app.py +53 -32
- {regscale_cli-6.20.4.0.dist-info → regscale_cli-6.20.5.0.dist-info}/LICENSE +0 -0
- {regscale_cli-6.20.4.0.dist-info → regscale_cli-6.20.5.0.dist-info}/WHEEL +0 -0
- {regscale_cli-6.20.4.0.dist-info → regscale_cli-6.20.5.0.dist-info}/entry_points.txt +0 -0
- {regscale_cli-6.20.4.0.dist-info → regscale_cli-6.20.5.0.dist-info}/top_level.txt +0 -0
tests/fixtures/test_fixture.py
CHANGED
|
@@ -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
|
-
|
|
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()
|
tests/regscale/core/test_app.py
CHANGED
|
@@ -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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
assert
|
|
172
|
-
|
|
173
|
-
os.environ
|
|
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
|
|
180
|
-
|
|
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'"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|