Per-image mutation routes authorize and write in separate statements, so a revoked permission still applies
Los mantenedores suelen responder en 5 días
@lstein ya está trabajando en esto.
Desde el 24/8/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
Every per-image mutation route authorizes and then writes as two separate statements, so a permission that is revoked in between is applied anyway:
_assert_image_owner(image_name, current_user) # decision
ApiDependencies.invoker.services.images.update(...) # write, under a decision that may be stale
The window is small but it is a real check-then-act: between the two statements a board can flip from Public to Private, an image can be reassigned to a board the caller cannot write to, or the caller's access to the board can be withdrawn. The write still lands.
What is already conditional
#9394 closed the one instance where the race caused incorrect data movement rather than merely a late-by-microseconds mutation. board_image_records.remove_image_from_board scopes its DELETE to the board the caller was authorized against:
DELETE FROM board_images WHERE image_name = ? AND board_id = ?;
An unscoped delete followed the image if it moved between the authorization and the write, applying a decision taken about one board to a different one. The scoped form matches zero rows instead, and the route classifies the zero-row outcome (moved / already uncategorized / deleted) rather than reporting a success that did not happen.
The remaining routes — star, unstar, add-to-board — have no equivalent. Their worst case is a mutation applied under a decision that was true a moment earlier, which is why this is a follow-up and not a blocker on that PR.
Why the obvious fix does not fit
Encoding the authorization as a WHERE clause means encoding all of it, and it is a four-way disjunction spanning three tables (invokeai/app/api/routers/_access.py):
- the caller is an admin (
users.is_admin), or - the caller owns the image (
images.user_id), or - the caller owns the board the image sits on (
board_images→boards.user_id), or - that board is Public (
boards.board_visibility).
Writing that as a correlated subquery on every mutating statement duplicates the policy in SQL, in several places, with no mechanism keeping the copies in step with the Python one. The first divergence is a silent authorization bug.
The shape that would work
Move the decision into the service call, so authorization and mutation share one transaction and one policy implementation:
- a single
authorize_image_mutation(image_name, user)used by the service layer inside the transaction that performs the write, rather than by each route beforehand; - routes keep reporting per-name outcomes exactly as they do now (
failed_imagesfor a genuine failure, silent skip for a name the caller may not touch), so the API contract does not change; - storage errors keep propagating rather than being read as "denied" (the invariant #9394 established).
This is the same refactor already wanted for the last-admin guards, which have the identical check-then-act shape in UserService. Worth doing once, for both.
Not urgent because
- The mutations at risk (star, unstar, board add) are low-impact and visible to the user who performed them.
- The high-impact case — a delete or a board move landing on the wrong board — is already conditional.
- Any fix touches the service interfaces for images, boards and users together, which is a poor fit for a bug-fix PR.
- Lenguaje dominante
- Python
- Estrellas
- 28.3k
- Forks
- 3k
- Merge medio
- 6 d 22 h
- PR fusionados (30 d)
- 10
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Sin guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de invoke-ai/InvokeAI
-
[enhancement]: UpscalingAbiertoenhancement
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
invoke-ai/InvokeAI#9608 · 2 comentarios ·
Los mantenedores suelen responder en 5 días
-
[bug]: Align Graph.add_edge and validate_self collector type validationPosiblemente ocupada @JPPhoto la tomó hace 4 días. Abiertobug
invoke-ai/InvokeAI#9610 · 1 asignado ·
Los mantenedores suelen responder en 5 días
-
[bug]: Nested Iterate execution mixes values across outer iterationsPosiblemente ocupada @JPPhoto la tomó hace 4 días. Abiertobug
invoke-ai/InvokeAI#9609 · 1 asignado ·
Los mantenedores suelen responder en 5 días
-
enhancement
Dificultad 5/5 Más de una semana Aptitud para principiantes 35/100
Los mantenedores suelen responder en 5 días
-
[bug]: 6.14.1 regression with `pytorch_cuda_alloc_conf: backend:cudaMallocAsync` — Z-Image bf16 + LoRA takes ~30 min whenever the transformer is (re)loaded (VRAM overflows into Windows shared memory)Posiblemente ocupada @lstein la tomó hace 6 días. Abierto
invoke-ai/InvokeAI#9597 · 2 comentarios · 1 asignado ·
Los mantenedores suelen responder en 5 días
Todos los issues de invoke-ai/InvokeAI
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 85/100
kornia/kornia#5263 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Metadata correction for W16-5400Abiertoapproved correction metadata
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
acl-org/acl-anthology#10133 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
BasedHardware/omi#20084 ·
Los mantenedores suelen responder en 1 día
-
bug needs-acceptance wg/evaluation-quality
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
vllm-project/semantic-router#4424 ·
Los mantenedores suelen responder en 1 día