Files
IxMxAMAR 3f9e39bb44 v0.3.0: security hardening, audit fixes, 10 new ops, theme UX (matchMedia perf fix + sandbox + leaks + features)
Critical security fixes
-----------------------
- Sandbox Jinja2 in template_render + prompt_template_render (was RCE via shared workflows)
- Drop user home dir from filesystem allow-list (was arbitrary delete/overwrite)
- Confine save_image_with_manifest + sanitize filename basename
- SSRF block on http_get/http_post/notify_webhook (catches 169.254.169.254 metadata)
- Case-insensitive path confinement on Windows

Critical correctness/perf
-------------------------
- halftone_dots: O(N^2) -> O(cells*36); was freezing worker on 1024+ images
- Preserve latent dict keys (noise_mask, batch_index) in latent_math/noise/upscale
- Preserve alpha channel in invert + threshold_binary on RGBA inputs
- Force odd kernel in mask_erode/mask_dilate to prevent H+1 shape mismatch
- Snap aspect_ratio_pick output to multiples of 8 (was crashing SD UNet)

Theme engine (JS)
-----------------
- Hoist window.matchMedia() out of per-frame hot path -> recovers 200 FPS from 25
- Wrap-once draw method to stop stomping other extensions wrappers
- nodeRemoved hook + WeakMap groups + in-place canvas reuse -> three memory leaks closed
- Wire thickness now uses connections_width (the wrap was a no-op vs LiteGraph)
- onGroupAdded auto-themes new groups; budget change triggers redraw
- Gradient caching in synthwave/glassmorphic

New ops (10)
------------
- json_path (programming): dotted/bracketed path extract
- latent_nan_guard, latent_pad_crop (mask_latent)
- mask_from_depth, mask_invert (mask_latent)
- color_match_histogram, image_composite_over (image_pro/value)
- frequency_separation (image_pro/style)
- pad_to_multiple (image_pro/spatial)
- save_images_zip (io_workflow)
- llm_enhance_prompt via Ollama / LM Studio / llama.cpp (prompt)

Theme UX
--------
- Right-click "Theme..." submenu on every node (including vanilla ComfyUI nodes)
- Auto-contrast title text (WCAG AA-based flip)

Tests: 373 Python + 33 JS = 406 passing (added 33 new tests for security + features).
Full review notes in CHANGELOG.md.
2026-05-17 15:41:19 +05:30

69 lines
2.0 KiB
Python

"""End-to-end integration tests for PromptNode."""
import importlib.util
import pathlib
import pytest
PACKAGE_ROOT = pathlib.Path(__file__).resolve().parent.parent.parent
@pytest.fixture(scope="module")
def pkg():
spec = importlib.util.spec_from_file_location(
"megapack_pkg",
PACKAGE_ROOT / "__init__.py",
)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def test_prompt_node_registered(pkg):
assert "UtilMegaPack_Prompt" in pkg.NODE_CLASS_MAPPINGS
assert pkg.NODE_DISPLAY_NAME_MAPPINGS["UtilMegaPack_Prompt"] == "Prompt"
def test_prompt_node_has_four_outputs(pkg):
NodeCls = pkg.NODE_CLASS_MAPPINGS["UtilMegaPack_Prompt"]
assert NodeCls.RETURN_TYPES == ("STRING", "STRING", "LIST", "INT")
assert NodeCls.RETURN_NAMES == ("prompt", "negative", "all_prompts", "token_count")
def test_prompt_node_lists_ops(pkg):
NodeCls = pkg.NODE_CLASS_MAPPINGS["UtilMegaPack_Prompt"]
schema = NodeCls.INPUT_TYPES()
options, _ = schema["required"]["mode"]
# Lower bound — new ops are additive, exact equality was a brittle assertion.
assert len(options) >= 9, f"expected >=9 ops, got {len(options)}"
def test_prompt_node_smoke_one_per_op_type(pkg):
NodeCls = pkg.NODE_CLASS_MAPPINGS["UtilMegaPack_Prompt"]
node = NodeCls()
# batch
assert node.process(
mode="prompt_batch_pick", theme="(use pack default)",
prompts="a\nb\nc", index=0,
)[0] == "a"
# mix
assert node.process(
mode="prompt_mix", theme="(use pack default)",
a="cat", weight_a=1.0, b="dog", weight_b=1.0,
)[0] == "(cat:1.00) (dog:1.00)"
# negative auto build
out = node.process(
mode="negative_auto_build", theme="(use pack default)",
positive="x", preset="realistic",
)
assert "blurry" in out[1]
# token count
assert node.process(
mode="token_count", theme="(use pack default)",
text="hello world",
)[3] == 2