Fix an issue with the openscap module

Fixes an issue with the openscap module where it would fail if the
command returned an exit code not found in the exit_codes_map
dictionary. Instead of failing, let's return false with the exit code
and error message in the return.
This commit is contained in:
Shane Lee 2023-11-20 16:01:55 -07:00 committed by Daniel Wozniak
parent cc2ec3bf3e
commit 9501122807
4 changed files with 214 additions and 214 deletions

2
changelog/65193.fixed.md Normal file
View file

@ -0,0 +1,2 @@
Fix issue with openscap when the error was outside the expected scope. It now
returns failed with the error code and the error

View file

@ -92,8 +92,8 @@ def xccdf(params):
tempdir = tempfile.mkdtemp()
proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, cwd=tempdir)
(stdoutdata, error) = proc.communicate()
success = _OSCAP_EXIT_CODES_MAP[proc.returncode]
returncode = proc.returncode
success = _OSCAP_EXIT_CODES_MAP.get(returncode, False)
if success:
__salt__["cp.push_dir"](tempdir)
shutil.rmtree(tempdir, ignore_errors=True)

View file

@ -0,0 +1,211 @@
import subprocess
import pytest
import salt.modules.openscap as openscap
from tests.support.mock import MagicMock, Mock, patch
@pytest.fixture
def policy_file():
yield "/usr/share/openscap/policy-file-xccdf.xml"
@pytest.fixture
def configure_loader_modules(tmp_path):
return {
openscap: {
"__salt__": MagicMock(),
}
}
def test_openscap_xccdf_eval_success(policy_file, tmp_path):
patch_rmtree = patch("shutil.rmtree", Mock())
mock_mkdtemp = Mock(return_value=str(tmp_path))
patch_mkdtemp = patch("tempfile.mkdtemp", mock_mkdtemp)
mock_popen = MagicMock(
return_value=Mock(**{"returncode": 0, "communicate.return_value": ("", "")})
)
patch_popen = patch.object(openscap, "Popen", mock_popen)
with patch_popen, patch_rmtree, patch_mkdtemp:
response = openscap.xccdf("eval --profile Default {}".format(policy_file))
assert mock_mkdtemp.call_count == 1
expected_cmd = [
"oscap",
"xccdf",
"eval",
"--oval-results",
"--results",
"results.xml",
"--report",
"report.html",
"--profile",
"Default",
policy_file,
]
openscap.Popen.assert_called_once_with(
expected_cmd,
cwd=openscap.tempfile.mkdtemp.return_value,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
openscap.__salt__["cp.push_dir"].assert_called_once_with(str(tmp_path))
assert openscap.shutil.rmtree.call_count == 1
expected = {
"upload_dir": str(tmp_path),
"error": "",
"success": True,
"returncode": 0,
}
assert response == expected
def test_openscap_xccdf_eval_success_with_failing_rules(policy_file, tmp_path):
patch_rmtree = patch("shutil.rmtree", Mock())
mock_mkdtemp = Mock(return_value=str(tmp_path))
patch_mkdtemp = patch("tempfile.mkdtemp", mock_mkdtemp)
mock_popen = MagicMock(
return_value=Mock(
**{"returncode": 2, "communicate.return_value": ("", "some error")}
)
)
patch_popen = patch.object(openscap, "Popen", mock_popen)
with patch_popen, patch_mkdtemp, patch_rmtree as mock_rmtree:
response = openscap.xccdf("eval --profile Default {}".format(policy_file))
assert mock_mkdtemp.call_count == 1
expected_cmd = [
"oscap",
"xccdf",
"eval",
"--oval-results",
"--results",
"results.xml",
"--report",
"report.html",
"--profile",
"Default",
policy_file,
]
openscap.Popen.assert_called_once_with(
expected_cmd,
cwd=openscap.tempfile.mkdtemp.return_value,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
openscap.__salt__["cp.push_dir"].assert_called_once_with(str(tmp_path))
assert mock_rmtree.call_count == 1
expected = {
"upload_dir": str(tmp_path),
"error": "some error",
"success": True,
"returncode": 2,
}
assert response == expected
def test_openscap_xccdf_eval_fail_no_profile():
response = openscap.xccdf("eval --param Default /unknown/param")
error = "the following arguments are required: --profile"
expected = {
"error": error,
"upload_dir": None,
"success": False,
"returncode": None,
}
assert response == expected
def test_openscap_xccdf_eval_success_ignore_unknown_params(tmp_path):
mock_mkdtemp = Mock(return_value=str(tmp_path))
patch_mkdtemp = patch("tempfile.mkdtemp", mock_mkdtemp)
mock_popen = MagicMock(
return_value=Mock(
**{"returncode": 2, "communicate.return_value": ("", "some error")}
)
)
patch_popen = patch("salt.modules.openscap.Popen", mock_popen)
with patch_popen, patch_mkdtemp:
response = openscap.xccdf("eval --profile Default --param Default /policy/file")
expected = {
"upload_dir": str(tmp_path),
"error": "some error",
"success": True,
"returncode": 2,
}
assert response == expected
expected_cmd = [
"oscap",
"xccdf",
"eval",
"--oval-results",
"--results",
"results.xml",
"--report",
"report.html",
"--profile",
"Default",
"/policy/file",
]
openscap.Popen.assert_called_once_with(
expected_cmd,
cwd=openscap.tempfile.mkdtemp.return_value,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
def test_openscap_xccdf_eval_evaluation_error(policy_file):
mock_popen = MagicMock(
return_value=Mock(
**{
"returncode": 1,
"communicate.return_value": ("", "evaluation error"),
}
)
)
patch_popen = patch("salt.modules.openscap.Popen", mock_popen)
with patch_popen:
response = openscap.xccdf("eval --profile Default {}".format(policy_file))
expected = {
"upload_dir": None,
"error": "evaluation error",
"success": False,
"returncode": 1,
}
assert response == expected
def test_openscap_xccdf_eval_fail_not_implemented_action(policy_file):
response = openscap.xccdf("info {}".format(policy_file))
mock_err = "argument action: invalid choice: 'info' (choose from 'eval')"
expected = {
"upload_dir": None,
"error": mock_err,
"success": False,
"returncode": None,
}
assert response == expected
def test_openscap_xccdf_eval_evaluation_unknown_error(policy_file):
mock_popen = MagicMock(
return_value=Mock(
**{
"returncode": 255,
"communicate.return_value": ("", "unknown error"),
}
)
)
patch_popen = patch("salt.modules.openscap.Popen", mock_popen)
with patch_popen:
response = openscap.xccdf("eval --profile Default {}".format(policy_file))
expected = {
"upload_dir": None,
"error": "unknown error",
"success": False,
"returncode": 255,
}
assert response == expected

View file

@ -1,213 +0,0 @@
import subprocess
import salt.modules.openscap as openscap
from tests.support.mock import MagicMock, Mock, patch
from tests.support.unit import TestCase
class OpenscapTestCase(TestCase):
random_temp_dir = "/tmp/unique-name"
policy_file = "/usr/share/openscap/policy-file-xccdf.xml"
def setUp(self):
import salt.modules.openscap
salt.modules.openscap.__salt__ = MagicMock()
patchers = [
patch("salt.modules.openscap.__salt__", MagicMock()),
patch("salt.modules.openscap.shutil.rmtree", Mock()),
patch(
"salt.modules.openscap.tempfile.mkdtemp",
Mock(return_value=self.random_temp_dir),
),
]
for patcher in patchers:
self.apply_patch(patcher)
def apply_patch(self, patcher):
patcher.start()
self.addCleanup(patcher.stop)
def test_openscap_xccdf_eval_success(self):
with patch(
"salt.modules.openscap.Popen",
MagicMock(
return_value=Mock(
**{"returncode": 0, "communicate.return_value": ("", "")}
)
),
):
response = openscap.xccdf(
"eval --profile Default {}".format(self.policy_file)
)
self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
expected_cmd = [
"oscap",
"xccdf",
"eval",
"--oval-results",
"--results",
"results.xml",
"--report",
"report.html",
"--profile",
"Default",
self.policy_file,
]
openscap.Popen.assert_called_once_with(
expected_cmd,
cwd=openscap.tempfile.mkdtemp.return_value,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
openscap.__salt__["cp.push_dir"].assert_called_once_with(
self.random_temp_dir
)
self.assertEqual(openscap.shutil.rmtree.call_count, 1)
self.assertEqual(
response,
{
"upload_dir": self.random_temp_dir,
"error": "",
"success": True,
"returncode": 0,
},
)
def test_openscap_xccdf_eval_success_with_failing_rules(self):
with patch(
"salt.modules.openscap.Popen",
MagicMock(
return_value=Mock(
**{"returncode": 2, "communicate.return_value": ("", "some error")}
)
),
):
response = openscap.xccdf(
"eval --profile Default {}".format(self.policy_file)
)
self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1)
expected_cmd = [
"oscap",
"xccdf",
"eval",
"--oval-results",
"--results",
"results.xml",
"--report",
"report.html",
"--profile",
"Default",
self.policy_file,
]
openscap.Popen.assert_called_once_with(
expected_cmd,
cwd=openscap.tempfile.mkdtemp.return_value,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
openscap.__salt__["cp.push_dir"].assert_called_once_with(
self.random_temp_dir
)
self.assertEqual(openscap.shutil.rmtree.call_count, 1)
self.assertEqual(
response,
{
"upload_dir": self.random_temp_dir,
"error": "some error",
"success": True,
"returncode": 2,
},
)
def test_openscap_xccdf_eval_fail_no_profile(self):
response = openscap.xccdf("eval --param Default /unknown/param")
error = "the following arguments are required: --profile"
self.assertEqual(
response,
{"error": error, "upload_dir": None, "success": False, "returncode": None},
)
def test_openscap_xccdf_eval_success_ignore_unknown_params(self):
with patch(
"salt.modules.openscap.Popen",
MagicMock(
return_value=Mock(
**{"returncode": 2, "communicate.return_value": ("", "some error")}
)
),
):
response = openscap.xccdf(
"eval --profile Default --param Default /policy/file"
)
self.assertEqual(
response,
{
"upload_dir": self.random_temp_dir,
"error": "some error",
"success": True,
"returncode": 2,
},
)
expected_cmd = [
"oscap",
"xccdf",
"eval",
"--oval-results",
"--results",
"results.xml",
"--report",
"report.html",
"--profile",
"Default",
"/policy/file",
]
openscap.Popen.assert_called_once_with(
expected_cmd,
cwd=openscap.tempfile.mkdtemp.return_value,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
def test_openscap_xccdf_eval_evaluation_error(self):
with patch(
"salt.modules.openscap.Popen",
MagicMock(
return_value=Mock(
**{
"returncode": 1,
"communicate.return_value": ("", "evaluation error"),
}
)
),
):
response = openscap.xccdf(
"eval --profile Default {}".format(self.policy_file)
)
self.assertEqual(
response,
{
"upload_dir": None,
"error": "evaluation error",
"success": False,
"returncode": 1,
},
)
def test_openscap_xccdf_eval_fail_not_implemented_action(self):
response = openscap.xccdf("info {}".format(self.policy_file))
mock_err = "argument action: invalid choice: 'info' (choose from 'eval')"
self.assertEqual(
response,
{
"upload_dir": None,
"error": mock_err,
"success": False,
"returncode": None,
},
)