From dfe0dc5dad851864c76bc9ad769719d3f0f2f9f5 Mon Sep 17 00:00:00 2001 From: stompy001 Date: Tue, 4 Aug 2026 17:49:00 +0530 Subject: [PATCH] fix: patch path traversal vulnerability, add spandrel fallback, and resolve symlink escapes --- __pycache__/__init__.cpython-311.pyc | Bin 0 -> 2076 bytes __pycache__/quick_merge_node.cpython-311.pyc | Bin 0 -> 63916 bytes pyproject.toml | 2 +- quick_merge_node.py | 56 ++++++++++++++++--- 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 __pycache__/__init__.cpython-311.pyc create mode 100644 __pycache__/quick_merge_node.cpython-311.pyc diff --git a/__pycache__/__init__.cpython-311.pyc b/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a17a5ff5263835095c88eb4add37c0e56992a1c7 GIT binary patch literal 2076 zcma(SNo*5W^nH&VkL__5a6$;QjM-FIv5N{-Q9x87F>O+^G@)u^9EQpK!I@x>-B}U{ zj+8?WJ)nwPE48Rm7ut$Tj~ppI^g2eCutti6)Dt(WTp~_=e;i|nMe3h7&u^c%{Qv!H zG#Un6vCjFWSODN3#x$32n|OK|iEn@cC3(m+eo2OSyf5RUzAque`9S6FpxxbVCjbJJ zqyD_V5Xc0O4yXZ*WIAYocHnQ21~YsPDE}kLgeaMXMCb|goK&9sBN4=x!}saG#LIwC zAB*`tw+1;_BW2h?<;!bl9-}b-K^ALS^R00quvon9wbZN-%Dy}Jg}eAR7J-}l4uRFt zo`e($=mf1+e7Jobi;WAsWv4%VZU~7h_SF1?)m+o9FW?upe&|*pW+w z3jen%8om9Bj-OgCZ`4P|jzGqs|=+O=rsa+;pIA}eZg z(OyV!1{;on`xxAIrG!fRI7-78>py(`oA`;XDObe_)yS#y;)G_J7(}FMN!6*UE1GH| zW2uUz(jjrxHz?ZgqwgzRhi$8v)O6F5^LZ9RFRrA->AWhNs<6w|SnIZ94Fx zMZ?N#3vRSQN`)e|^Qs#e^E7G0C>m~uTw=Alfs&!=mg`?CYPuUX?S+z2R8-S+!^)zn zT$L28%jL}LC{VAdifzdY7;4uDid8z@u=OG){BDn-eqn2dN~MylT$OXG>2?o@TTG4b`Trnk95c{sAFKbkjU#o8_vWjh5 z#e$?4scJTbNpIvyZ{%4?(sa#|q*Up;8{2NB)INS=KPJR>k$Dksgmrk_f3&veaBbgU zJ?P)tQ3phhzqc+x|A6C<)#C3yil2NKKUs+nRpUdb4x~I(;otZwa_jTDj|-(Xh2E-g zXyfCGaI7jEb3EAE>>8*8388fp)r9?zghLO}@}r87tO`k|33U!_wzjtF9l-b3p(9Wx zn|##iT)2PX@3{wa&XiP1U#_MvqXePCD^*@`c*V0BtMKDhe%#^5SuW@(^i_rUFUNmA z^T&yMz4t@T!Et9|zH)HBB3!Bpmz*YGf_tl7gDkx=X~&yFkF#sl$x4s1^kJ4ds$I!0 zSF_7Z=s`ueQ59}DZNTy`6IMWk<=&{kjAN63ik?wF~>b;Q@ z++W8D;wS7{P7t@#8=^PENgS^6CW`HDL@yrdRe$Q8B{RXfyClj?GS=AD)vYhbPi4UG%f`S!r}^COvXtYGy7GHewjwU{|LhFvK>7+l?C? zPX}H|Ioy@aOpF7}KxBwj@kjDQvU&ba3^yB literal 0 HcmV?d00001 diff --git a/__pycache__/quick_merge_node.cpython-311.pyc b/__pycache__/quick_merge_node.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a281674df7edc801ba89f89bdd4a23f546c5d0ee GIT binary patch literal 63916 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�ai4$!#?IJ_eEo8Jp3Hfsf}9y7>Q3+CULr+qyvXq&u-mv-}6)< zaTzyFbY{?X+ssL`%=a}5Wl89~tRf^Ydain^VwSPIjL6HJ1E8qzT>Vtd+{5y6A}@Es zJo@_#VC+^IpLKYu2yBSkfAt5xZBk)zNu6ozoH8}z13o?QD2G}7eDl*yY^o=wdf$rw zK5839*IA7$**z?~M`ZUPcC|jJZVad!eeyq7{i({ojGKl@)9|4kO@gl4EK$z})$;-M zJX!!%wI!ln3aZxv^em=3M199O0*Y&NnVSSrx(B&?NL?IMR|M1*USK0u6Lob^T^~@_ zV|61@H&P$pu!?ae(1U3}=B{7Y{i-dl7Z+5|1=MpK@7*~PkrxH!r2#qBA+f;{(Sqgm zL|z}1HwWa+Sl&wHtuUXK8=`yVEQ#xmpu8m@Z^805B5wnZ?)x~3h(?{MFi;dK+`aBb zlz#!32~4}U-QSKR6GtgYd6|$wmSkjdDiA65yC6TYZv%;H8MDd;;&8f^+Z}BUXIiPC zb8mggY7J*wt=oIHb#~vh)m{j`!dVOJASm2e+FjjRw0}-bvL|}3UYi)PUY!~1nT)@U zScc&(z4zKk*VTd1k#KsK?=yB z%*XxJP7VY*;5<7edidX=2iZM}|BeJHDe$q2lU!b^=t!`@Pl58F^xGsb_|c81 z(O?pM%|&q^C^61U`XEvZ;eOQPrdXPcu%|`GqO40Ev@J`ji@2kHknXzdM~~>uk!E z7cHf|JRnEU|Gh`Yiq3SyRP+mH5KwK9aKY}DePOkHbF+MTZxfUwwm{Bl|F6_0Gr@pW zw_r6&MnN_h&gD)ID32ZvNzfgw9R63L!Sy{CY3#oc{7-DJicQy>8?)#d$i zRx1D`)tk1Jr47)pj&0CCHer=rps}&eK^nilV~2eKoaT%DztiyE(DiJv%7Cf<2qFduuz8Cp$gtD6CH~DUW;DNzC~bD*F5{toN>x(11;ik!N_QLkF{&aGC64-wv3GpjPy;06|7f(EkVCuJP{x literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml index f9fc7e1..c2ebe36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "ComfyUI-Quick-Merge" -version = "1.0.1" +version = "1.0.2" description = "High-performance modular layer blending and folder batch processing for ComfyUI." license = { text = "MIT License" } authors = [ diff --git a/quick_merge_node.py b/quick_merge_node.py index 1de4f58..dd66014 100644 --- a/quick_merge_node.py +++ b/quick_merge_node.py @@ -8,7 +8,12 @@ import numpy as np import folder_paths import comfy.model_management as mm from comfy.utils import load_torch_file, ProgressBar -import spandrel + +try: + import spandrel + SPANDREL_AVAILABLE = True +except ImportError: + SPANDREL_AVAILABLE = False try: from transformers import AutoModelForImageSegmentation @@ -24,6 +29,7 @@ try: except ImportError: ONNXRUNTIME_AVAILABLE = False + class QuickMergeNode: def __init__(self): self.cached_bg_model = None @@ -391,6 +397,29 @@ class QuickMergeNode: bg_blur[:, :, start_y:start_y+new_h, start_x:start_x+new_w] = tensor_scaled return bg_blur + # --- SECURE PATH VALIDATION ENGINE --- + def get_secure_path(self, user_path): + """ + Validates that the provided folder path is strictly inside ComfyUI's input directory. + Prevents directory traversal (../) and symlink security exploits. + """ + if not user_path or user_path == "📁 Type or paste folder path here...": + return None + + # Use realpath to resolve malicious symlinks pointing outside the directory + base_dir = os.path.realpath(folder_paths.get_input_directory()) + full_path = os.path.realpath(os.path.join(base_dir, user_path)) + + try: + if os.path.commonpath([base_dir, full_path]) != base_dir: + print(f"[Quick Merge] SECURITY BLOCK: Attempted path traversal outside input directory -> {user_path}") + return None + return full_path + except ValueError: + # Handles edge cases where paths exist on different drives (Windows) + print(f"[Quick Merge] SECURITY BLOCK: Invalid path (different drive) -> {user_path}") + return None + def load_folder_images(self, folder_path, device, is_active): if not is_active or not os.path.isdir(folder_path): return None @@ -443,8 +472,8 @@ class QuickMergeNode: batch_processing = kwargs.get("📁 batch_processing", False) foreground_batch = kwargs.get("📁 foreground_batch", False) background_batch = kwargs.get("📁 background_batch", False) - fg_batch_folder = kwargs.get("📁 fg_batch_folder", "") - bg_batch_folder = kwargs.get("📁 bg_batch_folder", "") + fg_batch_folder = kwargs.get("📁 fg_batch_folder", "").strip() + bg_batch_folder = kwargs.get("📁 bg_batch_folder", "").strip() batch_pairing_mode = kwargs.get("📁 batch_pairing_mode", "1-to-1 Match (Sequential)") batch_chunk_size = kwargs.get("📁 batch_chunk_size", 4) @@ -510,7 +539,7 @@ class QuickMergeNode: pixel_smoothing = kwargs.get("🖼️ pixel_smoothing", "bicubic") interpolation = pixel_smoothing if pixel_smoothing != "lanczos" else "bicubic" - # --- 1. DIRECTORY CONDITIONAL PROCESSING OVERRIDES --- + # --- 1. DIRECTORY CONDITIONAL PROCESSING OVERRIDES (SECURED) --- bg_input = kwargs.get("Background", None) fg_input = kwargs.get("Foreground (optional)", None) mask_input = kwargs.get("mask (optional)", None) @@ -520,10 +549,14 @@ class QuickMergeNode: if bg_batch_folder == default_folder_text: bg_batch_folder = "" if batch_processing: - if background_batch and bg_batch_folder and os.path.isdir(bg_batch_folder): - bg_input = self.load_folder_images(bg_batch_folder, device, True) - if foreground_batch and fg_batch_folder and os.path.isdir(fg_batch_folder): - fg_input = self.load_folder_images(fg_batch_folder, device, True) + if background_batch and bg_batch_folder: + secure_bg = self.get_secure_path(bg_batch_folder) + if secure_bg and os.path.isdir(secure_bg): + bg_input = self.load_folder_images(secure_bg, device, True) + if foreground_batch and fg_batch_folder: + secure_fg = self.get_secure_path(fg_batch_folder) + if secure_fg and os.path.isdir(secure_fg): + fg_input = self.load_folder_images(secure_fg, device, True) has_bg = bg_input is not None has_fg = fg_input is not None @@ -670,6 +703,11 @@ class QuickMergeNode: if not ai_upscale or ai_upscale_model == "None Found": pbar.update_absolute(end_pct) return t_img + + if not SPANDREL_AVAILABLE: + print("[Quick Merge] Spandrel not available. Skipping upscale.") + pbar.update_absolute(end_pct) + return t_img chunk_b, orig_c, orig_h, orig_w = t_img.shape target_out_h = max(8, round(orig_h * upscale_by)) @@ -686,7 +724,9 @@ class QuickMergeNode: os.makedirs(dl_dir, exist_ok=True) model_path = os.path.join(dl_dir, "RealESRGAN_x2plus.pth") try: + print(f"[Quick Merge] Downloading RealESRGAN_x2plus.pth... Please wait, the UI may pause.") urllib.request.urlretrieve("https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.1/RealESRGAN_x2plus.pth", model_path) + print(f"[Quick Merge] Download complete!") except Exception: pbar.update_absolute(end_pct) return t_img