Pins that supports_cast() lets fp8-capable devices skip the quantized
text encoder fallback (these run on non-MPS CI too), that low vram
states still place text encoders on the CPU, and adds bf16 placement,
dict-form (full checkpoint) state dicts, and non-tensor state dict
entries to the MPS tests.
On Apple Silicon vram_state is VRAMState.SHARED (unified memory), but
text_encoder_device() only returned the GPU for HIGH_VRAM/NORMAL_VRAM,
so text encoders ran on the CPU. For LM-style encoders like ACE-Step
1.5 the text encode stage dominates generation time on Mac.
This re-lands #12809, which was reverted in #13070 because it broke
quantized text encoders on Mac: the MPS backend cannot cast float8
dtypes (pytorch/pytorch#132624), and the existing supports_cast()
fallback in CLIP.__init__ only inspects declared model dtypes, which
never reflect fp8 weights behind comfy_quant quantization metadata
(QuantizedTensor reports the compute dtype, not the fp8 storage dtype).
To keep quantized text encoders working, CLIP.__init__ now decides the
device before any weights are allocated: it checks supports_cast() on
the resolved dtype, and when the load device cannot cast fp8 it scans
the incoming state dict for fp8 tensors or comfy_quant markers and
keeps those text encoders on the offload device. Devices that can cast
fp8 (cuda etc.) skip the scan entirely. The pre-existing shift-back
path now also updates the current device so the load log stays
accurate.
Adds MPS unit tests: fp16 placement on the GPU, fp8 fallback via
declared dtype, secondary dtype (dtype_llama style), state-dict fp8
weights, and comfy_quant markers, plus a canary that fails when a
torch release adds fp8 casts on MPS so the fallback can be relaxed.
* security: fix five vulnerabilities (GHSA-779p-m5rp-r4h4)
- CVE-2026-56670: force download of SVG/XML responses on /view to prevent stored XSS
- CVE-2026-56671: contain /experiment/models/preview reads within the model folder
- CVE-2026-56672: stop inline rendering of uploaded /userdata/{file} content
- CVE-2026-56673: prevent path traversal in get_annotated_filepath (LoadImage /prompt input)
- CVE-2026-56674: reject opaque/null Origin to close the CSRF middleware bypass
Adds regression tests under tests-unit/security_test/ covering all five.
* security: address review feedback on GHSA-779p fixes
- Fix Windows CI failure in test_get_annotated_filepath: compare against
os.path.abspath(...) to match the intentional abspath normalization added
by the traversal hardening (abspath prepends the drive letter on Windows).
- origin_check: narrow the bare `except:` in is_loopback() to ValueError so
genuine interrupts aren't swallowed (review nit).
- origin_check: guard .port access in is_cross_origin_forbidden() so a
malformed/out-of-range port (e.g. Origin: http://127.0.0.1:99999) fails
closed with a 403 instead of surfacing an uncaught 500 in the middleware.
- server /view: escape backslash/quote in the Content-Disposition filename
(RFC 6266 quoted-string) so a filename containing a double quote can't
malform the response header.
* security: address CodeRabbit review feedback on GHSA-779p tests
- test #3: guard the symlink-escape test with a try/except skip so it no
longer errors on Windows CI where os.symlink needs elevated privileges /
Developer Mode (mirrors the guard in the sibling test #2).
- test #5: refresh the stale module docstring to describe the actual /view
gating (view_image closure calling folder_paths.is_dangerous_content_type,
the normalising check) instead of the bypassable raw set-membership test.
* revert(security): drop CVE-2026-56674 Origin: null CSRF change
Per maintainer review, the reported CSRF is already mitigated by the pre-existing
Sec-Fetch-Site: cross-site check for current browsers, and the null-origin
rejection risked breaking legitimate sandboxed-iframe embeds. Restores
origin_only_middleware and is_loopback in server.py to their prior state
(the Sec-Fetch-Site check is retained) and removes utils/origin_check.py and its
regression test. The other four GHSA-779p fixes are unaffected.
Add a --enable-asset-hashing CLI flag (action=store_true, default False)
and plumb it into the two asset-seeder call sites in main.py that
previously hardcoded compute_hashes=True (the startup scan and the
post-job output enqueue). Local runs now skip blake3 hashing unless the
user opts in, avoiding the startup/per-output cost on large models
directories while keeping hashing available for asset-portability
features.
Co-authored-by: Alexis Rolland <alexisrolland@hotmail.com>