This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new e7ffeb7c39b reject mixed-separator path traversal in imap attachment 
names (#70665)
e7ffeb7c39b is described below

commit e7ffeb7c39b020cceaf2e26df8343980f896d2f4
Author: Samina <[email protected]>
AuthorDate: Sat Aug 1 09:06:33 2026 +0530

    reject mixed-separator path traversal in imap attachment names (#70665)
    
    * reject mixed-separator path traversal in imap attachment names
    
    * Use os.altsep for separator-agnostic imap traversal check
    
    Signed-off-by: bibi samina <[email protected]>
    
    ---------
    
    Signed-off-by: bibi samina <[email protected]>
---
 .../imap/src/airflow/providers/imap/hooks/imap.py  | 10 ++++++-
 providers/imap/tests/unit/imap/hooks/test_imap.py  | 33 ++++++++++++++++++++++
 2 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/providers/imap/src/airflow/providers/imap/hooks/imap.py 
b/providers/imap/src/airflow/providers/imap/hooks/imap.py
index 40a4711d906..65d80bd3a0d 100644
--- a/providers/imap/src/airflow/providers/imap/hooks/imap.py
+++ b/providers/imap/src/airflow/providers/imap/hooks/imap.py
@@ -332,7 +332,15 @@ class ImapHook(BaseHook):
         return os.path.islink(self._correct_path(name, local_output_directory))
 
     def _is_escaping_current_directory(self, name: str) -> bool:
-        return f"..{os.sep}" in name
+        # On Windows os.altsep is "/", so a "../" attachment name traverses 
even
+        # though os.sep is a backslash and the old f"..{os.sep}" check missed 
it.
+        # Split on every separator the host recognises and reject any ".."
+        # component. Using os.altsep keeps backslashes as ordinary characters 
on
+        # POSIX, where they are legal in filenames.
+        normalised = name.replace(os.sep, "/")
+        if os.altsep:
+            normalised = normalised.replace(os.altsep, "/")
+        return any(part == ".." for part in normalised.split("/"))
 
     def _correct_path(self, name: str, local_output_directory: str) -> str:
         return (
diff --git a/providers/imap/tests/unit/imap/hooks/test_imap.py 
b/providers/imap/tests/unit/imap/hooks/test_imap.py
index bb6d4fc7a68..86fda41522e 100644
--- a/providers/imap/tests/unit/imap/hooks/test_imap.py
+++ b/providers/imap/tests/unit/imap/hooks/test_imap.py
@@ -409,6 +409,39 @@ class TestImapHook:
         mock_open_method.assert_not_called()
         mock_open_method.return_value.write.assert_not_called()
 
+    @patch("airflow.providers.imap.hooks.imap.os.altsep", None)
+    @patch("airflow.providers.imap.hooks.imap.os.sep", "/")
+    @pytest.mark.parametrize(
+        ("name", "expected"),
+        [
+            ("../test1.csv", True),
+            ("subdir/../../test1.csv", True),
+            ("..", True),
+            ("test1.csv", False),
+            ("report..final.csv", False),
+            # On POSIX a backslash is an ordinary filename character, not a 
separator.
+            ("..\\..\\test1.csv", False),
+        ],
+    )
+    def test_is_escaping_current_directory_on_posix(self, name, expected):
+        assert ImapHook()._is_escaping_current_directory(name) is expected
+
+    @patch("airflow.providers.imap.hooks.imap.os.altsep", "/")
+    @patch("airflow.providers.imap.hooks.imap.os.sep", "\\")
+    @pytest.mark.parametrize(
+        ("name", "expected"),
+        [
+            ("..\\..\\test1.csv", True),
+            ("../../test1.csv", True),
+            ("subdir\\..\\..\\test1.csv", True),
+            ("..", True),
+            ("test1.csv", False),
+            ("report..final.csv", False),
+        ],
+    )
+    def test_is_escaping_current_directory_on_windows(self, name, expected):
+        assert ImapHook()._is_escaping_current_directory(name) is expected
+
     @patch("airflow.providers.imap.hooks.imap.os.path.islink", 
return_value=True)
     @patch(open_string, new_callable=mock_open)
     @patch(imaplib_string)

Reply via email to