Updated pyproject.toml to change the display name and icon for the project. Enhanced superbeastsai_nodes.py by adding SHA-256 checksum verification for model files and implementing security measures to prevent unsafe module imports, improving overall integrity and safety of the model loading process.

This commit is contained in:
SuperBeastsAI
2025-07-20 21:32:52 +10:00
parent 697a0e9a7e
commit 368ce10322
3 changed files with 60 additions and 4 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

+3 -3
View File
@@ -28,10 +28,10 @@ SOFTWARE.
[project.urls]
Repository = "https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts"
Super Pop Color Adjustement Model (license) = "https://github.com/SuperBeastsAI/SuperBeastsAI-Models/tree/main/SuperPopColorAdjustment"
"Super Pop Color Adjustement Model (license)" = "https://github.com/SuperBeastsAI/SuperBeastsAI-Models/tree/main/SuperPopColorAdjustment"
# Used by Comfy Registry https://comfyregistry.org
[tool.comfy]
PublisherId = "superbeastsai"
DisplayName = "ComfyUI-SuperBeasts"
Icon = ""
DisplayName = "SuperBeasts.AI Nodes"
Icon = "https://github.com/SuperBeastsAI/ComfyUI-SuperBeasts/tree/dev/docs/assets/icon.png"
+57 -1
View File
@@ -49,6 +49,44 @@ _MODEL_REGISTRY: dict[str, dict[str, str]] = {
# Future models / versions can be added here
}
# ---------------------------------------------------------------------------
# Known SHA-256 digests for published weight files. This allows us to verify
# that a downloaded (or pre-existing) file has not been tampered with. When
# a file’s entry is missing we skip verification so development models can be
# iterated quickly – but **release** models **should always** have a digest.
# ---------------------------------------------------------------------------
_MODEL_SHA256: dict[str, str] = {
"SuperBeasts_ColorAdjustment_512px_V1.onnx": "a813fbb091dc7849f460f17e54c70cc6e6ef2984589a0ae94ab308d2957dc4ad",
}
# Helper – compute sha256 digest of a file (hex string)
def _sha256_of_file(path: str, chunk: int = 1 << 20) -> str:
import hashlib
h = hashlib.sha256()
with open(path, "rb") as f:
while True:
blk = f.read(chunk)
if not blk:
break
h.update(blk)
return h.hexdigest()
# Verify checksum if we have one for this filename.
def _verify_model_checksum(path: str):
fn = os.path.basename(path)
expected = _MODEL_SHA256.get(fn)
if not expected:
# No checksum known – skip (development weight)
return
actual = _sha256_of_file(path)
if actual != expected:
raise RuntimeError(
f"Checksum mismatch for '{fn}'.\nExpected: {expected}\nActual: {actual}\n"
"The file appears to be corrupted or has been tampered with. Delete it and rerun to re-download, "
"or obtain a clean copy from the official model repository."
)
def _latest_version(family: str) -> str:
versions = list(_MODEL_REGISTRY.get(family, {}).keys())
@@ -288,6 +326,13 @@ class SBLoadModel:
f"Attempted path: {rel_path}\n{e}"
)
# --- Security: check SHA-256 digest (raises if mismatch) ---
try:
_verify_model_checksum(model_path)
except Exception as _ck_err:
# Surface early so users see a clear error in ComfyUI
raise
# Use cache if possible
if model_path in self._model_cache:
return (self._model_cache[model_path], )
@@ -316,8 +361,19 @@ class SBLoadModel:
from safetensors.torch import load_file as safe_load
import importlib.util, sys, pathlib
model_dir = pathlib.Path(__file__).parent / 'torch_models'
# ------------------------------------------------------------------
# Safety guard – ensure we never import a file outside our plugin dir
# even if the relative path is somehow manipulated.
# ------------------------------------------------------------------
module_path = (model_dir / 'contextual_residual_unet_v4.py').resolve()
allowed_root = model_dir.resolve()
if not str(module_path).startswith(str(allowed_root)):
raise RuntimeError("Blocked unsafe module import: path escapes plugin directory")
sys.path.append(str(model_dir))
spec = importlib.util.spec_from_file_location('contextual_residual_unet_v4', model_dir / 'contextual_residual_unet_v4.py')
spec = importlib.util.spec_from_file_location('contextual_residual_unet_v4', module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore
ContextualResidualUNetV4Torch = module.ContextualResidualUNetV4Torch