Auditoría Suprema 2 · Tanda 1 (core) — cerrar gates de permiso que faltaban
Visión general
Primera tanda de arreglos de la Auditoría Suprema 2 (segunda ronda de revisión de seguridad, 31-08-2026 — la primera ronda fue [[feature—security—s104-auditoria-suprema-fixes-core]]). Cierra 4 hallazgos ALTA de control de acceso, todos en el dominio core y todos de la misma clase: un endpoint o vista que mutaba/leía datos comprobando solo la pertenencia a la organización, nunca el NIVEL de permiso del usuario.
Versión: v1.90.0 · PR: #476 · Fecha: 2026-08-31 · Autor: Edu (Claude Fable 5) · Ámbito: seguridad · control de acceso (core)
Los 4 hallazgos ALTA
1. Escalada de privilegios al crear/promover usuarios
- Función afectada:
core.api.users::create_useryupdate_user(POST /api/users/,PUT /api/users/{id}) - Riesgo: Quien tenía
users:adminpodía crear un usuario conrole="admin"(que otorga admin en TODOS los scopes: terminal, network, racks…) o promover a otro a ese role, ganando permisos que él mismo no tenía. - Fix: Nueva
_role_exceeds_grantor()compara lo que elroleotorga, scope a scope, contra los permisos efectivos del solicitante (get_effective_permissions), y devuelve 403 si excede — el mismo control quecore/api/admin.pyya aplicaba aModulePermission/TemporaryAccess. Se valida además que elrolesea uno de los válidos (VALID_ROLES). - Líneas:
core/api/users.py(+27 LOC función_role_exceeds_grantory sus dos call sites). - Test:
tests/api/test_users_authz.py::TestUserPrivilegeEscalation(4 tests: crear admin rechazado, crear readonly aceptado, promover a admin rechazado, admin pleno sí puede).
2. Un admin de organización tomaba el control de un superuser de su org
- Funciones afectadas:
core.api.users::update_user,delete_user,delete_user_mfa_device - Riesgo: Los tres handlers solo comprobaban la pertenencia a la organización, nunca si el objetivo era
is_superuser/is_staff— un admin normal podía cambiarle la contraseña, borrarlo o quitarle el MFA a un superuser de su propia org. - Fix: Los tres rechazan con 403 si el objetivo es superuser/staff y el solicitante no lo es. Los tres pasan además de
get_current_orgarequire_org(fail-closed). - Líneas:
core/api/users.py(+3 guards de ~4 LOC cada uno). - Test:
tests/api/test_users_authz.py::TestSuperuserProtection(3 tests: password rechazado, delete rechazado, superuser gestionando a otro superuser sí funciona).
3. 13 mutadores HTMX de racks/papelera/grupos sin ningún gate de permiso
- Módulo afectado:
core/htmx_views.py(racks_create,rack_detail,rack_delete,rack_clone,trash_restore,trash_delete_permanent,trash_empty,groups_create,group_delete,group_rename,groups_assign,device_groups_create,device_group_delete) - Riesgo: Estas vistas solo llevaban
@login_required. Un usuarioreadonlypodía crear/borrar/clonar racks y vaciar la papelera entera desde la interfaz — mientras sus gemelas de la API Ninja ya exigíanracks:edit/racks:admin. - Fix: Gate
has_permission(request.user, "racks", level)+ nuevo helper_deny()que devuelve un fragmento HTML 403 (en vistas Django clásicas no se usarequire_perm, que lanza elHttpErrorde Ninja → 500 sin gestionar).racks:editen creación/edición/clonado/grupos,racks:adminen el borrado permanente y el vaciado de papelera. - Modularización: Device Groups (Terminal) se extrajo a
core/htmx_device_groups.py—core/htmx_views.pyrondaba el techo de 500 LOC lógicas de la Regla 5. Rutas y comportamiento sin cambios, actualizado el import encore/htmx_urls.py. - Test:
tests/test_htmx_authz.py(9 tests: racks, papelera y grupos, con readonly/operator/admin).
4. Contrato T5 desalineado tras migrar a require_org
- No es un hallazgo de seguridad nuevo, sino un ajuste de contrato derivado del fix nº1: al pasar
create_user/update_userarequire_org(fail-closed), un admin sin organización ahora recibe 403 en vez del 400/404 anterior. El invariante T5 (no tocar el primer tenant) se mantiene — solo cambia el código de estado. Actualizadas las aserciones detests/api/test_t5_org_fallback.py.
Tests añadidos
tests/api/test_users_authz.py— 7 tests nuevos (escalada de role + protección de superuser).tests/test_htmx_authz.py— 9 tests nuevos (racks/papelera/grupos por HTMX).tests/api/test_t5_org_fallback.py— 2 aserciones ajustadas al nuevo contrato 403.
Total: 16 tests nuevos, todos verdes.
Patrón aplicado
Mismo patrón que [[decision—20260609—require-perm-unified-authorization]] fijó para network: require_perm/has_permission + require_org como gate uniforme, aquí extendido a las vistas HTMX clásicas de core (que no pueden usar require_perm directamente porque lanza HttpError de Ninja). Ver [[entity—core—service—has-permission]] para el evaluador de permisos subyacente.
Documentación
- CHANGELOG.md — entrada
[1.90.0]con detalle de los 4 fixes. - RELEASE_NOTES.md — entrada de usuario explicando el impacto sin jerga técnica.
Véase también
- [[entity—core—service—has-permission]]
- [[decision—20260609—require-perm-unified-authorization]]
- [[feature—security—s104-auditoria-suprema-fixes-core]]
- [[concept—saas—multi-tenancy]]
- [[entity—racks—model—rack]]
- [[decision—20260602—auditoria-suprema-4-hallazgos-alta]]
- [[entity—core—model—user]]