File ignore-non-utf8-characters-while-reading-files-with-.patch of Package salt

From b4945a0608b3d8996e8b5593dcc458c15b11d6ba Mon Sep 17 00:00:00 2001
From: Victor Zhestkov <Victor.Zhestkov@suse.com>
Date: Wed, 14 Sep 2022 14:57:29 +0300
Subject: [PATCH] Ignore non utf8 characters while reading files with
 core grains module (bsc#1202165)

* Ignore UnicodeDecodeError on reading files with core grains

* Add tests for non utf8 chars in cmdline

* Blacken modified lines

* Fix the tests

* Add changelog entry

* Change ignore to surrogateescape for kernelparameters

* Turn static test files to dynamic
---
 changelog/62633.fixed                  |   1 +
 salt/grains/core.py                    |  12 ++-
 tests/pytests/unit/grains/test_core.py | 118 +++++++++++++++++++++++++
 3 files changed, 128 insertions(+), 3 deletions(-)
 create mode 100644 changelog/62633.fixed

diff --git a/changelog/62633.fixed b/changelog/62633.fixed
new file mode 100644
index 0000000000..1ab74f9122
--- /dev/null
+++ b/changelog/62633.fixed
@@ -0,0 +1 @@
+Prevent possible tracebacks in core grains module by ignoring non utf8 characters in /proc/1/environ, /proc/1/cmdline, /proc/cmdline
diff --git a/salt/grains/core.py b/salt/grains/core.py
index 9530a43fc5..b543144da2 100644
--- a/salt/grains/core.py
+++ b/salt/grains/core.py
@@ -1093,7 +1093,9 @@ def _virtual(osdata):
         if ("virtual_subtype" not in grains) or (grains["virtual_subtype"] != "LXC"):
             if os.path.isfile("/proc/1/environ"):
                 try:
-                    with salt.utils.files.fopen("/proc/1/environ", "r") as fhr:
+                    with salt.utils.files.fopen(
+                        "/proc/1/environ", "r", errors="ignore"
+                    ) as fhr:
                         fhr_contents = fhr.read()
                     if "container=lxc" in fhr_contents:
                         grains["virtual"] = "container"
@@ -1911,7 +1913,9 @@ def os_data():
             grains["init"] = "systemd"
         except OSError:
             try:
-                with salt.utils.files.fopen("/proc/1/cmdline") as fhr:
+                with salt.utils.files.fopen(
+                    "/proc/1/cmdline", "r", errors="ignore"
+                ) as fhr:
                     init_cmdline = fhr.read().replace("\x00", " ").split()
             except OSError:
                 pass
@@ -3160,7 +3164,9 @@ def kernelparams():
         return {}
     else:
         try:
-            with salt.utils.files.fopen("/proc/cmdline", "r") as fhr:
+            with salt.utils.files.fopen(
+                "/proc/cmdline", "r", errors="surrogateescape"
+            ) as fhr:
                 cmdline = fhr.read()
                 grains = {"kernelparams": []}
                 for data in [
diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py
index 84dd97d62f..e640a07f76 100644
--- a/tests/pytests/unit/grains/test_core.py
+++ b/tests/pytests/unit/grains/test_core.py
@@ -11,6 +11,7 @@ import os
 import pathlib
 import platform
 import socket
+import tempfile
 import textwrap
 from collections import namedtuple
 
@@ -2635,6 +2636,38 @@ def test_kernelparams_return_linux(cmdline, expectation):
         assert core.kernelparams() == expectation
 
 
+@pytest.mark.skip_unless_on_linux
+def test_kernelparams_return_linux_non_utf8():
+    _salt_utils_files_fopen = salt.utils.files.fopen
+
+    expected = {
+        "kernelparams": [
+            ("TEST_KEY1", "VAL1"),
+            ("TEST_KEY2", "VAL2"),
+            ("BOOTABLE_FLAG", "\udc80"),
+            ("TEST_KEY_NOVAL", None),
+            ("TEST_KEY3", "3"),
+        ]
+    }
+
+    with tempfile.TemporaryDirectory() as tempdir:
+
+        def _open_mock(file_name, *args, **kwargs):
+            return _salt_utils_files_fopen(
+                os.path.join(tempdir, "cmdline"), *args, **kwargs
+            )
+
+        with salt.utils.files.fopen(
+            os.path.join(tempdir, "cmdline"),
+            "wb",
+        ) as cmdline_fh, patch("salt.utils.files.fopen", _open_mock):
+            cmdline_fh.write(
+                b'TEST_KEY1=VAL1 TEST_KEY2=VAL2 BOOTABLE_FLAG="\x80" TEST_KEY_NOVAL TEST_KEY3=3\n'
+            )
+            cmdline_fh.close()
+            assert core.kernelparams() == expected
+
+
 def test_linux_gpus():
     """
     Test GPU detection on Linux systems
@@ -2837,3 +2870,88 @@ def test_virtual_set_virtual_ec2():
 
         assert virtual_grains["virtual"] == "kvm"
         assert "virtual_subtype" not in virtual_grains
+
+
+@pytest.mark.skip_on_windows
+def test_linux_proc_files_with_non_utf8_chars():
+    _salt_utils_files_fopen = salt.utils.files.fopen
+
+    empty_mock = MagicMock(return_value={})
+
+    with tempfile.TemporaryDirectory() as tempdir:
+
+        def _mock_open(filename, *args, **kwargs):
+            return _salt_utils_files_fopen(
+                os.path.join(tempdir, "cmdline-1"), *args, **kwargs
+            )
+
+        with salt.utils.files.fopen(
+            os.path.join(tempdir, "cmdline-1"),
+            "wb",
+        ) as cmdline_fh, patch("os.path.isfile", return_value=False), patch(
+            "salt.utils.files.fopen", _mock_open
+        ), patch.dict(
+            core.__salt__,
+            {
+                "cmd.retcode": salt.modules.cmdmod.retcode,
+                "cmd.run": MagicMock(return_value=""),
+            },
+        ), patch.object(
+            core, "_linux_bin_exists", return_value=False
+        ), patch.object(
+            core, "_parse_lsb_release", return_value=empty_mock
+        ), patch.object(
+            core, "_parse_os_release", return_value=empty_mock
+        ), patch.object(
+            core, "_hw_data", return_value=empty_mock
+        ), patch.object(
+            core, "_virtual", return_value=empty_mock
+        ), patch.object(
+            core, "_bsd_cpudata", return_value=empty_mock
+        ), patch.object(
+            os, "stat", side_effect=OSError()
+        ):
+            cmdline_fh.write(
+                b"/usr/lib/systemd/systemd\x00--switched-root\x00--system\x00--deserialize\x0028\x80\x00"
+            )
+            cmdline_fh.close()
+            os_grains = core.os_data()
+            assert os_grains != {}
+
+
+@pytest.mark.skip_on_windows
+def test_virtual_linux_proc_files_with_non_utf8_chars():
+    _salt_utils_files_fopen = salt.utils.files.fopen
+
+    def _is_file_mock(filename):
+        if filename == "/proc/1/environ":
+            return True
+        return False
+
+    with tempfile.TemporaryDirectory() as tempdir:
+
+        def _mock_open(filename, *args, **kwargs):
+            return _salt_utils_files_fopen(
+                os.path.join(tempdir, "environ"), *args, **kwargs
+            )
+
+        with salt.utils.files.fopen(
+            os.path.join(tempdir, "environ"),
+            "wb",
+        ) as environ_fh, patch("os.path.isfile", _is_file_mock), patch(
+            "salt.utils.files.fopen", _mock_open
+        ), patch.object(
+            salt.utils.path, "which", MagicMock(return_value=None)
+        ), patch.dict(
+            core.__salt__,
+            {
+                "cmd.run_all": MagicMock(
+                    return_value={"retcode": 1, "stderr": "", "stdout": ""}
+                ),
+                "cmd.run": MagicMock(return_value=""),
+            },
+        ):
+            environ_fh.write(b"KEY1=VAL1 KEY2=VAL2\x80 KEY2=VAL2")
+            environ_fh.close()
+            virt_grains = core._virtual({"kernel": "Linux"})
+            assert virt_grains == {"virtual": "physical"}
-- 
2.37.3

openSUSE Build Service is sponsored by