Replace all bare ComfyUI internal imports with centralized _load_module()
using importlib.util.spec_from_file_location(). This eliminates sys.path
shadowing attacks, removes the remove/re-insert gap window, and makes
import resolution deterministic regardless of working directory, install
layout, or prior sys.path state.
node_runtime.py — core rewrite:
- Add _load_module(): centralized importlib isolation layer that loads
modules from explicit file paths verified against a ComfyUI root
confirmed by _is_comfyui_directory() (checks for nodes.py marker)
- Add _is_comfyui_directory(): structural verification of candidate paths
- Add _find_from_extension_location(): walk up from __file__ via realpath
to find ComfyUI root (handles symlinked installs)
- Rewrite get_comfyui_path(): multi-strategy fallback chain:
(1) COMFYUI_PATH env var with verification, (2) relative extension walk,
(3) CWD walk as depth-limited last resort (max 20 levels)
- Replace bare 'from nodes import NODE_CLASS_MAPPINGS' with _load_module()
- Replace bare 'import execution/server' with _load_module()
- Replace bare 'from main import load_extra_path_config' with _load_module()
(alias "comfy_main" to prevent sys.modules cache collision)
- Replace bare 'from utils.extra_config import ...' with _load_module()
- Replace bare 'import comfy.options/cli_args' with _load_module()
- Replace bare 'import cuda_malloc' with _load_module()
- Rewrite add_comfyui_directory_to_sys_path(): idempotent insert-once,
no remove/re-insert gap window
- Rewrite import_custom_nodes(): _load_module() for execution/nodes/server,
eliminated sys.path.remove() that created a gap window for lazy imports
- Rewrite get_node_class_mappings(): _load_module() for nodes.py
- Replace all print() statements with logging.DEBUG (no path leaks to stdout)
- Add None guards on args attributes after cli_args module load
generator/render.py — generated script isolation:
- Embed _load_module(), _is_comfyui_directory(), _find_from_extension_location()
in generated scripts via inspect.getsource()
- Add importlib.util and logging imports to generated output
- Generated scripts now inherit full importlib isolation guarantee — zero
bare imports of ComfyUI internals
generator/generated_helpers.py:
- Export _load_module, _is_comfyui_directory, _find_from_extension_location
for use by render.py
tests/test_import_path_resolution.py — new (14 test cases):
- TestIsComfyuiDirectory: structural verification against real ComfyUI checkout,
rejection of empty/nonexistent directories
- TestLoadModule: module loading from explicit path, graceful None for missing
- TestShadowingResistance: malicious main.py and nodes.py at sys.path[0] are
ignored; _load_module() always loads from verified file path
- TestSysPathIdempotence: insert-once behavior, no remove/re-insert in
import_custom_nodes(), no print() statements in module source
- TestGeneratedScriptIsolation: generated output contains _load_module() and
spec_from_file_location; zero bare ComfyUI imports
- TestGetComfyuiPath: env var strategy with verification, fallback chain
tests/test_upscale_model_loader_export.py — updated assertions:
- Replace "import comfy.options" / "import cuda_malloc" checks with
_load_module() string pattern matching ("comfy.options", "cuda_malloc")
- Update ordering assertion for cuda_malloc/torch import sequence
Fixes: #105, #117 (utils package shadowing), #19 (FileNotFoundError on Windows)
Addresses security audit findings: #1 (COMFYUI_PATH poisoning), #2 (directory
collision), #3 (main.py shadowing), #6 (gap window race), #7 (unbounded walk),
#8 (path confusion), #11 (stdout path leak), #12 (symlink resolution)
Spec: docs/specs/harden-import-path-resolution.html
Tests
This directory contains two test paths:
- unit tests for exporter behavior
- runtime validation for committed workflow fixtures
Use the lightweight path for routine contributor validation, and use the runtime path when you need end-to-end confidence against a real ComfyUI checkout.
Prerequisites
- Run commands from the repo root.
- Use
uvfor all project commands. - Install the repo environment first:
uv sync
- Runtime validation also needs a working ComfyUI checkout.
Set
COMFYUI_PATHto that checkout, or run the tests from a location where a parent directory containsComfyUI. - Runtime-tier tests do not download missing models automatically.
If a required model is missing, the fixture fails with
model provisioning failure. Use--print-download-planto print the manual download commands.
Runtime Model Requirements
The current runtime-capable fixtures require these model files in the target ComfyUI checkout:
text-to-image:models/checkpoints/v1-5-pruned-emaonly-fp16.safetensorsupscale-model-loader:models/upscale_models/RealESRGAN_x4plus.safetensors
Unit Tests
Run the exporter-focused unit test module:
uv run python -m unittest tests.test_upscale_model_loader_export
Run the runtime-harness unit test module:
uv run python -m unittest tests.test_runtime_validation_harness
Run all unittest-discoverable tests under tests:
uv run python -m unittest discover -s tests
Runtime Tests
The runtime harness lives at tests/runtime/run_runtime_validation.py.
Fast Tier
Fast tier validates export behavior against committed fixtures without requiring a full ComfyUI runtime for every fixture. This is the recommended default validation lane for routine changes.
Run all fast-tier compatible fixtures:
uv run python tests/runtime/run_runtime_validation.py --tier fast --fixture all
Run one fixture:
uv run python tests/runtime/run_runtime_validation.py --tier fast --fixture unsafe-kwargs
Runtime Tier
Runtime tier exports inside a real ComfyUI checkout and executes generated Python for runtime-capable fixtures. This is the heavier validation lane for changes that need end-to-end runtime confidence.
Run all runtime-capable fixtures:
uv run python tests/runtime/run_runtime_validation.py --tier runtime --fixture all
Run one runtime-capable fixture:
uv run python tests/runtime/run_runtime_validation.py --tier runtime --fixture text-to-image
Print download commands for missing models instead of failing immediately:
uv run python tests/runtime/run_runtime_validation.py --tier runtime --fixture all --print-download-plan
Runtime Fixture Names
Current committed fixtures:
upscale-model-loadertext-to-imageunsafe-kwargssubgraph-identifiersreused-node-class-branchessecondary-output-selection
Notes:
--tier runtimeonly runs fixtures marked runtime-capable.--tier fastonly runs fixtures with local test mappings.reused-node-class-branchesprotects repeated node-class usage and branch wiring in the fast tier.secondary-output-selectionprotects non-zero output index wiring in the fast tier.
Troubleshooting
No module named ...: runuv syncin this repo, and for runtime-tier failures also make sure the target ComfyUI checkout has its own dependencies installed.Could not find a valid ComfyUI checkout for runtime validation.: setCOMFYUI_PATHto your ComfyUI checkout, or run the tests from a directory layout where a parent folder containsComfyUI.Missing models for ...: runtime-tier tests do not fetch models for you. Rerun with--print-download-planto print the expectedcurlcommands and target model directories, then install the files manually.No selected fixtures are runtime-capable for this tier.: choose a runtime-capable fixture such astext-to-imageorupscale-model-loader.- Generated script execution fails because files or models are missing:
confirm the required models exist under the target ComfyUI checkout and that any staged inputs can be copied into its
input/directory.