From 16cae1c4b740afa1b45b445e309159c4f62eae9d Mon Sep 17 00:00:00 2001 From: Tyler Levy Conde Date: Tue, 2 Apr 2024 15:57:58 -0600 Subject: [PATCH 1/5] file.managed correctly handles paths containing a '#' --- salt/fileclient.py | 2 ++ tests/pytests/unit/fileclient/test_fileclient.py | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/salt/fileclient.py b/salt/fileclient.py index fad1da72ece..90b5ee47cc9 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -481,9 +481,11 @@ def get_url( """ Get a single file from a URL. """ + url = urllib.parse.quote(url, safe=":/") url_data = urllib.parse.urlparse(url) url_scheme = url_data.scheme url_path = os.path.join(url_data.netloc, url_data.path).rstrip(os.sep) + url_path = urllib.parse.unquote(url_path) # If dest is a directory, rewrite dest with filename if dest is not None and (os.path.isdir(dest) or dest.endswith(("/", "\\"))): diff --git a/tests/pytests/unit/fileclient/test_fileclient.py b/tests/pytests/unit/fileclient/test_fileclient.py index 94512e50633..a423e075e0d 100644 --- a/tests/pytests/unit/fileclient/test_fileclient.py +++ b/tests/pytests/unit/fileclient/test_fileclient.py @@ -223,3 +223,17 @@ def test_get_file_client(file_client): with patch("salt.fileclient.RemoteClient", MagicMock(return_value="remote_client")): ret = fileclient.get_file_client(minion_opts) assert "remote_client" == ret +def test_get_url_with_hash(client_opts): + """ + Test get_url function with a URL containing a hash character. + """ + with patch("os.path.isfile", return_value=True): + with patch("os.makedirs", return_value=None): + with patch("urllib.request.urlretrieve", return_value=None): + client = fileclient.Client(client_opts) + url = "file:///path/to/file#with#hash" + dest = "/mocked/destination" + + result = client.get_url(url, dest) + + assert result == "/path/to/file#with#hash" From 813f60a565f0b1226eb30b8b8c9968d464a41d67 Mon Sep 17 00:00:00 2001 From: Tyler Levy Conde Date: Tue, 2 Apr 2024 16:19:58 -0600 Subject: [PATCH 2/5] Black and changelog added for 63060 --- changelog/63060.fixed.md | 1 + tests/pytests/unit/fileclient/test_fileclient.py | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 changelog/63060.fixed.md diff --git a/changelog/63060.fixed.md b/changelog/63060.fixed.md new file mode 100644 index 00000000000..e0290447aca --- /dev/null +++ b/changelog/63060.fixed.md @@ -0,0 +1 @@ +file.managed correctly handles file path with '#' diff --git a/tests/pytests/unit/fileclient/test_fileclient.py b/tests/pytests/unit/fileclient/test_fileclient.py index a423e075e0d..0892c6b35f6 100644 --- a/tests/pytests/unit/fileclient/test_fileclient.py +++ b/tests/pytests/unit/fileclient/test_fileclient.py @@ -223,6 +223,8 @@ def test_get_file_client(file_client): with patch("salt.fileclient.RemoteClient", MagicMock(return_value="remote_client")): ret = fileclient.get_file_client(minion_opts) assert "remote_client" == ret + + def test_get_url_with_hash(client_opts): """ Test get_url function with a URL containing a hash character. From aa65aabe2d7eea38bb60434ddc798a24f9bb1b6b Mon Sep 17 00:00:00 2001 From: Tyler Levy Conde Date: Thu, 4 Apr 2024 11:13:18 -0600 Subject: [PATCH 3/5] Passing pre-commit --- tests/pytests/unit/fileclient/test_fileclient.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/pytests/unit/fileclient/test_fileclient.py b/tests/pytests/unit/fileclient/test_fileclient.py index 0892c6b35f6..b9222dbe688 100644 --- a/tests/pytests/unit/fileclient/test_fileclient.py +++ b/tests/pytests/unit/fileclient/test_fileclient.py @@ -8,9 +8,12 @@ import pytest -import salt.utils.files -from salt import fileclient -from tests.support.mock import AsyncMock, MagicMock, Mock, patch +try: + import salt.utils.files + from salt import fileclient + from tests.support.mock import AsyncMock, MagicMock, Mock, patch +except ImportError: + ... log = logging.getLogger(__name__) From e5fbd243c4fc3dcdbcaa60f1b186fcc5dffae45a Mon Sep 17 00:00:00 2001 From: Tyler Levy Conde Date: Fri, 5 Apr 2024 10:33:05 -0600 Subject: [PATCH 4/5] revert test changes --- tests/pytests/unit/fileclient/test_fileclient.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tests/pytests/unit/fileclient/test_fileclient.py b/tests/pytests/unit/fileclient/test_fileclient.py index b9222dbe688..0892c6b35f6 100644 --- a/tests/pytests/unit/fileclient/test_fileclient.py +++ b/tests/pytests/unit/fileclient/test_fileclient.py @@ -8,12 +8,9 @@ import pytest -try: - import salt.utils.files - from salt import fileclient - from tests.support.mock import AsyncMock, MagicMock, Mock, patch -except ImportError: - ... +import salt.utils.files +from salt import fileclient +from tests.support.mock import AsyncMock, MagicMock, Mock, patch log = logging.getLogger(__name__) From a84653d266bbd3fc886c8a87e9a60f9853c46e1b Mon Sep 17 00:00:00 2001 From: Tyler Levy Conde Date: Mon, 8 Apr 2024 13:01:54 -0600 Subject: [PATCH 5/5] set allow_fragments to False --- salt/fileclient.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/salt/fileclient.py b/salt/fileclient.py index 90b5ee47cc9..32c5cd0d948 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -481,11 +481,9 @@ def get_url( """ Get a single file from a URL. """ - url = urllib.parse.quote(url, safe=":/") - url_data = urllib.parse.urlparse(url) + url_data = urllib.parse.urlparse(url, allow_fragments=False) url_scheme = url_data.scheme url_path = os.path.join(url_data.netloc, url_data.path).rstrip(os.sep) - url_path = urllib.parse.unquote(url_path) # If dest is a directory, rewrite dest with filename if dest is not None and (os.path.isdir(dest) or dest.endswith(("/", "\\"))):