From 76d534adb75d81599c374190bf1e8bce145e63ff Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Thu, 15 Oct 2026 19:17:23 +0300 Subject: [PATCH] fix: symlink loops no longer stall the .gguf listing; a name with .. is refused after a link too (attacker round 12) Co-Authored-By: Claude Sonnet 5.5 --- modules/loaders/gguf_support.py | 10 +++++++++- modules/loaders/tests/test_gguf_support.py | 17 +++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/modules/loaders/gguf_support.py b/modules/loaders/gguf_support.py index 0d5fbb8c..86583158 100644 --- a/modules/loaders/gguf_support.py +++ b/modules/loaders/gguf_support.py @@ -90,7 +90,13 @@ def gguf_names(folder: str) -> list[str]: for root in roots: if not root or not os.path.isdir(root): continue - for dirpath, _dirs, files in os.walk(root, followlinks=True): + walked: set[str] = set() + for dirpath, dirs, files in os.walk(root, followlinks=True): + real = os.path.realpath(dirpath) + if real in walked: # a symlink loop: this folder was already listed + dirs[:] = [] + continue + walked.add(real) for f in files: if not is_gguf(f): continue @@ -113,6 +119,8 @@ def gguf_path(folder: str, name: str) -> Optional[str]: roots += list(folder_paths.get_folder_paths(extra) or []) except Exception: pass + if ".." in name.replace("\\", "/").split("/"): + return None for root in roots: cand = os.path.join(root or "", name) if not os.path.normpath(cand).startswith(os.path.normpath(root or ".") + os.sep): diff --git a/modules/loaders/tests/test_gguf_support.py b/modules/loaders/tests/test_gguf_support.py index a430372d..091a3bc6 100644 --- a/modules/loaders/tests/test_gguf_support.py +++ b/modules/loaders/tests/test_gguf_support.py @@ -369,9 +369,14 @@ def test_a_symlinked_gguf_inside_the_model_folder_is_found(fake_folders): assert gguf_support.gguf_path("text_encoders", "linked.gguf") -def test_a_pack_in_managers_disabled_folder_is_not_used(fake_folders): - pack = fake_folders / "custom_nodes" / ".disabled" / "ComfyUI-GGUF" - pack.mkdir(parents=True) - (pack / "ops.py").write_text("") - (pack / "loader.py").write_text("def gguf_sd_loader(p): pass\n") - assert gguf_support._pack_dir() is None + +def test_symlink_loops_do_not_hang_the_listing_and_dotdot_after_a_link_is_refused(fake_folders): + enc = fake_folders / "text_encoders" + (enc / "a").symlink_to(enc) + (enc / "b").symlink_to(enc) + assert gguf_support.gguf_names("text_encoders") == ["a/gemma-Q5_K.gguf"] or "gemma-Q5_K.gguf" in gguf_support.gguf_names("text_encoders") + (fake_folders / "outside").mkdir() + (fake_folders / "outside" / "inner").mkdir() + (fake_folders / "outside" / "s.gguf").write_bytes(b"x") + (enc / "lnk").symlink_to(fake_folders / "outside" / "inner") + assert gguf_support.gguf_path("text_encoders", "lnk/../s.gguf") is None