From 7064d6bd0192f26f803b0bd7634f9189b71419ae Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Sun, 24 Sep 2023 10:23:27 +0100 Subject: [PATCH 01/13] Bump to `sqren/backport-github-action@v9.3.0-a` Signed-off-by: Pedro Algarvio --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index b70b84df5b3..4e255576c92 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -33,7 +33,7 @@ jobs: ) steps: - name: Backport Action - uses: sqren/backport-github-action@v8.9.7 + uses: sqren/backport-github-action@v9.3.0-a with: github_token: ${{ secrets.GITHUB_TOKEN }} auto_backport_label_prefix: "backport:" From 625a939459fa354f61f02aae9b43212f2c47b6f3 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Sun, 24 Sep 2023 21:20:44 +0100 Subject: [PATCH 02/13] Downgrade to `sqren/backport-github-action@v8.9.7` at least errors are reported Signed-off-by: Pedro Algarvio --- .backportrc.json | 2 +- .github/workflows/backport.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.backportrc.json b/.backportrc.json index b988c16660f..1fc808b961b 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -1,7 +1,7 @@ { "repoOwner": "saltstack", "repoName": "salt", - "targetBranchChoices": ["master", "3006.x", "3005.x"], + "targetBranchChoices": ["master", "3006.x", "3005.x", "freeze"], "autoMerge": false, "autoMergeMethod": "rebase", "branchLabelMapping": { diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 4e255576c92..b70b84df5b3 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -33,7 +33,7 @@ jobs: ) steps: - name: Backport Action - uses: sqren/backport-github-action@v9.3.0-a + uses: sqren/backport-github-action@v8.9.7 with: github_token: ${{ secrets.GITHUB_TOKEN }} auto_backport_label_prefix: "backport:" From 67bdf8b2ec4bc396a7226d57565b8a05eaec1e88 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Sun, 7 Jan 2024 18:10:36 -0700 Subject: [PATCH 03/13] Fix CVE-2024-22231 and CVE-2024-22232 --- salt/fileserver/__init__.py | 7 +- salt/fileserver/roots.py | 26 ++ salt/master.py | 8 +- tests/pytests/unit/fileserver/test_roots.py | 313 ++++++++++++++++++++ tests/pytests/unit/test_fileserver.py | 127 ++++++++ tests/pytests/unit/test_master.py | 32 ++ tests/unit/fileserver/test_roots.py | 273 ----------------- tests/unit/test_fileserver.py | 79 ----- 8 files changed, 508 insertions(+), 357 deletions(-) create mode 100644 tests/pytests/unit/fileserver/test_roots.py create mode 100644 tests/pytests/unit/test_fileserver.py delete mode 100644 tests/unit/fileserver/test_roots.py delete mode 100644 tests/unit/test_fileserver.py diff --git a/salt/fileserver/__init__.py b/salt/fileserver/__init__.py index 99f12387f91..51e8ed66597 100644 --- a/salt/fileserver/__init__.py +++ b/salt/fileserver/__init__.py @@ -569,10 +569,6 @@ class Fileserver: back = self.backends(back) kwargs = {} fnd = {"path": "", "rel": ""} - if os.path.isabs(path): - return fnd - if "../" in path: - return fnd if salt.utils.url.is_escaped(path): # don't attempt to find URL query arguments in the path path = salt.utils.url.unescape(path) @@ -588,6 +584,9 @@ class Fileserver: args = comp.split("=", 1) kwargs[args[0]] = args[1] + if os.path.isabs(path) or "../" in path: + return fnd + if "env" in kwargs: # "env" is not supported; Use "saltenv". kwargs.pop("env") diff --git a/salt/fileserver/roots.py b/salt/fileserver/roots.py index 4880cbab9b4..4ffdb6df6ff 100644 --- a/salt/fileserver/roots.py +++ b/salt/fileserver/roots.py @@ -27,6 +27,7 @@ import salt.utils.hashutils import salt.utils.path import salt.utils.platform import salt.utils.stringutils +import salt.utils.verify import salt.utils.versions log = logging.getLogger(__name__) @@ -98,6 +99,11 @@ def find_file(path, saltenv="base", **kwargs): if saltenv == "__env__": root = root.replace("__env__", actual_saltenv) full = os.path.join(root, path) + + # Refuse to serve file that is not under the root. + if not salt.utils.verify.clean_path(root, full, subdir=True): + continue + if os.path.isfile(full) and not salt.fileserver.is_file_ignored(__opts__, full): fnd["path"] = full fnd["rel"] = path @@ -128,6 +134,26 @@ def serve_file(load, fnd): ret["dest"] = fnd["rel"] gzip = load.get("gzip", None) fpath = os.path.normpath(fnd["path"]) + + actual_saltenv = saltenv = load["saltenv"] + if saltenv not in __opts__["file_roots"]: + if "__env__" in __opts__["file_roots"]: + log.debug( + "salt environment '%s' maps to __env__ file_roots directory", saltenv + ) + saltenv = "__env__" + else: + return fnd + file_in_root = False + for root in __opts__["file_roots"][saltenv]: + if saltenv == "__env__": + root = root.replace("__env__", actual_saltenv) + # Refuse to serve file that is not under the root. + if salt.utils.verify.clean_path(root, fpath, subdir=True): + file_in_root = True + if not file_in_root: + return ret + with salt.utils.files.fopen(fpath, "rb") as fp_: fp_.seek(load["loc"]) data = fp_.read(__opts__["file_buffer_size"]) diff --git a/salt/master.py b/salt/master.py index 9c06a52c1cd..7101416ec0c 100644 --- a/salt/master.py +++ b/salt/master.py @@ -1730,10 +1730,16 @@ class AESFuncs(TransportMethods): self.mminion.returners[fstr](load["jid"], load["load"]) # Register the syndic + + # We are creating a path using user suplied input. Use the + # clean_path to prevent a directory traversal. + root = os.path.join(self.opts["cachedir"], "syndics") syndic_cache_path = os.path.join( self.opts["cachedir"], "syndics", load["id"] ) - if not os.path.exists(syndic_cache_path): + if not os.path.exists(syndic_cache_path) and salt.utils.verify.clean_path( + root, syndic_cache_path + ): path_name = os.path.split(syndic_cache_path)[0] if not os.path.exists(path_name): os.makedirs(path_name) diff --git a/tests/pytests/unit/fileserver/test_roots.py b/tests/pytests/unit/fileserver/test_roots.py new file mode 100644 index 00000000000..72e3d5ca39a --- /dev/null +++ b/tests/pytests/unit/fileserver/test_roots.py @@ -0,0 +1,313 @@ +""" + :codeauthor: Mike Place +""" + +import copy +import pathlib +import shutil +import textwrap + +import pytest +import salt.fileclient +import salt.fileserver.roots as roots +import salt.utils.files +import salt.utils.hashutils +import salt.utils.platform +import salt.utils.stringutils +from tests.support.mock import MagicMock, mock_open, patch + + +@pytest.fixture(scope="module") +def unicode_filename(): + return "питон.txt" + + +@pytest.fixture(scope="module") +def unicode_dirname(): + return "соль" + + +@pytest.fixture(autouse=True) +def testfile(tmp_path): + fp = tmp_path / "testfile" + fp.write_text("This is a testfile") + return fp + + +@pytest.fixture(autouse=True) +def tmp_state_tree(tmp_path, testfile, unicode_filename, unicode_dirname): + dirname = tmp_path / "roots_tmp_state_tree" + dirname.mkdir(parents=True, exist_ok=True) + shutil.copyfile(str(testfile), str(dirname / testfile.name)) + unicode_dir = dirname / unicode_dirname + unicode_dir.mkdir(parents=True, exist_ok=True) + (dirname / unicode_filename).write_text("this is a unicode file") + (unicode_dir / unicode_filename).write_text( + "this is a unicode file in a unicode env" + ) + (unicode_dir / "notunicode").write_text( + "this is NOT a unicode file in a unicode env" + ) + + return dirname + + +@pytest.fixture(autouse=True) +def testfilepath(tmp_state_tree, testfile): + return tmp_state_tree / testfile.name + + +@pytest.fixture +def configure_loader_modules(tmp_state_tree, temp_salt_master): + opts = temp_salt_master.config.copy() + overrides = {"file_roots": {"base": [str(tmp_state_tree)]}} + opts.update(overrides) + return {roots: {"__opts__": opts}} + + +def test_file_list(unicode_filename): + ret = roots.file_list({"saltenv": "base"}) + assert "testfile" in ret + assert unicode_filename in ret + + +def test_find_file(tmp_state_tree): + ret = roots.find_file("testfile") + assert "testfile" == ret["rel"] + + full_path_to_file = str(tmp_state_tree / "testfile") + assert full_path_to_file == ret["path"] + + +def test_serve_file(testfilepath): + with patch.dict(roots.__opts__, {"file_buffer_size": 262144}): + load = { + "saltenv": "base", + "path": str(testfilepath), + "loc": 0, + } + fnd = {"path": str(testfilepath), "rel": "testfile"} + ret = roots.serve_file(load, fnd) + + with salt.utils.files.fopen(str(testfilepath), "rb") as fp_: + data = fp_.read() + + assert ret == {"data": data, "dest": "testfile"} + + +def test_envs(unicode_dirname): + opts = {"file_roots": copy.copy(roots.__opts__["file_roots"])} + opts["file_roots"][unicode_dirname] = opts["file_roots"]["base"] + with patch.dict(roots.__opts__, opts): + ret = roots.envs() + assert "base" in ret + assert unicode_dirname in ret + + +def test_file_hash(testfile): + load = { + "saltenv": "base", + "path": str(testfile), + } + fnd = {"path": str(testfile), "rel": "testfile"} + ret = roots.file_hash(load, fnd) + + # Hashes are different in Windows. May be how git translates line + # endings + with salt.utils.files.fopen(str(testfile), "rb") as fp_: + hsum = salt.utils.hashutils.sha256_digest(fp_.read()) + + assert ret == {"hsum": hsum, "hash_type": "sha256"} + + +def test_file_list_emptydirs(tmp_state_tree): + empty_dir = tmp_state_tree / "empty_dir" + empty_dir.mkdir(parents=True, exist_ok=True) + ret = roots.file_list_emptydirs({"saltenv": "base"}) + assert "empty_dir" in ret + + +def test_file_list_with_slash(unicode_filename): + opts = {"file_roots": copy.copy(roots.__opts__["file_roots"])} + opts["file_roots"]["foo/bar"] = opts["file_roots"]["base"] + load = { + "saltenv": "foo/bar", + } + with patch.dict(roots.__opts__, opts): + ret = roots.file_list(load) + assert "testfile" in ret + assert unicode_filename in ret + + +def test_dir_list(tmp_state_tree, unicode_dirname): + empty_dir = tmp_state_tree / "empty_dir" + empty_dir.mkdir(parents=True, exist_ok=True) + ret = roots.dir_list({"saltenv": "base"}) + assert "empty_dir" in ret + assert unicode_dirname in ret + + +def test_symlink_list(tmp_state_tree): + source_sym = tmp_state_tree / "source_sym" + source_sym.write_text("") + dest_sym = tmp_state_tree / "dest_sym" + dest_sym.symlink_to(str(source_sym)) + ret = roots.symlink_list({"saltenv": "base"}) + assert ret == {"dest_sym": str(source_sym)} + + +def test_dynamic_file_roots(tmp_path): + dyn_root_dir = tmp_path / "dyn_root_dir" + dyn_root_dir.mkdir(parents=True, exist_ok=True) + top_sls = dyn_root_dir / "top.sls" + with salt.utils.files.fopen(str(top_sls), "w") as fp_: + fp_.write("{{saltenv}}:\n '*':\n - dynamo\n") + dynamo_sls = dyn_root_dir / "dynamo.sls" + with salt.utils.files.fopen(str(dynamo_sls), "w") as fp_: + fp_.write("foo:\n test.nop\n") + opts = {"file_roots": copy.copy(roots.__opts__["file_roots"])} + opts["file_roots"]["__env__"] = [str(dyn_root_dir)] + with patch.dict(roots.__opts__, opts): + ret1 = roots.find_file("dynamo.sls", "dyn") + ret2 = roots.file_list({"saltenv": "dyn"}) + assert "dynamo.sls" == ret1["rel"] + assert "top.sls" in ret2 + assert "dynamo.sls" in ret2 + + +@pytest.mark.skip_on_windows( + reason="Windows does not support this master function", +) +def test_update_no_change(): + # process all changes that have happen + # changes will always take place the first time during testing + ret = roots.update() + assert ret["changed"] is True + + # check if no changes took place + ret = roots.update() + assert ret["changed"] is False + assert ret["files"]["changed"] == [] + assert ret["files"]["removed"] == [] + assert ret["files"]["added"] == [] + + +def test_update_mtime_map(): + """ + Test that files with colons in the filename are properly handled in the + mtime_map, and that they are properly identified as having changed. + """ + mtime_map_path = pathlib.Path(roots.__opts__["cachedir"], "roots", "mtime_map") + mtime_map_mock = mock_open( + read_data={ + str(mtime_map_path): textwrap.dedent( + """\ + /srv/salt/kleine_Datei.txt:1594263154.0469685 + /srv/salt/große:Datei.txt:1594263160.9336357 + """ + ), + } + ) + new_mtime_map = { + "/srv/salt/kleine_Datei.txt": 1594263154.0469685, + "/srv/salt/große:Datei.txt": 1594263261.0616212, + } + + with patch( + "salt.fileserver.reap_fileserver_cache_dir", MagicMock(return_value=True) + ), patch( + "salt.fileserver.generate_mtime_map", MagicMock(return_value=new_mtime_map) + ), patch.dict( + roots.__opts__, {"fileserver_events": False} + ), patch( + "salt.utils.files.fopen", mtime_map_mock + ): + ret = roots.update() + + # Confirm the expected return from the function + assert ret == { + "changed": True, + "files": { + "changed": ["/srv/salt/große:Datei.txt"], + "removed": [], + "added": [], + }, + "backend": "roots", + }, ret + + # Confirm that the new values were written to the mtime_map. Sort both + # lists of lines to account for variances in dictionary iteration order + # between Python releases. + lines_written = sorted(mtime_map_mock.write_calls()) + expected = sorted( + salt.utils.stringutils.to_bytes(f"{key}:{val}\n") + for key, val in new_mtime_map.items() + ) + assert lines_written == expected, lines_written + + +def test_update_mtime_map_unicode_error(tmp_path): + """ + Test that a malformed mtime_map (which causes an UnicodeDecodeError + exception) is handled properly. + """ + new_mtime_map = { + "/srv/salt/große:Datei.txt": 1594263261.0616212, + } + tmpdirname = tmp_path / "unicode_error" + mtime_map_path = tmpdirname / "roots" / "mtime_map" + mtime_map_path.parent.mkdir(parents=True, exist_ok=True) + with salt.utils.files.fopen(str(mtime_map_path), "wb") as fp: + fp.write(b"\x9c") + + with patch( + "salt.fileserver.reap_fileserver_cache_dir", + MagicMock(return_value=True), + ), patch( + "salt.fileserver.generate_mtime_map", + MagicMock(return_value=new_mtime_map), + ), patch.dict( + roots.__opts__, + {"fileserver_events": False, "cachedir": str(tmpdirname)}, + ): + ret = roots.update() + + assert ret == { + "changed": True, + "files": { + "changed": [], + "removed": [], + "added": ["/srv/salt/große:Datei.txt"], + }, + "backend": "roots", + } + + +def test_find_file_not_in_root(tmp_state_tree): + """ + Fileroots should never 'find' a file that is outside of it's root. + """ + badfile = pathlib.Path(tmp_state_tree).parent / "bar" + badfile.write_text("Bad file") + badpath = f"../bar" + ret = roots.find_file(badpath) + assert ret == {"path": "", "rel": ""} + badpath = f"{tmp_state_tree / '..' / 'bar'}" + ret = roots.find_file(badpath) + assert ret == {"path": "", "rel": ""} + + +def test_serve_file_not_in_root(tmp_state_tree): + """ + Fileroots should never 'serve' a file that is outside of it's root. + """ + badfile = pathlib.Path(tmp_state_tree).parent / "bar" + badfile.write_text("Bad file") + badpath = f"../bar" + load = {"path": "salt://|..\\bar", "saltenv": "base", "loc": 0} + fnd = { + "path": f"{tmp_state_tree / '..' / 'bar'}", + "rel": f"{pathlib.Path('..') / 'bar'}", + } + ret = roots.serve_file(load, fnd) + assert ret == {"data": "", "dest": "../bar"} diff --git a/tests/pytests/unit/test_fileserver.py b/tests/pytests/unit/test_fileserver.py new file mode 100644 index 00000000000..f2b2bb480f9 --- /dev/null +++ b/tests/pytests/unit/test_fileserver.py @@ -0,0 +1,127 @@ +""" +""" + + +import datetime +import os +import time + +import salt.fileserver +import salt.utils.files + + +def test_diff_with_diffent_keys(): + """ + Test that different maps are indeed reported different + """ + map1 = {"file1": 1234} + map2 = {"file2": 1234} + assert salt.fileserver.diff_mtime_map(map1, map2) is True + + +def test_diff_with_diffent_values(): + """ + Test that different maps are indeed reported different + """ + map1 = {"file1": 12345} + map2 = {"file1": 1234} + assert salt.fileserver.diff_mtime_map(map1, map2) is True + + +def test_whitelist(): + opts = { + "fileserver_backend": ["roots", "git", "s3fs", "hgfs", "svn"], + "extension_modules": "", + } + fs = salt.fileserver.Fileserver(opts) + assert sorted(fs.servers.whitelist) == sorted( + ["git", "gitfs", "hg", "hgfs", "svn", "svnfs", "roots", "s3fs"] + ), fs.servers.whitelist + + +def test_future_file_list_cache_file_ignored(tmp_path): + opts = { + "fileserver_backend": ["roots"], + "cachedir": tmp_path, + "extension_modules": "", + } + + back_cachedir = os.path.join(tmp_path, "file_lists/roots") + os.makedirs(os.path.join(back_cachedir)) + + # Touch a couple files + for filename in ("base.p", "foo.txt"): + with salt.utils.files.fopen(os.path.join(back_cachedir, filename), "wb") as _f: + if filename == "base.p": + _f.write(b"\x80") + + # Set modification time to file list cache file to 1 year in the future + now = datetime.datetime.utcnow() + future = now + datetime.timedelta(days=365) + mod_time = time.mktime(future.timetuple()) + os.utime(os.path.join(back_cachedir, "base.p"), (mod_time, mod_time)) + + list_cache = os.path.join(back_cachedir, "base.p") + w_lock = os.path.join(back_cachedir, ".base.w") + ret = salt.fileserver.check_file_list_cache(opts, "files", list_cache, w_lock) + assert ( + ret[1] is True + ), "Cache file list cache file is not refreshed when future modification time" + + +def test_file_server_url_escape(tmp_path): + (tmp_path / "srv").mkdir() + (tmp_path / "srv" / "salt").mkdir() + (tmp_path / "foo").mkdir() + (tmp_path / "foo" / "bar").write_text("Bad file") + fileroot = str(tmp_path / "srv" / "salt") + badfile = str(tmp_path / "foo" / "bar") + opts = { + "fileserver_backend": ["roots"], + "extension_modules": "", + "optimization_order": [ + 0, + ], + "file_roots": { + "base": [fileroot], + }, + "file_ignore_regex": "", + "file_ignore_glob": "", + } + fs = salt.fileserver.Fileserver(opts) + ret = fs.find_file( + "salt://|..\\..\\..\\foo/bar", + "base", + ) + assert ret == {"path": "", "rel": ""} + + +def test_file_server_serve_url_escape(tmp_path): + (tmp_path / "srv").mkdir() + (tmp_path / "srv" / "salt").mkdir() + (tmp_path / "foo").mkdir() + (tmp_path / "foo" / "bar").write_text("Bad file") + fileroot = str(tmp_path / "srv" / "salt") + badfile = str(tmp_path / "foo" / "bar") + opts = { + "fileserver_backend": ["roots"], + "extension_modules": "", + "optimization_order": [ + 0, + ], + "file_roots": { + "base": [fileroot], + }, + "file_ignore_regex": "", + "file_ignore_glob": "", + "file_buffer_size": 2048, + } + fs = salt.fileserver.Fileserver(opts) + ret = fs.serve_file( + { + "path": "salt://|..\\..\\..\\foo/bar", + "saltenv": "base", + "loc": 0, + } + ) + assert ret == {"data": "", "dest": ""} diff --git a/tests/pytests/unit/test_master.py b/tests/pytests/unit/test_master.py index a49ecfec3b1..b4a0e0f44f5 100644 --- a/tests/pytests/unit/test_master.py +++ b/tests/pytests/unit/test_master.py @@ -14,3 +14,35 @@ def test_fileserver_duration(): update.called_once() # Timeout is 1 second assert 2 > end - start > 1 + + +def test_syndic_return_cache_dir_creation(encrypted_requests): + """master's cachedir for a syndic will be created by AESFuncs._syndic_return method""" + cachedir = pathlib.Path(encrypted_requests.opts["cachedir"]) + assert not (cachedir / "syndics").exists() + encrypted_requests._syndic_return( + { + "id": "mamajama", + "jid": "", + "return": {}, + } + ) + assert (cachedir / "syndics").exists() + assert (cachedir / "syndics" / "mamajama").exists() + + +def test_syndic_return_cache_dir_creation_traversal(encrypted_requests): + """ + master's AESFuncs._syndic_return method cachdir creation is not vulnerable to a directory traversal + """ + cachedir = pathlib.Path(encrypted_requests.opts["cachedir"]) + assert not (cachedir / "syndics").exists() + encrypted_requests._syndic_return( + { + "id": "../mamajama", + "jid": "", + "return": {}, + } + ) + assert not (cachedir / "syndics").exists() + assert not (cachedir / "mamajama").exists() diff --git a/tests/unit/fileserver/test_roots.py b/tests/unit/fileserver/test_roots.py deleted file mode 100644 index 0b917091b46..00000000000 --- a/tests/unit/fileserver/test_roots.py +++ /dev/null @@ -1,273 +0,0 @@ -""" - :codeauthor: Mike Place -""" - -import copy -import os -import pathlib -import shutil -import tempfile -import textwrap - -import salt.fileclient -import salt.fileserver.roots as roots -import salt.utils.files -import salt.utils.hashutils -import salt.utils.platform -import salt.utils.stringutils -from tests.support.mixins import ( - AdaptedConfigurationTestCaseMixin, - LoaderModuleMockMixin, -) -from tests.support.mock import MagicMock, mock_open, patch -from tests.support.runtests import RUNTIME_VARS -from tests.support.unit import TestCase, skipIf - -UNICODE_FILENAME = "питон.txt" -UNICODE_DIRNAME = UNICODE_ENVNAME = "соль" - - -class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMixin): - def setup_loader_modules(self): - config_overrides = {"file_roots": {"base": [str(self.tmp_state_tree)]}} - self.opts = self.get_temp_config("master", **config_overrides) - return {roots: {"__opts__": self.opts}} - - @classmethod - def setUpClass(cls): - cls.tmp_dir = pathlib.Path(tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)) - cls.tmp_state_tree = pathlib.Path(tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)) - full_path_to_file = os.path.join(RUNTIME_VARS.BASE_FILES, "testfile") - shutil.copyfile(full_path_to_file, str(cls.tmp_dir / "testfile")) - shutil.copyfile(full_path_to_file, str(cls.tmp_state_tree / "testfile")) - shutil.copyfile( - os.path.join(RUNTIME_VARS.BASE_FILES, UNICODE_FILENAME), - str(cls.tmp_state_tree / UNICODE_FILENAME), - ) - shutil.copytree( - os.path.join(RUNTIME_VARS.BASE_FILES, UNICODE_DIRNAME), - str(cls.tmp_state_tree / UNICODE_DIRNAME), - ) - - @classmethod - def tearDownClass(cls): - salt.utils.files.rm_rf(str(cls.tmp_dir)) - salt.utils.files.rm_rf(str(cls.tmp_state_tree)) - - def tearDown(self): - del self.opts - - def test_file_list(self): - ret = roots.file_list({"saltenv": "base"}) - self.assertIn("testfile", ret) - self.assertIn(UNICODE_FILENAME, ret) - - def test_find_file(self): - ret = roots.find_file("testfile") - self.assertEqual("testfile", ret["rel"]) - - full_path_to_file = str(self.tmp_state_tree / "testfile") - self.assertEqual(full_path_to_file, ret["path"]) - - def test_serve_file(self): - with patch.dict(roots.__opts__, {"file_buffer_size": 262144}): - load = { - "saltenv": "base", - "path": str(self.tmp_dir / "testfile"), - "loc": 0, - } - fnd = {"path": str(self.tmp_dir / "testfile"), "rel": "testfile"} - ret = roots.serve_file(load, fnd) - - with salt.utils.files.fopen( - os.path.join(RUNTIME_VARS.BASE_FILES, "testfile"), "rb" - ) as fp_: - data = fp_.read() - - self.assertDictEqual(ret, {"data": data, "dest": "testfile"}) - - def test_envs(self): - opts = {"file_roots": copy.copy(self.opts["file_roots"])} - opts["file_roots"][UNICODE_ENVNAME] = opts["file_roots"]["base"] - with patch.dict(roots.__opts__, opts): - ret = roots.envs() - self.assertIn("base", ret) - self.assertIn(UNICODE_ENVNAME, ret) - - def test_file_hash(self): - load = { - "saltenv": "base", - "path": str(self.tmp_dir / "testfile"), - } - fnd = {"path": str(self.tmp_dir / "testfile"), "rel": "testfile"} - ret = roots.file_hash(load, fnd) - - # Hashes are different in Windows. May be how git translates line - # endings - with salt.utils.files.fopen( - os.path.join(RUNTIME_VARS.BASE_FILES, "testfile"), "rb" - ) as fp_: - hsum = salt.utils.hashutils.sha256_digest(fp_.read()) - - self.assertDictEqual(ret, {"hsum": hsum, "hash_type": "sha256"}) - - def test_file_list_emptydirs(self): - empty_dir = self.tmp_state_tree / "empty_dir" - if not empty_dir.is_dir(): - empty_dir.mkdir() - self.addCleanup(salt.utils.files.rm_rf, str(empty_dir)) - ret = roots.file_list_emptydirs({"saltenv": "base"}) - self.assertIn("empty_dir", ret) - - def test_file_list_with_slash(self): - opts = {"file_roots": copy.copy(self.opts["file_roots"])} - opts["file_roots"]["foo/bar"] = opts["file_roots"]["base"] - load = { - "saltenv": "foo/bar", - } - with patch.dict(roots.__opts__, opts): - ret = roots.file_list(load) - self.assertIn("testfile", ret) - self.assertIn(UNICODE_FILENAME, ret) - - def test_dir_list(self): - empty_dir = self.tmp_state_tree / "empty_dir" - if not empty_dir.is_dir(): - empty_dir.mkdir() - self.addCleanup(salt.utils.files.rm_rf, str(empty_dir)) - ret = roots.dir_list({"saltenv": "base"}) - self.assertIn("empty_dir", ret) - self.assertIn(UNICODE_DIRNAME, ret) - - def test_symlink_list(self): - source_sym = self.tmp_state_tree / "source_sym" - source_sym.write_text("") - dest_sym = self.tmp_state_tree / "dest_sym" - dest_sym.symlink_to(str(source_sym)) - self.addCleanup(dest_sym.unlink) - self.addCleanup(source_sym.unlink) - ret = roots.symlink_list({"saltenv": "base"}) - self.assertDictEqual(ret, {"dest_sym": str(source_sym)}) - - def test_dynamic_file_roots(self): - dyn_root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP) - top_sls = os.path.join(dyn_root_dir, "top.sls") - with salt.utils.files.fopen(top_sls, "w") as fp_: - fp_.write("{{saltenv}}:\n '*':\n - dynamo\n") - dynamo_sls = os.path.join(dyn_root_dir, "dynamo.sls") - with salt.utils.files.fopen(dynamo_sls, "w") as fp_: - fp_.write("foo:\n test.nop\n") - opts = {"file_roots": copy.copy(self.opts["file_roots"])} - opts["file_roots"]["__env__"] = [dyn_root_dir] - with patch.dict(roots.__opts__, opts): - ret1 = roots.find_file("dynamo.sls", "dyn") - ret2 = roots.file_list({"saltenv": "dyn"}) - self.assertEqual("dynamo.sls", ret1["rel"]) - self.assertIn("top.sls", ret2) - self.assertIn("dynamo.sls", ret2) - - @skipIf( - salt.utils.platform.is_windows(), - "Windows does not support this master function", - ) - def test_update_no_change(self): - # process all changes that have happen - # changes will always take place the first time during testing - ret = roots.update() - self.assertTrue(ret["changed"]) - - # check if no changes took place - ret = roots.update() - self.assertFalse(ret["changed"]) - self.assertEqual(ret["files"]["changed"], []) - self.assertEqual(ret["files"]["removed"], []) - self.assertEqual(ret["files"]["added"], []) - - def test_update_mtime_map(self): - """ - Test that files with colons in the filename are properly handled in the - mtime_map, and that they are properly identified as having changed. - """ - mtime_map_path = os.path.join(self.opts["cachedir"], "roots", "mtime_map") - mtime_map_mock = mock_open( - read_data={ - mtime_map_path: textwrap.dedent( - """\ - /srv/salt/kleine_Datei.txt:1594263154.0469685 - /srv/salt/große:Datei.txt:1594263160.9336357 - """ - ), - } - ) - new_mtime_map = { - "/srv/salt/kleine_Datei.txt": 1594263154.0469685, - "/srv/salt/große:Datei.txt": 1594263261.0616212, - } - - with patch( - "salt.fileserver.reap_fileserver_cache_dir", MagicMock(return_value=True) - ), patch( - "salt.fileserver.generate_mtime_map", MagicMock(return_value=new_mtime_map) - ), patch.dict( - roots.__opts__, {"fileserver_events": False} - ), patch( - "salt.utils.files.fopen", mtime_map_mock - ): - ret = roots.update() - - # Confirm the expected return from the function - assert ret == { - "changed": True, - "files": { - "changed": ["/srv/salt/große:Datei.txt"], - "removed": [], - "added": [], - }, - "backend": "roots", - }, ret - - # Confirm that the new values were written to the mtime_map. Sort both - # lists of lines to account for variances in dictionary iteration order - # between Python releases. - lines_written = sorted(mtime_map_mock.write_calls()) - expected = sorted( - salt.utils.stringutils.to_bytes("{key}:{val}\n".format(key=key, val=val)) - for key, val in new_mtime_map.items() - ) - assert lines_written == expected, lines_written - - def test_update_mtime_map_unicode_error(self): - """ - Test that a malformed mtime_map (which causes an UnicodeDecodeError - exception) is handled properly. - """ - new_mtime_map = { - "/srv/salt/große:Datei.txt": 1594263261.0616212, - } - with tempfile.TemporaryDirectory() as tmpdirname: - mtime_map_path = os.path.join(tmpdirname, "roots", "mtime_map") - os.makedirs(os.path.dirname(mtime_map_path)) - with salt.utils.files.fopen(mtime_map_path, "wb") as fp: - fp.write(b"\x9c") - - with patch( - "salt.fileserver.reap_fileserver_cache_dir", - MagicMock(return_value=True), - ), patch( - "salt.fileserver.generate_mtime_map", - MagicMock(return_value=new_mtime_map), - ), patch.dict( - roots.__opts__, - {"fileserver_events": False, "cachedir": tmpdirname}, - ): - ret = roots.update() - - assert ret == { - "changed": True, - "files": { - "changed": [], - "removed": [], - "added": ["/srv/salt/große:Datei.txt"], - }, - "backend": "roots", - }, ret diff --git a/tests/unit/test_fileserver.py b/tests/unit/test_fileserver.py deleted file mode 100644 index c290b16b7e4..00000000000 --- a/tests/unit/test_fileserver.py +++ /dev/null @@ -1,79 +0,0 @@ -""" - :codeauthor: Joao Mesquita -""" - - -import datetime -import os -import time - -import salt.utils.files -from salt import fileserver -from tests.support.helpers import with_tempdir -from tests.support.mixins import LoaderModuleMockMixin -from tests.support.unit import TestCase - - -class MapDiffTestCase(TestCase): - def test_diff_with_diffent_keys(self): - """ - Test that different maps are indeed reported different - """ - map1 = {"file1": 1234} - map2 = {"file2": 1234} - assert fileserver.diff_mtime_map(map1, map2) is True - - def test_diff_with_diffent_values(self): - """ - Test that different maps are indeed reported different - """ - map1 = {"file1": 12345} - map2 = {"file1": 1234} - assert fileserver.diff_mtime_map(map1, map2) is True - - -class VCSBackendWhitelistCase(TestCase, LoaderModuleMockMixin): - def setup_loader_modules(self): - return {fileserver: {}} - - def test_whitelist(self): - opts = { - "fileserver_backend": ["roots", "git", "s3fs", "hgfs", "svn"], - "extension_modules": "", - } - fs = fileserver.Fileserver(opts) - assert sorted(fs.servers.whitelist) == sorted( - ["git", "gitfs", "hg", "hgfs", "svn", "svnfs", "roots", "s3fs"] - ), fs.servers.whitelist - - @with_tempdir() - def test_future_file_list_cache_file_ignored(self, cachedir): - opts = { - "fileserver_backend": ["roots"], - "cachedir": cachedir, - "extension_modules": "", - } - - back_cachedir = os.path.join(cachedir, "file_lists/roots") - os.makedirs(os.path.join(back_cachedir)) - - # Touch a couple files - for filename in ("base.p", "foo.txt"): - with salt.utils.files.fopen( - os.path.join(back_cachedir, filename), "wb" - ) as _f: - if filename == "base.p": - _f.write(b"\x80") - - # Set modification time to file list cache file to 1 year in the future - now = datetime.datetime.utcnow() - future = now + datetime.timedelta(days=365) - mod_time = time.mktime(future.timetuple()) - os.utime(os.path.join(back_cachedir, "base.p"), (mod_time, mod_time)) - - list_cache = os.path.join(back_cachedir, "base.p") - w_lock = os.path.join(back_cachedir, ".base.w") - ret = fileserver.check_file_list_cache(opts, "files", list_cache, w_lock) - assert ( - ret[1] is True - ), "Cache file list cache file is not refreshed when future modification time" From 3468033d42bbf55566b8fff33b80540a73bb9728 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Mon, 8 Jan 2024 14:01:23 -0700 Subject: [PATCH 04/13] Fix linter issue --- tests/pytests/unit/test_fileserver.py | 4 ---- tests/pytests/unit/test_master.py | 1 + 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/pytests/unit/test_fileserver.py b/tests/pytests/unit/test_fileserver.py index f2b2bb480f9..8dd3ea0a27d 100644 --- a/tests/pytests/unit/test_fileserver.py +++ b/tests/pytests/unit/test_fileserver.py @@ -1,7 +1,3 @@ -""" -""" - - import datetime import os import time diff --git a/tests/pytests/unit/test_master.py b/tests/pytests/unit/test_master.py index b4a0e0f44f5..992ac94728c 100644 --- a/tests/pytests/unit/test_master.py +++ b/tests/pytests/unit/test_master.py @@ -1,3 +1,4 @@ +import pathlib import time import salt.master From 4a5839e74d2cc50b3ceb206f8dcc5cf96d46cc38 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Wed, 10 Jan 2024 15:37:07 -0700 Subject: [PATCH 05/13] Clean up test fixtures --- tests/pytests/unit/fileserver/test_roots.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/pytests/unit/fileserver/test_roots.py b/tests/pytests/unit/fileserver/test_roots.py index 72e3d5ca39a..62b9b3ac01c 100644 --- a/tests/pytests/unit/fileserver/test_roots.py +++ b/tests/pytests/unit/fileserver/test_roots.py @@ -27,14 +27,14 @@ def unicode_dirname(): return "соль" -@pytest.fixture(autouse=True) +@pytest.fixture def testfile(tmp_path): fp = tmp_path / "testfile" fp.write_text("This is a testfile") return fp -@pytest.fixture(autouse=True) +@pytest.fixture def tmp_state_tree(tmp_path, testfile, unicode_filename, unicode_dirname): dirname = tmp_path / "roots_tmp_state_tree" dirname.mkdir(parents=True, exist_ok=True) @@ -52,17 +52,16 @@ def tmp_state_tree(tmp_path, testfile, unicode_filename, unicode_dirname): return dirname -@pytest.fixture(autouse=True) +@pytest.fixture def testfilepath(tmp_state_tree, testfile): return tmp_state_tree / testfile.name @pytest.fixture -def configure_loader_modules(tmp_state_tree, temp_salt_master): - opts = temp_salt_master.config.copy() +def configure_loader_modules(tmp_state_tree, master_config): overrides = {"file_roots": {"base": [str(tmp_state_tree)]}} - opts.update(overrides) - return {roots: {"__opts__": opts}} + master_config.update(overrides) + return {roots: {"__opts__": master_config}} def test_file_list(unicode_filename): From d9019bdd6476c9505af98c736c17bdb77cdadb86 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Wed, 10 Jan 2024 15:42:53 -0700 Subject: [PATCH 06/13] Add changelogs for CVE-2024-22231 and CVE-2024-22232 --- changelog/565.security | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/565.security diff --git a/changelog/565.security b/changelog/565.security new file mode 100644 index 00000000000..7f1da931eb2 --- /dev/null +++ b/changelog/565.security @@ -0,0 +1,2 @@ +CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master +CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. From 2c8f1b702df34b45ba3d7412b88c8ec041c08f1e Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" Date: Wed, 17 Jan 2024 14:44:23 -0700 Subject: [PATCH 07/13] Add credit --- changelog/565.security | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog/565.security b/changelog/565.security index 7f1da931eb2..5d7ec8202ba 100644 --- a/changelog/565.security +++ b/changelog/565.security @@ -1,2 +1,4 @@ CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. +These vulerablities were discovered and reported by: +Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) From 8504ad85389ce94cf9c09c785a8fa4bed3e76bb2 Mon Sep 17 00:00:00 2001 From: Shane Lee Date: Tue, 23 Jan 2024 14:59:51 -0700 Subject: [PATCH 08/13] Backport notarize.sh from 3006 --- pkg/osx/notarize.sh | 195 +++++++++++++++++++++++--------------------- 1 file changed, 101 insertions(+), 94 deletions(-) diff --git a/pkg/osx/notarize.sh b/pkg/osx/notarize.sh index d2461eff5dd..87c9cf2adeb 100755 --- a/pkg/osx/notarize.sh +++ b/pkg/osx/notarize.sh @@ -6,18 +6,19 @@ # Date: December 2020 # # Description: This notarizes the macOS Installer Package (.pkg). It uses the -# `altool` xcode utility which is only available in the full -# Xcode package. It is not available in Command Line Tools. +# `notarytool` xcode utility which became available in Xcode 13. +# Xcode 13 requires macOS Big Sur 11.3 or higher. However, the +# notarytool binary can be extracted and run on macOS Catalina +# 10.15.7 and higher. It is not available in Command Line Tools. # -# This script will upload a copy of the package to apple and wait +# This script will upload a copy of the package to Apple and wait # for the notarization to return. This can take several minutes. # -# This script should be run with sudo. If not, it will attempt to -# elevate and run with sudo. In order for the environment variables -# to be available in sudo you need to pass the `-E` option. For -# example: +# This script requires the presence of some environment variables. +# If running this script with sudo, be sure to pass the `-E` +# option. # -# sudo -E ./notarize.sh +# sudo -E ./notarize.sh salt-3006.2-signed.pkg # # Requirements: # - Full Xcode Installation @@ -32,36 +33,27 @@ # The package that will be notarized (must be signed) # # Example: -# The following will notarize the 'salt-v2015.8.3-signed.pkg' file +# The following will notarize the 'salt-3006.2-signed.pkg' file: # -# sudo ./notarize.sh salt-v2015.8.3-signed.pkg +# ./notarize.sh salt-3006.2-signed.pkg # # Environment Setup: # # Define Environment Variables: -# Create two environment variables for the apple account and the -# app-specific password associated with that account. To generate the -# app-specific password see: https://support.apple.com/en-us/HT204397 +# Create three environment variables for the apple account, apple team +# ID, and the app-specific password associated with that account. To +# generate the app-specific password see: +# https://support.apple.com/en-us/HT204397 # # export APPLE_ACCT="username@domain.com" +# export APPLE_TEAM_ID="AB283DVDS5" # export APP_SPEC_PWD="abcd-efgh-ijkl-mnop" # ################################################################################ -echo "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv" -echo "Notarize Salt Package" -# Make sure the script is launched with sudo -# We're doing this with notarize because the package we're notarizing was -# built with sudo... so, we need sudo here in order to staple the notarization -# to the package -if [[ $(id -u) -ne 0 ]]; then - echo ">>>>>> Re-launching as sudo <<<<<<" - exec sudo -E /bin/bash -c "$(printf '%q ' "${BASH_SOURCE[0]}" "$@")" -fi - -################################################################################ +#------------------------------------------------------------------------------- # Check input parameters -################################################################################ +#------------------------------------------------------------------------------- if [ "$1" == "" ]; then echo "Must supply a package to notarize" exit 1 @@ -69,84 +61,99 @@ else PACKAGE=$1 fi -################################################################################ -# Environment Variables -################################################################################ -echo "**** Setting Variables" -BUNDLE_ID="com.saltstack.salt" -NOTARIZE_APP_LOG=$(mktemp -t notarize-app) -NOTARIZE_INFO_LOG=$(mktemp -t notarize-info) +#------------------------------------------------------------------------------- +# Functions +#------------------------------------------------------------------------------- +# _msg +# +# Prints the message with a dash... no new line +_msg() { + printf -- "- %s: " "$1" +} -################################################################################ +# _success +# +# Prints a green Success +_success() { + printf "\e[32m%s\e[0m\n" "Success" +} + +# _failure +# +# Prints a red Failure and exits +_failure() { + printf "\e[31m%s\e[0m\n" "Failure" + echo "output >>>>>>" + cat "$NOTARIZE_LOG" 1>&2 + echo "<<<<<< output" + exit 1 +} + +#------------------------------------------------------------------------------- +# Environment Variables +#------------------------------------------------------------------------------- +_msg "Setting Variables" +NOTARIZE_LOG=$(mktemp -t notarize-app.log) +NOTARY_TOOL=$(xcrun --find notarytool) +_success + +#------------------------------------------------------------------------------- +# Check for notarytool +#------------------------------------------------------------------------------- +if [ ! -f "$NOTARY_TOOL" ]; then + echo "This script requires the NotaryTool binary" + exit 1 +fi + +#------------------------------------------------------------------------------- # Delete temporary files on exit -################################################################################ +#------------------------------------------------------------------------------- function finish { - rm "$NOTARIZE_APP_LOG" "$NOTARIZE_INFO_LOG" + rm "$NOTARIZE_LOG" } trap finish EXIT -################################################################################ +#------------------------------------------------------------------------------- +# Script Start +#------------------------------------------------------------------------------- +printf "=%.0s" {1..80}; printf "\n" +echo "Notarize Salt Package" +echo "- This can take up to 30 minutes" +printf -- "-%.0s" {1..80}; printf "\n" + +#------------------------------------------------------------------------------- # Submit app for notarization -################################################################################ -echo "**** Submitting Package for Notarization" -if ! xcrun altool --notarize-app \ - --primary-bundle-id "$BUNDLE_ID" \ - --username "$APPLE_ACCT" \ - --password "$APP_SPEC_PWD" \ - -f "$PACKAGE" > "$NOTARIZE_APP_LOG" 2>&1; then - cat "$NOTARIZE_APP_LOG" 1>&2 - exit 1 +#------------------------------------------------------------------------------- +_msg "Submitting Package for Notarization" +if $NOTARY_TOOL submit \ + --apple-id "$APPLE_ACCT" \ + --team-id "$APPLE_TEAM_ID" \ + --password "$APP_SPEC_PWD" \ + --wait \ + "$PACKAGE" > "$NOTARIZE_LOG" 2>&1; then + _success +else + _failure fi -# Get RequestUUID from the APP LOG -# Uncomment for debugging -# cat "$NOTARIZE_APP_LOG" - -if ! grep -q "No errors uploading" "$NOTARIZE_APP_LOG"; then - echo ">>>>>> Failed Uploading Package <<<<<<" - exit 1 -fi -RequestUUID=$(awk -F ' = ' '/RequestUUID/ {print $2}' "$NOTARIZE_APP_LOG") - -echo "**** Checking Notarization Status (every 30 seconds)" -echo -n "**** " -# Check status every 30 seconds -while sleep 30; do - echo -n "." - - # check notarization status - if ! xcrun altool --notarization-info "$RequestUUID" \ - --username "$APPLE_ACCT" \ - --password "$APP_SPEC_PWD" > "$NOTARIZE_INFO_LOG" 2>&1; then - cat "$NOTARIZE_INFO_LOG" 1>&2 - exit 1 - fi - - # Look for Status in the INFO LOG - # Uncomment for debugging - # cat "$NOTARIZE_INFO_LOG" - - # Continue checking until Status is no longer "in progress" - if ! grep -q "Status: in progress" "$NOTARIZE_INFO_LOG"; then - echo "" - break - fi - -done - -# Make sure the result is "success", then staple -if ! grep -q "Status: success" "$NOTARIZE_INFO_LOG"; then - echo "**** There was a problem notarizing the package" - echo "**** View the log for details:" - awk -F ': ' '/LogFileURL/ {print $2}' "$NOTARIZE_INFO_LOG" - exit 1 +# Make sure the status is "Accepted", then staple +_msg "Verifying accepted status" +if grep -q "status: Accepted" "$NOTARIZE_LOG"; then + _success +else + _failure fi -echo "**** Stapling Notarization to the Package" -if ! xcrun stapler staple "$PACKAGE" > "$NOTARIZE_INFO_LOG"; then - cat "$NOTARIZE_INFO_LOG" 1>&2 - exit 1 +_msg "Stapling Notarization to the Package" +if xcrun stapler staple "$PACKAGE" > "$NOTARIZE_LOG"; then + _success +else + _failure fi -echo "Notarize Salt Package Completed Successfully" -echo "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" +#------------------------------------------------------------------------------- +# Script Completed +#------------------------------------------------------------------------------- +printf -- "-%.0s" {1..80}; printf "\n" +echo "Notarize Salt Package Completed" +printf "=%.0s" {1..80}; printf "\n" From 01752fcf1d645fece62c157e98ba6e3bf485b68b Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Tue, 23 Jan 2024 18:44:04 +0000 Subject: [PATCH 09/13] Stop time bombing with `RuntimeError`'s Signed-off-by: Pedro Algarvio --- salt/utils/versions.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/salt/utils/versions.py b/salt/utils/versions.py index 6df888178f4..495470b2fe1 100644 --- a/salt/utils/versions.py +++ b/salt/utils/versions.py @@ -9,12 +9,11 @@ Version parsing based on distutils.version which works under python 3 because on python 3 you can no longer compare strings against integers. """ - - import datetime import inspect import logging import numbers +import os import sys import warnings @@ -141,7 +140,7 @@ def warn_until( if _version_ >= version: caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) - raise RuntimeError( + deprecated_message = ( "The warning triggered on filename '{filename}', line number " "{lineno}, is supposed to be shown until version " "{until_version} is released. Current version is now " @@ -150,8 +149,15 @@ def warn_until( lineno=caller.lineno, until_version=version.formatted_version, salt_version=_version_.formatted_version, - ), + ) ) + if os.environ.get("RAISE_DEPRECATIONS_RUNTIME_ERRORS", "0") == "1": + # We don't raise RuntimeError by default since that can break + # users systems. We do however want to raise them in a CI context. + raise RuntimeError(deprecated_message) + # Otherwise, print the deprecated message to STDERR + sys.stderr.write(f"\n{deprecated_message}\n") + sys.stderr.flush() if _dont_call_warnings is False: warnings.warn( @@ -209,7 +215,7 @@ def warn_until_date( today = _current_date or datetime.datetime.utcnow().date() if today >= date: caller = inspect.getframeinfo(sys._getframe(stacklevel - 1)) - raise RuntimeError( + deprecated_message = ( "{message} This warning(now exception) triggered on " "filename '{filename}', line number {lineno}, is " "supposed to be shown until {date}. Today is {today}. " @@ -221,6 +227,13 @@ def warn_until_date( today=today.isoformat(), ), ) + if os.environ.get("RAISE_DEPRECATIONS_RUNTIME_ERRORS", "0") == "1": + # We don't raise RuntimeError by default since that can break + # users systems. We do however want to raise them in a CI context. + raise RuntimeError(deprecated_message) + # Otherwise, print the deprecated message to STDERR + sys.stderr.write(f"\n{deprecated_message}\n") + sys.stderr.flush() if _dont_call_warnings is False: warnings.warn( From ac52f60ee31af3117686d185cb4df883468c197d Mon Sep 17 00:00:00 2001 From: Alyssa Rock Date: Sat, 20 Jan 2024 12:14:41 -0700 Subject: [PATCH 10/13] Create 3006.5 release notes and update 3005 changelog --- CHANGELOG.md | 12 ++++++++++++ changelog/565.security | 4 ---- doc/topics/releases/3005.5.rst | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) delete mode 100644 changelog/565.security create mode 100644 doc/topics/releases/3005.5.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index 221549c4168..9f784ae29e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ Versions are `MAJOR.PATCH`. # Changelog +Salt 3005.5 (2024-01-19) +======================== + +Security +-------- + +- CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master + CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. + These vulnerablities were discovered and reported by: + Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) + + Salt v3005.4 (2023-10-16) ========================= diff --git a/changelog/565.security b/changelog/565.security deleted file mode 100644 index 5d7ec8202ba..00000000000 --- a/changelog/565.security +++ /dev/null @@ -1,4 +0,0 @@ -CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master -CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. -These vulerablities were discovered and reported by: -Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) diff --git a/doc/topics/releases/3005.5.rst b/doc/topics/releases/3005.5.rst new file mode 100644 index 00000000000..4bea66f7061 --- /dev/null +++ b/doc/topics/releases/3005.5.rst @@ -0,0 +1,15 @@ +.. _release-3005-5: + +========================= +Salt 3005.5 Release Notes +========================= + +Version 3005.5 is a CVE security fix release for :ref:`3005 `. + +Security +-------- + +- Fix CVE-2024-22231 by preventing directory traversal when creating syndic cache directory on the master + CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. + These vulnerablities were discovered and reported by: + Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) \ No newline at end of file From 20aff06da50aed192aa863f4c9d2e504a5f356e4 Mon Sep 17 00:00:00 2001 From: Alyssa Rock Date: Mon, 22 Jan 2024 10:17:23 -0700 Subject: [PATCH 11/13] Add minor revisions requested by Shane --- CHANGELOG.md | 7 ++++--- doc/topics/releases/3005.5.rst | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f784ae29e0..bc0bed2beb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,10 @@ Security -------- - CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master - CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. - These vulnerablities were discovered and reported by: - Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) +- CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. + +These vulnerablities were discovered and reported by: +Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) Salt v3005.4 (2023-10-16) diff --git a/doc/topics/releases/3005.5.rst b/doc/topics/releases/3005.5.rst index 4bea66f7061..c459ade53ba 100644 --- a/doc/topics/releases/3005.5.rst +++ b/doc/topics/releases/3005.5.rst @@ -10,6 +10,7 @@ Security -------- - Fix CVE-2024-22231 by preventing directory traversal when creating syndic cache directory on the master - CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. - These vulnerablities were discovered and reported by: - Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) \ No newline at end of file +- Fix CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. + +These vulnerablities were discovered and reported by: +Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) \ No newline at end of file From 066ab8fd5ad78074f8b41f774f89caa66f2eeed5 Mon Sep 17 00:00:00 2001 From: Alyssa Rock Date: Mon, 22 Jan 2024 10:19:35 -0700 Subject: [PATCH 12/13] More revisions --- CHANGELOG.md | 4 ++-- doc/topics/releases/3005.5.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc0bed2beb6..5e182b22bdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,8 @@ Salt 3005.5 (2024-01-19) Security -------- -- CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master -- CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. +- Fix CVE-2024-22231 Prevent directory traversal when creating syndic cache directory on the master. +- Fix CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. These vulnerablities were discovered and reported by: Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) diff --git a/doc/topics/releases/3005.5.rst b/doc/topics/releases/3005.5.rst index c459ade53ba..21fbcabd801 100644 --- a/doc/topics/releases/3005.5.rst +++ b/doc/topics/releases/3005.5.rst @@ -9,7 +9,7 @@ Version 3005.5 is a CVE security fix release for :ref:`3005 `. Security -------- -- Fix CVE-2024-22231 by preventing directory traversal when creating syndic cache directory on the master +- Fix CVE-2024-22231 by preventing directory traversal when creating syndic cache directory on the master. - Fix CVE-2024-22232 Prevent directory traversal attacks in the master's serve_file method. These vulnerablities were discovered and reported by: From e39b65aa4be1675b9bb156aba1c2deeababa0420 Mon Sep 17 00:00:00 2001 From: Alyssa Rock Date: Wed, 24 Jan 2024 14:07:56 -0700 Subject: [PATCH 13/13] Add man pages --- doc/man/salt-api.1 | 2 +- doc/man/salt-call.1 | 2 +- doc/man/salt-cloud.1 | 2 +- doc/man/salt-cp.1 | 2 +- doc/man/salt-key.1 | 2 +- doc/man/salt-master.1 | 2 +- doc/man/salt-minion.1 | 2 +- doc/man/salt-proxy.1 | 2 +- doc/man/salt-run.1 | 2 +- doc/man/salt-ssh.1 | 2 +- doc/man/salt-syndic.1 | 2 +- doc/man/salt.1 | 2 +- doc/man/salt.7 | 54 ++++++++++++++++++++++++++++++++----------- doc/man/spm.1 | 2 +- 14 files changed, 54 insertions(+), 26 deletions(-) diff --git a/doc/man/salt-api.1 b/doc/man/salt-api.1 index f93a4808031..460c89ebcae 100644 --- a/doc/man/salt-api.1 +++ b/doc/man/salt-api.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-API" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-API" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-api \- salt-api Command . diff --git a/doc/man/salt-call.1 b/doc/man/salt-call.1 index 2a1ec1051c5..db8d2ce9043 100644 --- a/doc/man/salt-call.1 +++ b/doc/man/salt-call.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-CALL" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-CALL" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-call \- salt-call Documentation . diff --git a/doc/man/salt-cloud.1 b/doc/man/salt-cloud.1 index d2906e9ad33..3b328768e4b 100644 --- a/doc/man/salt-cloud.1 +++ b/doc/man/salt-cloud.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-CLOUD" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-CLOUD" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-cloud \- Salt Cloud Command . diff --git a/doc/man/salt-cp.1 b/doc/man/salt-cp.1 index 1c342aa60c0..0a28d3628df 100644 --- a/doc/man/salt-cp.1 +++ b/doc/man/salt-cp.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-CP" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-CP" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-cp \- salt-cp Documentation . diff --git a/doc/man/salt-key.1 b/doc/man/salt-key.1 index 978fa8f106a..ba2f6bbf160 100644 --- a/doc/man/salt-key.1 +++ b/doc/man/salt-key.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-KEY" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-KEY" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-key \- salt-key Documentation . diff --git a/doc/man/salt-master.1 b/doc/man/salt-master.1 index e22e558d2ef..4e04b00a73d 100644 --- a/doc/man/salt-master.1 +++ b/doc/man/salt-master.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-MASTER" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-MASTER" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-master \- salt-master Documentation . diff --git a/doc/man/salt-minion.1 b/doc/man/salt-minion.1 index 8cccf72a077..1e35245aab3 100644 --- a/doc/man/salt-minion.1 +++ b/doc/man/salt-minion.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-MINION" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-MINION" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-minion \- salt-minion Documentation . diff --git a/doc/man/salt-proxy.1 b/doc/man/salt-proxy.1 index cefa2152540..9a42395e9ac 100644 --- a/doc/man/salt-proxy.1 +++ b/doc/man/salt-proxy.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-PROXY" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-PROXY" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-proxy \- salt-proxy Documentation . diff --git a/doc/man/salt-run.1 b/doc/man/salt-run.1 index f710a86474d..b03bea69239 100644 --- a/doc/man/salt-run.1 +++ b/doc/man/salt-run.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-RUN" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-RUN" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-run \- salt-run Documentation . diff --git a/doc/man/salt-ssh.1 b/doc/man/salt-ssh.1 index f4c26cb5d55..69285bf33e5 100644 --- a/doc/man/salt-ssh.1 +++ b/doc/man/salt-ssh.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-SSH" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-SSH" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-ssh \- salt-ssh Documentation . diff --git a/doc/man/salt-syndic.1 b/doc/man/salt-syndic.1 index 7c981e737a9..cbc2d8edd0b 100644 --- a/doc/man/salt-syndic.1 +++ b/doc/man/salt-syndic.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT-SYNDIC" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT-SYNDIC" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt-syndic \- salt-syndic Documentation . diff --git a/doc/man/salt.1 b/doc/man/salt.1 index 5ee91d6afc9..26f79eec158 100644 --- a/doc/man/salt.1 +++ b/doc/man/salt.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SALT" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME salt \- salt . diff --git a/doc/man/salt.7 b/doc/man/salt.7 index 2718ffd2bfd..a8f1cd196c9 100644 --- a/doc/man/salt.7 +++ b/doc/man/salt.7 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SALT" "7" "Oct 16, 2023" "3005" "Salt" +.TH "SALT" "7" "Jan 24, 2024" "3005" "Salt" .SH NAME salt \- Salt Documentation . @@ -151127,7 +151127,7 @@ salt \(aq*\(aq cmd.powershell_all "dir mydirectory" force_list=True .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.retcode(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, clean_env=False, template=None, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, password=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.retcode(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, clean_env=False, template=None, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, password=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Execute a shell command and return the command\(aqs return code. .INDENT 7.0 .TP @@ -151386,7 +151386,7 @@ salt \(aq*\(aq cmd.retcode "grep f" stdin=\(aqone\entwo\enthree\enfour\enfive\en .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.run(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, bg=False, password=None, encoded_cmd=False, raise_err=False, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.run(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, bg=False, password=None, encoded_cmd=False, raise_err=False, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Execute the passed command and return the output as a string .INDENT 7.0 .TP @@ -151767,7 +151767,7 @@ salt \(aq*\(aq cmd.run cmd=\(aqsed \-e s/=/:/g\(aq .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.run_all(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, redirect_stderr=False, password=None, encoded_cmd=False, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.run_all(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, redirect_stderr=False, password=None, encoded_cmd=False, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Execute the passed command and return a dict of return data .INDENT 7.0 .TP @@ -152110,7 +152110,7 @@ salt \(aq*\(aq cmd.run_all "grep f" stdin=\(aqone\entwo\enthree\enfour\enfive\en .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.run_bg(cmd, cwd=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, clean_env=False, template=None, umask=None, timeout=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.run_bg(cmd, cwd=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, clean_env=False, template=None, umask=None, timeout=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) New in version 2016.3.0. .sp @@ -152411,7 +152411,7 @@ salt \(aq*\(aq cmd.run_bg cmd=\(aqls \-lR / | sed \-e s/=/:/g > /tmp/dontwait\(a .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.run_chroot(root, cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=True, binds=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqquiet\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, bg=False, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.run_chroot(root, cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=True, binds=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqquiet\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, bg=False, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) New in version 2014.7.0. .sp @@ -152632,7 +152632,7 @@ salt \(aq*\(aq cmd.run_chroot /var/lib/lxc/container_name/rootfs \(aqsh /tmp/boo .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.run_stderr(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.run_stderr(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Execute a command and only return the standard error .INDENT 7.0 .TP @@ -152909,7 +152909,7 @@ salt \(aq*\(aq cmd.run_stderr "grep f" stdin=\(aqone\entwo\enthree\enfour\enfive .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.run_stdout(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.run_stdout(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Execute a command, and only return the standard out .INDENT 7.0 .TP @@ -153186,7 +153186,7 @@ salt \(aq*\(aq cmd.run_stdout "grep f" stdin=\(aqone\entwo\enthree\enfour\enfive .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.script(source, args=None, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, template=None, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, saltenv=None, use_vt=False, bg=False, password=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.script(source, args=None, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, template=None, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, saltenv=None, use_vt=False, bg=False, password=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Download a script from a remote location and execute the script locally. The script can be located on the salt master file server or on an HTTP/FTP server. @@ -153462,7 +153462,7 @@ salt \(aq*\(aq cmd.script salt://scripts/runme.sh stdin=\(aqone\entwo\enthree\en .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.script_retcode(source, args=None, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, python_shell=None, env=None, template=\(aqjinja\(aq, umask=None, timeout=None, reset_system_locale=True, saltenv=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, use_vt=False, password=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.script_retcode(source, args=None, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, python_shell=None, env=None, template=\(aqjinja\(aq, umask=None, timeout=None, reset_system_locale=True, saltenv=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, use_vt=False, password=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Download a script from a remote location and execute the script locally. The script can be located on the salt master file server or on an HTTP/FTP server. @@ -153687,7 +153687,7 @@ salt \(aq*\(aq cmd.script_retcode salt://scripts/runme.sh stdin=\(aqone\entwo\en .UNINDENT .INDENT 0.0 .TP -.B salt.modules.cmdmod.shell(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/zsh\(aq, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, bg=False, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) +.B salt.modules.cmdmod.shell(cmd, cwd=None, stdin=None, runas=None, group=None, shell=\(aq/bin/bash\(aq, env=None, clean_env=False, template=None, rstrip=True, umask=None, output_encoding=None, output_loglevel=\(aqdebug\(aq, log_callback=None, hide_output=False, timeout=None, reset_system_locale=True, ignore_retcode=False, saltenv=None, use_vt=False, bg=False, password=None, prepend_path=None, success_retcodes=None, success_stdout=None, success_stderr=None, **kwargs) Execute the passed command and return the output as a string. .sp New in version 2015.5.0. @@ -192875,7 +192875,7 @@ Passes through all the parameters described in the \fI\%utils.http.query function\fP: .INDENT 7.0 .TP -.B salt.utils.http.query(url, method=\(aqGET\(aq, params=None, data=None, data_file=None, header_dict=None, header_list=None, header_file=None, username=None, password=None, auth=None, decode=False, decode_type=\(aqauto\(aq, status=False, headers=False, text=False, cookies=None, cookie_jar=None, cookie_format=\(aqlwp\(aq, persist_session=False, session_cookie_jar=None, data_render=False, data_renderer=None, header_render=False, header_renderer=None, template_dict=None, test=False, test_url=None, node=\(aqminion\(aq, port=80, opts=None, backend=None, ca_bundle=None, verify_ssl=None, cert=None, text_out=None, headers_out=None, decode_out=None, stream=False, streaming_callback=None, header_callback=None, handle=False, agent=\(aqSalt/3005.3+25.g1184c5ebfa\(aq, hide_fields=None, raise_error=True, formdata=False, formdata_fieldname=None, formdata_filename=None, decode_body=True, **kwargs) +.B salt.utils.http.query(url, method=\(aqGET\(aq, params=None, data=None, data_file=None, header_dict=None, header_list=None, header_file=None, username=None, password=None, auth=None, decode=False, decode_type=\(aqauto\(aq, status=False, headers=False, text=False, cookies=None, cookie_jar=None, cookie_format=\(aqlwp\(aq, persist_session=False, session_cookie_jar=None, data_render=False, data_renderer=None, header_render=False, header_renderer=None, template_dict=None, test=False, test_url=None, node=\(aqminion\(aq, port=80, opts=None, backend=None, ca_bundle=None, verify_ssl=None, cert=None, text_out=None, headers_out=None, decode_out=None, stream=False, streaming_callback=None, header_callback=None, handle=False, agent=\(aqSalt/3005.4+8.ge88ed17326\(aq, hide_fields=None, raise_error=True, formdata=False, formdata_fieldname=None, formdata_filename=None, decode_body=True, **kwargs) Query a resource, and decode the return data .UNINDENT .INDENT 7.0 @@ -448748,7 +448748,7 @@ installed2 .UNINDENT .INDENT 0.0 .TP -.B salt.states.zcbuildout.installed(name, config=\(aqbuildout.cfg\(aq, quiet=False, parts=None, user=None, env=(), buildout_ver=None, test_release=False, distribute=None, new_st=None, offline=False, newest=False, python=\(aq/home/ch3ll/git/salt/.nox/docs\-man\-compress\-false\-update\-true\-clean\-true/bin/python\(aq, debug=False, verbose=False, unless=None, onlyif=None, use_vt=False, loglevel=\(aqdebug\(aq, **kwargs) +.B salt.states.zcbuildout.installed(name, config=\(aqbuildout.cfg\(aq, quiet=False, parts=None, user=None, env=(), buildout_ver=None, test_release=False, distribute=None, new_st=None, offline=False, newest=False, python=\(aq/mnt/c/Users/alyssar/Documents/Git\-Projects/salt\-priv/.nox/docs\-man\-compress\-false\-update\-true\-clean\-true/bin/python\(aq, debug=False, verbose=False, unless=None, onlyif=None, use_vt=False, loglevel=\(aqdebug\(aq, **kwargs) Install buildout in a specific directory .sp It is a thin wrapper to modules.buildout.buildout @@ -468156,6 +468156,34 @@ Fix __env__ and improve cache cleaning see more info at pull #65017. (#65002) .IP \(bu 2 Update to \fIgitpython>=3.1.35\fP due to \fI\%https://github.com/advisories/GHSA\-wfm5\-v35h\-vwf4\fP and \fI\%https://github.com/advisories/GHSA\-cwvm\-v4w8\-q58c\fP (#65167) .UNINDENT +.SS Salt 3005.4 Release Notes +.sp +Version 3005.4 is a CVE security fix release for 3005\&. +.SS Security +.INDENT 0.0 +.IP \(bu 2 +Fix CVE\-2023\-34049 by ensuring we do not use a predictable name for the script and correctly check returncode of scp command. +This only impacts salt\-ssh users using the pre\-flight option. (cve\-2023\-34049) +.IP \(bu 2 +Bump to \fIcryptography==41.0.4\fP due to \fI\%https://github.com/advisories/GHSA\-v8gr\-m533\-ghj9\fP (#65267) +.IP \(bu 2 +Bump to \fIurllib3==1.26.17\fP or \fIurllib3==2.0.6\fP due to \fI\%https://github.com/advisories/GHSA\-v845\-jxx5\-vc9f\fP (#65334) +.IP \(bu 2 +Bump to \fIgitpython==3.1.37\fP due to \fI\%https://github.com/advisories/GHSA\-cwvm\-v4w8\-q58c\fP (#65383) +.UNINDENT +.SS Salt 3005.5 Release Notes +.sp +Version 3005.5 is a CVE security fix release for 3005\&. +.SS Security +.INDENT 0.0 +.IP \(bu 2 +Fix CVE\-2024\-22231 by preventing directory traversal when creating syndic cache directory on the master. +.IP \(bu 2 +Fix CVE\-2024\-22232 Prevent directory traversal attacks in the master\(aqs serve_file method. +.UNINDENT +.sp +These vulnerablities were discovered and reported by: +Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) .SS Salt 3004 Release Notes \- Codename Silicon .SS New Features .SS Transactional System Support (MicroOS) diff --git a/doc/man/spm.1 b/doc/man/spm.1 index a5be93e66a2..96dba9071b5 100644 --- a/doc/man/spm.1 +++ b/doc/man/spm.1 @@ -1,6 +1,6 @@ .\" Man page generated from reStructuredText. . -.TH "SPM" "1" "Oct 16, 2023" "3005" "Salt" +.TH "SPM" "1" "Jan 24, 2024" "3005" "Salt" .SH NAME spm \- Salt Package Manager Command .