- Restricted file writing to the ComfyUI output directory to prevent arbitrary path access.
105 lines
3.9 KiB
Python
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"}
|