Files
artyclaw d07ef87121 v1.0.1: Security fix
- Restricted file writing to the ComfyUI output directory to prevent arbitrary path access.
2026-04-13 13:41:59 +02:00

105 lines
3.9 KiB
Python

# ======================================================================
# Node Pack: artyclaw-comfy
# Author: artyclaw
# Version: 1.0.1
# Description: A power-user toolkit for prompt logic, IO, and RNG.
# Changes:
# - 1.0.1: Security fix - Restricted file writing to the ComfyUI output directory
# to prevent arbitrary path access.
# ======================================================================
import os
import re
from datetime import datetime
import traceback
import folder_paths
NODE_NAME = "DebugTextSaver"
class DebugTextSaver:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": ("STRING", {"multiline": True, "default": ""}),
"output_subfolder": ("STRING", {"default": "debug"}),
"file_name": ("STRING", {"default": "debug_log"}),
},
"optional": {
"file_extension": ("STRING", {"default": "txt"}),
"append": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("file_path",)
FUNCTION = "save"
CATEGORY = "text/utils"
def save(self, string, output_subfolder="debug", file_name="debug_log",
file_extension="txt", append=False):
print("[DebugTextSaver] --- run start ---")
# Ensure we have a str
if not isinstance(string, str):
string = str(string)
def normalize_escapes(s: str) -> str:
# Convert common literal escapes to real characters
s = s.replace('\\r\\n', '\n').replace('\\n', '\n').replace('\\r', '\n').replace('\\t', '\t')
# Collapse double backslashes -> single backslash
s = s.replace('\\\\', '\\')
return s
clean_text = normalize_escapes(string)
# Normalize any remaining CR/LF to single \n
clean_text = re.sub(r'\r\n?', '\n', clean_text)
# Timestamp header
timestamp = datetime.now().strftime("[%Y-%m-%d %H:%M:%S]")
clean_text = f"{timestamp}\n{clean_text.strip()}\n"
# --- SECURITY FIX: Restrict to ComfyUI Output Directory ---
output_dir = folder_paths.get_output_directory()
# Remove any leading slashes or drive letters to treat subfolder as relative
safe_subfolder = os.path.normpath(output_subfolder).lstrip(os.sep).lstrip("/")
# Prevent directory traversal (e.g., "../../")
safe_subfolder = safe_subfolder.replace("..", "")
base_dir = os.path.join(output_dir, safe_subfolder)
# Final safety check: ensure the resulting path is still inside the output directory
if os.path.commonpath([os.path.abspath(output_dir), os.path.abspath(base_dir)]) != os.path.abspath(output_dir):
print(f"[DebugTextSaver] Security block: attempted to write to {base_dir}")
return ("",)
os.makedirs(base_dir, exist_ok=True)
safe_name = re.sub(r"[^\w\-.]", "_", file_name.strip())
if not safe_name:
safe_name = datetime.now().strftime("debug_%Y%m%d_%H%M%S")
ext = file_extension.strip().lstrip(".") or "txt"
file_path = os.path.join(base_dir, f"{safe_name}.{ext}")
# Choose mode
mode = "a" if append else "w"
# Write (append or overwrite)
try:
with open(file_path, mode, encoding="utf-8", newline="\n") as f:
f.write(clean_text)
print(f"[DebugTextSaver] Saved to: {file_path}")
except Exception as e:
print("[DebugTextSaver] ERROR while writing file!")
traceback.print_exc()
return ("",)
print("[DebugTextSaver] --- run end ---")
return (file_path,)
NODE_CLASS_MAPPINGS = {"DebugTextSaver": DebugTextSaver}
NODE_DISPLAY_NAME_MAPPINGS = {"DebugTextSaver": "Debug Text Saver"}