| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix(imagemap): load CLIP towers with the top-level projection_dim apple/DFN2B-CLIP-ViT-L-14-39B (and potentially other published full-CLIP repos) carries nested vision_config/text_config projection_dim values (512) that disagree with its own weights (768). CLIPModel loads fine because it sizes both projections from the authoritative top-level projection_dim and never reads the nested values — but the CLIPVision/CLIPText ModelWithProjection classes size their head from the nested value and fail with a state-dict size mismatch. clip_tower_config_override() copies the top-level projection_dim into the sub-config before a tower is loaded on its own, at all three load sites (CLIPVision cache loader, index CPU-mode vision load, index text tower). It returns None for anything that is not a full-CLIP checkpoint (vision-only IP-Adapter encoders, SigLIP), and is a no-op for repos with consistent values (the curated InvokeAI/clip-vit-large-patch14 has 768 in both places). Validated against the real checkpoint: tower embeddings match open_clip loading the same weights to cosine 0.9999 (image; residual is resize interpolation) and 0.99999994 (text). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RggchcVXV147KL5FJihwZL | 2 个月前 | |
Formatting | 1 年前 | |
refactor(model-manager): share the llama.cpp key converter and the embedding materialization Folds Mistral's own copy of the llama.cpp key converter into the shared one, which now serves three decoder families, and renames it to match. Replaces five inline embedding dequantizations with one helper that reserves host RAM first — four of them reserved none. The reservation factor is now measured rather than derived: the k-quant, 5-bit and 4-bit dequantizers unpack through temporaries that coexist with the result, peaking at up to 5.4x instead of the 2x the code assumed. | 1 个月前 | |
fix(api): reject path traversal in object-serializer names and model keys (#9595) * fix(api): reject path-separator characters in serialized object names `ObjectSerializerDisk._get_path()` joined the caller-supplied `name` straight onto the output directory. Names are generated server-side, but they round-trip through the client inside node fields (`TensorField. tensor_name`, `LatentsField.latents_name`, `ConditioningField. conditioning_name`, `DenoiseMaskField.mask_name`), so on the way back in they are attacker-controlled: any authenticated user who can submit a graph could put `../` (or an absolute path) in one and make `load()` `torch.load()` a file anywhere the server process can read. A node that renders the loaded tensor - `tensor_mask_to_image` is the easy one - then hands the contents back as an image the caller owns. Validate at the single choke point: a name must be a plain filename. `PureWindowsPath` is used for the check so that backslash- and drive-letter-shaped payloads are rejected on posix too, rather than only blowing up on Windows deployments. Also pin `weights_only=True` on the `torch.load()` call. It has been the torch default since 2.6 and we require >=2.7, so this is a no-op today, but the file being loaded is named by untrusted input and the safe globals allowlist is only meaningful while the unpickling path stays shut. Reported by Dan Aridor. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(api): stop client-supplied model keys escaping the model images folder Extends the object-serializer guard from the previous commit to the other name-to-path service, and factors the shared predicate out into `invokeai/app/util/path_safety.py` so the next such service has one obvious thing to call. Two copies of a security predicate is how this bug class survived in the first place: the pre-6.7 hardening fixed `ImageFileStorageDisk.get_path()` and left its siblings alone. `ModelImageFileStorageDisk` joined an unvalidated `model_key`. Reachable over HTTP only in its Windows-backslash shape - uvicorn percent-decodes before Starlette routes, so `%2F` never matches the `{key}` path segment - but `save()` was also building its own path instead of going through `get_path()`, so a guard on `get_path()` alone would have left the write side open. Worse, `_process_preview_image()` builds `model_images_path / f"{model_key}.webp"` by hand, bypassing the service entirely, and `model_key` there is whatever the install request asked for: `ModelRecordChanges.key` is an optional body field and `ModelConfigFactory` honours it verbatim. A key of `../../pwned` writes the thumbnail two levels outside the store. That key also reaches `dest_dir = models_path / info.key` in `install_path()`, which creates the directory and moves the model into it. Validating `ModelRecordChanges.key` closes both at the point the untrusted value enters; the hand-built join is guarded too, since it is a backend util that takes a bare `Path`. `update_model_image` and `delete_model_image` now resolve the model record before touching the filesystem. Neither checked that the key named a model at all, so an upload for an unknown key created a stray file; resolving first makes that a 404, and keeps a rejected key from reaching a service whose `except Exception` would have laundered it into a 500. Note that `update_model_image` can now return 404, which its `responses` dict does not declare - declaring it would churn `openapi.json` and `schema.ts`, which are otherwise byte-identical across this branch. Two gaps in the predicate itself, from an adversarial review of the previous commit: `pathlib` keeps trailing dots and spaces but the Win32 normaliser strips them at open time, so `".. "` reached the parent directory on Windows; and control characters were accepted, which is not "safe to join" for a name that also ends up in a header or a log line. Reported by Dan Aridor (the tensor case); the model-key cases were found while hardening the surrounding code. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(api): let the image-delete route clean up orphans; check the record under the claim Review follow-ups on the model-image routes: - DELETE /i/{key}/image no longer requires the model's record. Deleting a model leaves its cover image behind, and this route is how that orphan is removed, so the record check made orphans permanent. A missing image or a key that is not a plain filename (rejected by the storage service) now maps to 404 instead of 500. - PATCH /i/{key}/image resolves the record while holding the key claim. Deletion and conversion claim the key too, so the model can no longer disappear between the check and the save. - Document the PATCH route's 404 in OpenAPI; regenerate openapi.json and schema.ts. - Correct the ModelRecordChanges.key comment: install_path()'s join is not guarded at the sink (#9596). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(api): case-fold model key claims; make the image-delete traversal test bite - A cover image is `<key>.webp`, and on a case-insensitive filesystem a case variant of a key names the same file. Claims were compared exactly, so an image delete for `K` could run while a conversion held `k` and unlink the image the conversion was carrying over. Claims are now case-folded. - The image-delete traversal test only used backslash and `..` keys, which are literal (missing) filenames on posix and 404 even with no key validation. Add `../escaped`, which does escape the folder when unguarded. - Correct the PATCH comment: removing an external provider's models does not take the claim, so that path can still leave an orphan image. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(model-manager): cover legacy unsafe image key * fix(api): 404 an upload for a legacy unsafe key; guard install_path at the sink - PATCH /i/{key}/image: a record written before install keys were validated can carry a key that is not a plain filename. The storage guard rejected it inside save(), which surfaced as an unhandled ModelImageFileSaveException (500). Ask the storage service for the path before reading the upload and answer 404. - Claims use Unicode canonical caseless matching (NFD, casefold, NFD), not bare casefold: APFS also ignores normalization, so `é` and `é` name one cover image there. - install_path() refuses a key that is not a plain filename before creating or moving anything, so the models_path join no longer relies solely on the ModelRecordChanges validator. Closes #9596. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(install): skip a legacy install marker whose key no longer validates `_restore_incomplete_installs` built `ModelRecordChanges` from each marker's `config_in` outside the per-marker try. Now that the key is validated, a marker written by an older version with an unsafe key raised out of the loop at every startup, leaving every later interrupted install unrestored. Build it inside the try so that marker is skipped like any other unreadable one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test(path-safety): cover NTFS parent stream traversal * fix(api): reject NTFS stream syntax and Windows device names in stored names Addresses JPPhoto's round-3 blocker and the fallout from fixing it. `is_plain_filename()` passed `..:stream`. `PureWindowsPath` only reads a single *letter* followed by a colon as a drive, so it parses `C:foo` but hands `..:stream` back whole, and `.name == name` held. On NTFS that names the alternate data stream `stream` on the file `..`, i.e. the parent directory, so `<store>/..:stream` writes outside the store while looking like one bare component. Colons are now rejected outright, along with the rest of the set Windows forbids in a filename. Extends the one case in 3895394 to the other shapes that leaked the same way. Windows device names are rejected too. They do not escape upwards but they do not stay put either: Win32 resolves `NUL`, `CON`, `COM1` and friends to the device namespace from any directory, so a write to `<store>/NUL.webp` is silently discarded while `.exists()` still reports True. Includes the superscript `COM¹`/`LPT¹` forms and `COM0`/`LPT0`, and strips trailing spaces from the base name after the extension split, because Win32 trims them before the device lookup. The control-character set now covers C1, U+2028 and U+2029, which `str.splitlines()` treats as line breaks. The `ModelRecordChanges.key` validator is gone, and the check moved to where a key is actually adopted. That model is the *update* body as well as the install body, and the model edit form posts the whole record back with the key riding along as an echo, so validating the field made any model whose key predates key validation impossible to edit at all: a 422 before the handler runs, with no key field in the UI to fix it. On Linux and macOS every one of `: ? * | < > "` is a legal filename character and no released version checked the key, so such records exist. The same model is re-parsed from install markers written by older versions, where rejecting one stranded its partial download forever - no job is created to clean up and `_remove_dangling_install_dirs` keeps any readable non-terminal marker. In its place: - `update_model_record` refuses to *change* a key. `update_model` copies every set field into the stored config, so a `key` in the body landed in the blob while the row id kept the old one, leaving a record that answers to a key nothing can look it up by - `replace_model`, and so reidentify, raises `UnknownModelException` from then on, and the UI caches the model under the new key so every later request 404s. The old validator did not close this: a *valid* but different key poisoned the row just the same. Gating on the key changing accepts the form's echo and closes the write. - `_probe()` checks the key. It is the one place a caller-supplied key becomes a record's key, and unlike the previous check in `install_path()` it also covers `register_path()`, i.e. every in-place install. External registration builds its config directly, so it carries its own check. - `install_model` keeps a route-level check so a bad key is a 422 rather than a job that fails later. - `migration_24` leaves a model in place rather than `mkdir`+`rename`ing it into `models_dir / key`, which was the one join in the repo with no containment check and takes its key from the stored blob. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(model-images): cover legacy POSIX keys * fix(model-images): keep existing covers for keys that are legal on posix The store's join guard used `is_plain_filename`, which applies Windows' rules on every platform. That is the right gate for a key we are about to accept, but no released version checked model keys, and on posix `: ? * | < > " \` and names like `NUL` are ordinary filename characters. A model keyed `legacy:key` with a cover on disk lost it on upgrade: `get_url()` returned None and the image PATCH/DELETE routes answered 404, so it could not be replaced or removed. Add `is_contained_filename`, which checks containment on the running platform only: exactly `is_plain_filename` on Windows, and no empty name, `.`, `..`, `/` or control character on posix. Use it where a join addresses data that may already exist - the model-images store and migration_24's relocation. New keys still go through `is_plain_filename` at every entry point (install route, `_probe`, external registration, `install_path`), so nothing new can be stored under a name some deployment could not hold. Store tests now run the Windows payloads with Windows rules forced, so a posix CI run still covers them, alongside the posix-escaping subset and the legacy names that must keep working. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix(model-images): encode model keys in URLs; cover migration_24's guard Follow-ups from self-review of the posix-containment change: - A legacy posix key can hold URL syntax. With `X?y` now served, its cover URL addressed `GET /i/X` and the delete-image button sent `DELETE /i/X?y/image`, which routes to `DELETE /i/{key}` for model `X`. Percent-encode the key in `get_model_image_url` and in every model route the frontend builds. - `get_url()` returns None when `exists()` raises (ENAMETOOLONG for an over-long stored key), instead of failing the whole model list. - Test migration_24's relocation guard: an escaping key stays in place, a Windows-only key stays in place under Windows rules, and a legacy posix key is normalized like any other. - Skip the forced-posix legacy-key store test on Windows, where `get_path` builds a real WindowsPath that splits `a\b`; drop `trailing.`, which the strict predicate accepts anyway. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: JPPhoto <jpollack@jpollackphoto.com> | 28 天前 | |
feat(fp8): run scaled fp8 FLUX.1 checkpoints on the tensor cores A ComfyUI scaled-fp8 FLUX.1 checkpoint did not load at all: the loader knew nothing about the scale side-channel, so its 629 scale and marker keys reached `load_state_dict` as unexpected keys and it raised. Verified against `comfyanonymous/flux_dev_scaled_fp8_test`, the reference export. FluxCheckpointModel now takes the same path as Krea-2 and Z-Image: extract the scaled layers, fold them when the matmul is unavailable, keep them and attach the scales when it is. FLUX.1 needs no key conversion -- the checkpoint is already in the BFL layout the model implements, and `qkv` stays one fused Linear, so a per-tensor scale attaches to exactly the module it was computed for. No split, no scale copying. Reading the scales runs after the bundle conversion, since they carry the same `model.diffusion_model.` prefix as the weights. Also stop that conversion from folding fp8 scales to bf16. `.weight_scale` ends in "scale" like the model's own RMSNorm parameters, so the unguarded test cost 8 mantissa bits on a value every quantized weight is multiplied by. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> | 2 个月前 | |
perf(krea2): drop the Qwen3-VL visual tower on the safetensors paths Krea-2 and Ideogram 4 condition on the language tower alone -- `Qwen3VLModel.forward` reaches `visual` only for `pixel_values`, which neither invocation passes, and Ideogram 4 drives `language_model` directly. The tower was loaded and cached anyway. Dropping it leaves the conditioning bit-identical and takes 0.774 GiB off the bf16 4B encoder (8.266 -> 7.492) and 0.390 GiB off the fp8_scaled one. Covers all four paths that build this encoder, including the two bundled pipelines the loader nodes fall back to when no standalone encoder is selected -- the default for the starter models. On the single-file and Ideogram 4 paths the tensors go before the quantization passes, so they are never dequantized, copied or reserved for. Suppresses the per-layer LoRA warning for `lora_qwen3vl-visual.*`, which the converter emits: ~108 lines per generation blaming an adapter for layers that never affected an image. | 1 个月前 | |
Round 2 copyright removal | 25 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 2 个月前 | ||
| 1 年前 | ||
| 1 个月前 | ||
| 28 天前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 25 天前 |