# ¿Puede una prueba de automatización de la interfaz de macOS superar la aprobación de un agente?

Una tarjeta de aprobación solo es un control de seguridad si la aprobación la provoca la persona y no otro proceso. Parece evidente hasta que colocas la tarjeta en un escritorio normal de macOS, donde los clientes de Accessibility pueden inspeccionar controles, las herramientas de automatización pueden activar aplicaciones y cualquier ventana en primer plano puede competir por el siguiente clic.

No pruebes esto preguntando si la tarjeta parece modal o si el botón tiene una etiqueta tranquilizadora. Comprueba si un proceso con privilegios locales realistas puede provocar una acción protegida sin que la persona prevista tome una decisión deliberada e informada. El resultado útil no es un vídeo espectacular de explotación. Es una respuesta reproducible a una pregunta más concreta: qué rutas de entrada pueden cruzar el límite de aprobación, con qué permisos y qué pruebas demuestran el resultado.

Este es un ejercicio de red team para software propio, en un Mac y una cuenta bajo tu control. Mantén inocua la acción solicitada. Usa un endpoint HTTP de prueba, un host SSH que no sea de producción o una acción que devuelva un valor fijo. Una prueba que pueda gastar dinero, borrar datos o cambiar el acceso de producción por accidente ya está mal delimitada antes de escribir el primer script.

## El permiso de Accessibility cambia el modelo del atacante

Un proceso con permiso de Accessibility no es un proceso en segundo plano normal. Apple describe el acceso de Accessibility como un permiso que permite a las aplicaciones controlar un Mac, y macOS exige que la persona lo conceda en los ajustes de Privacidad y seguridad. Apple describe por separado el permiso de Automation, que permite acceder a otras aplicaciones y controlarlas. Son permisos distintos, pero ambos deben formar parte del modelo de amenazas cuando una tarjeta de aprobación vive en el escritorio.

La distinción que los equipos suelen difuminar es esta: una interfaz de aprobación puede ser accesible sin que toda acción originada desde Accessibility sea una prueba de intención humana. Accessibility debe exponer etiquetas, roles, cambios de estado y foco significativos para que las personas que usan tecnología de asistencia puedan manejar la aplicación. Es una exigencia de inclusión. No obliga a la aplicación a permitir que un proceso externo invoque una confirmación irreversible sin ninguna señal local de la persona.

Empieza la prueba describiendo el atacante que realmente estás simulando. Evita la etiqueta vaga «malware». Oculta los permisos que deciden el resultado. Un atacante de referencia útil es un helper firmado que se ejecuta localmente con la misma cuenta iniciada en sesión y que ya recibió permiso de Accessibility. Una variante más fuerte tiene también permiso de Automation y puede solicitar la activación de aplicaciones. Ninguna de las dos variantes obtiene credenciales de administrador, evita el bloqueo de pantalla ni usa una copia modificada de la aplicación de aprobación.

Ese límite hace justa la prueba. Si la conclusión es «un atacante con control total de la cuenta puede controlar la cuenta», no has aprendido nada. Si la conclusión es «un helper con los mismos permisos que una persona podría conceder a un gestor de ventanas, un ejecutor de pruebas, una herramienta de macros o una utilidad de asistencia puede aprobar el uso de una credencial», has encontrado un problema de diseño con una vía clara para corregirlo.

Crea una tabla de capacidades antes de la primera ejecución:

| Capacidad | Estado de prueba | Por qué importa |
| --- | --- | --- |
| Permiso de Accessibility | Activado o desactivado | Determina si el helper puede descubrir e invocar elementos de interfaz expuestos. |
| Permiso de Automation | Activado o desactivado | Determina si puede pedir a macOS que controle otra aplicación. |
| Input Monitoring | Activado o desactivado | Permite separar la observación de la entrada humana de la generación de acciones de interfaz. |
| Grabación de pantalla | Activada o desactivada | Es útil para las pruebas, pero no confundas una captura con una autorización. |
| Misma sesión de usuario | Obligatoria | Mantiene la prueba centrada en una interferencia realista del escritorio. |

No concedas un permiso solo porque lo pida una herramienta de pruebas. Cada permiso adicional cambia la afirmación que podrás hacer después. Registra la combinación exacta en el título del resultado, por ejemplo «un helper con solo Accessibility no puede confirmar una aprobación por llamada», en lugar de «la aprobación es segura». La segunda afirmación no resistirá una configuración de escritorio distinta.

Registra también el identificador del bundle del helper, su autoridad de firma, el ID del proceso, el proceso padre y la ruta de lanzamiento. Un buen sistema de aprobación debería mostrar suficiente identidad del proceso para que la persona pueda valorar la solicitud, pero el registro de la prueba necesita más detalles de los que una tarjeta puede mostrar razonablemente. La persona que ejecuta la prueba debe poder responder si un proceso hijo heredó un proceso padre de apariencia confiable, si un wrapper de terminal lanzó el helper y si un relanzamiento creó una identidad de proceso nueva.

## Un botón pulsable no demuestra que la aprobación sea débil

Un botón expuesto mediante la jerarquía de accesibilidad de macOS no es automáticamente una vulnerabilidad. AppKit espera que los elementos de accesibilidad participen en esa jerarquía para que los clientes de asistencia encuentren controles funcionales, y Apple documenta que los controles exponen propiedades como etiquetas, títulos, marcos y su estado habilitado.

El diseño débil es aquel en el que invocar el botón expuesto basta como prueba para una acción sensible. Así, una acción sintética de Accessibility resulta indistinguible de un clic consciente. Además, el fallo suele pasar inadvertido: el equipo prueba con un ratón, ve el callback esperado y nunca pregunta qué más puede producir ese mismo callback.

Haz que la decisión dependa de un evento de autorización local y no solo del manejador de acción del botón. El control puede iniciar la solicitud, pero la acción protegida debe esperar un resultado de autorización que registre cómo se produjo la confirmación. En una aprobación normal de sesión, puede ser un clic deliberado después de que la aplicación verifique que su propia superficie está activa y actualizada. Para una acción de alto riesgo, exige un evento de autenticación local mediado por el sistema, como Touch ID cuando esté disponible.

No cometas el error habitual de ocultar o cambiar el nombre del botón Approve en el árbol de accesibilidad. Eso perjudica a las personas que necesitan tecnología de asistencia y hace poco frente a un atacante local capaz de manipular el foco o invocar rutas alternativas. Prueba, en cambio, si la cadena de confirmación rechaza una ruta sintética, una tarjeta antigua, una tarjeta inactiva y una solicitud cuyos detalles visibles ya no coinciden con la acción que va a ejecutarse.

Un registro interno de decisión puede tener este aspecto:

```json
{
  "request_id": "test-4d8f",
  "decision": "approved",
  "decision_method": "touch_id",
  "card_generation": 7,
  "request_generation": 7,
  "app_active": true,
  "card_frontmost": true,
  "requester_pid": 48102,
  "requester_signing_authority": "Example Development Team",
  "action_started_after_decision": true
}
```

Los nombres de los campos importan menos que la separación. `decision` indica qué ocurrió. `decision_method` indica cómo ocurrió. Los valores de generación impiden que una tarjeta antigua apruebe una solicitud nueva después de un redibujado, un reintento o un cambio de acción. Los campos de activación registran si la persona estaba viendo la tarjeta cuando se tomó la decisión. El último campo permite comprobar el orden de las operaciones.

Esta separación también detecta un fallo sutil: aceptar la aprobación antes y mostrar la tarjeta después como gesto de cortesía. En ese caso, todas las pruebas visuales pasan. La acción protegida ya ha comenzado, por lo que la tarjeta ofrece apariencia de control, no control real. Haz que el ejecutor de acciones necesite un token de decisión que solo cree el subsistema de aprobación después de completar las comprobaciones pertinentes.

## El equipo de pruebas debe producir ejecuciones inocuas y comparables

Construye un fixture que cree una solicitud de aprobación predecible y un observador que recopile las pruebas. No empieces con un framework general de automatización del escritorio apuntando a una credencial real. El equipo de pruebas debe reducir la ambigüedad, no aumentarla.

El fixture necesita una solicitud con un identificador estable, detalles visibles que cambien en cada ejecución, una caducidad breve y un efecto final inofensivo. Por ejemplo, haz que la acción protegida llame a un endpoint local que devuelva `approved:test-4d8f` solo después de que el sistema libere la aprobación. Si la acción funciona sin un registro de aprobación válido, tienes un fallo claro. Si termina por tiempo de espera o registra una denegación, tienes un resultado claro de no aprobación.

Usa un nonce de corta duración tanto en la tarjeta como en la solicitud protegida. Una tarjeta que solo diga «¿Permitir acceso del agente?» no puede revelar un fallo de ventana antigua. Una que diga «¿Permitir que la solicitud de prueba 4d8f llame al servicio de eco de staging?» sí puede hacerlo. Cambia el nonce en cada ejecución y comprueba que el resultado contiene el mismo nonce.

Mantén tres relojes independientes en las pruebas:

1. La hora de creación de la solicitud.
2. La hora de la decisión.
3. La hora de inicio de la acción protegida.

Usa tiempo monótono dentro de la aplicación cuando sea posible. La hora del sistema sirve para una cronología humana, pero puede cambiar por sincronización de red, suspensión o ajustes manuales. La regla es sencilla: la acción debe comenzar después de una decisión válida para la misma generación de solicitud. Una captura de pantalla no puede demostrar ese orden.

El observador debe capturar la aplicación activa, la ventana enfocada, el ID de solicitud visible en la tarjeta y el resultado final de la acción. Una grabación de pantalla ayuda a diagnosticar comportamientos inesperados, pero no sustituye los eventos estructurados. Las grabaciones pierden identificadores de proceso, mezclan transiciones rápidas y a menudo ocultan el evento importante detrás de un movimiento del puntero.

Usa un registro como este en cada intento:

```text
case=AX-invoke-approve
request=test-4d8f
helper=TestHelper pid=48102
permissions=accessibility
approval_surface=active
attempt=accessibility_action
decision=denied
protected_action=not_started
artifact=activity-event-0192
```

El valor de `attempt` debe describir el mecanismo y no el resultado deseado. Escribe `activate-then-click`, `stale-window`, `keyboard-focus-shift` o `voiceover-navigation` en lugar de `attack-1`. Seis semanas después, esa descripción sencilla evitará que alguien repita el caso equivocado.

Ejecuta primero el mismo fixture sin permisos para el helper. Ese es tu control. Después activa una capacidad cada vez. Un resultado de red team sin una ejecución de control es difícil de interpretar. Quizá la tarjeta nunca era accesible mediante el teclado, falló el endpoint o el helper no tenía el entitlement que creías. El control convierte esas dudas en diferencias visibles.

## Prueba la aprobación sintética sin convertir la prueba en un kit de explotación

El objetivo es determinar si un cliente de Accessibility autorizado puede invocar el control de aprobación y hacer que se ejecute la acción protegida. No necesitas un pulsador listo para publicar ni una colección de scripts frágiles basados en coordenadas.

Usa tu propio helper de prueba con una tarea limitada: encontrar la ventana de aprobación de tu aplicación de prueba por su identidad y el nonce de prueba, solicitar la misma acción semántica que pediría un cliente de accesibilidad y comunicar si la acción protegida terminó. No busques todas las ventanas del escritorio. No apuntes a diálogos de terceros. No guardes credenciales ni interactúes con cuentas de servicios reales.

Prueba estas rutas por separado, porque demuestran cosas distintas:

- Una invocación semántica de Accessibility del control Approve.
- Navegación con teclado hasta Approve seguida de una activación sintética.
- Una acción del puntero después de que el helper traiga la aplicación de aprobación al frente.
- Una acción del puntero sin que el helper la traiga al frente.
- Una acción retrasada después de que caduque la tarjeta o cambie la solicitud.

Conservar los clics por coordenadas solo merece la pena como prueba de regresión para solapamientos visuales. Indican si un clic en una posición de la pantalla puede caer sobre el control equivocado después de un cambio de diseño. No indican si un proceso externo puede identificar y activar el elemento previsto. La automatización semántica de la interfaz es una prueba más sólida para la lógica de autorización; las coordenadas lo son para los accidentes de diseño y foco.

Espera resultados distintos según el modo de aprobación. Una aprobación de sesión suele estar diseñada para autorizar un proceso de agente reconocido durante su ejecución. Si acepta un clic normal, un helper con permiso de Accessibility puede imitarlo, salvo que la aplicación distinga la ruta de entrada o vincule la aprobación a un evento local más fuerte. Puede ser un intercambio aceptable para acciones de bajo riesgo, pero debes declararlo abiertamente.

La aprobación por llamada debería resistir más. Si la acción usa una credencial que puede cambiar datos, publicar una versión o alcanzar un host de producción, pulsar un botón no basta. Pide Touch ID u otra señal local mediada por el sistema. La prueba pasa a ser esta: ¿puede el helper mostrar el aviso, invocar todos los controles accesibles a su alrededor y aun así no completar la acción protegida sin que la persona se autentique? Esa es una propiedad de seguridad mucho más clara.

No informes «bloqueado» simplemente porque el botón no respondió. Informa de toda la cadena: si el helper encontró la tarjeta, si invocó el control, si la aplicación registró una decisión, si el subsistema de autorización emitió un token y si el ejecutor de acciones comenzó. Hoy el helper puede no conseguir pulsar el botón y mañana puede funcionar una ruta alternativa con el teclado. El token de decisión es el límite que importa.

## El robo de foco puede convertir un clic honesto en una decisión equivocada

Los clics directos mediante scripts llaman la atención porque parecen maliciosos. El robo de foco suele ser un fallo más realista, porque puede convertir un gesto humano auténtico en la aprobación de otra solicitud.

La documentación de AppKit deja claro que la activación de una aplicación es una solicitud y no una garantía, y que el estado de activación puede cambiar mientras macOS administra el escritorio. Apple también ofrece APIs que abren aplicaciones con distintos comportamientos de activación. Por eso el código de aprobación debe tratar el estado de primer plano como un evento que observa y verifica, no como un hecho permanente asumido al dibujar la tarjeta.

Prueba la carrera de forma deliberada. Muestra una tarjeta inocua con un nonce único. Coloca el puntero sobre el control de confirmación sin hacer clic. Haz que tu helper controlado active una segunda aplicación de prueba o muestre una ventana de prueba inofensiva. Después realiza el clic previsto en repetidas ejecuciones. Registra qué aplicación estaba activa al presionar y soltar el ratón, qué ventana era propietaria del elemento enfocado y si comenzó la acción protegida.

No aceptes «la tarjeta siguió visible» como aprobado. Una tarjeta visible puede estar inactiva, detrás de otra ventana o conservar sus controles dibujados mientras el foco del teclado se ha desplazado. La ruta de aprobación debe volver a comprobar que la solicitud actual sigue siendo la propietaria de la tarjeta mostrada y que la aplicación está activa cuando consume la aprobación.

Un conjunto sólido de pruebas de robo de foco incluye estos casos:

| Caso | Interferencia | Resultado esperado |
| --- | --- | --- |
| Activación antes del clic | El helper activa otra aplicación antes de presionar el ratón | No hay aprobación; la tarjeta exige una nueva interacción deliberada. |
| Activación entre presionar y soltar | El foco cambia durante el gesto de clic | No hay aprobación; se registra la transición de foco. |
| Ventana superpuesta | Una ventana controlada cubre parte de la tarjeta | No hay acción hasta que la persona vuelva a interactuar con la superficie de aprobación. |
| Sustitución de solicitud | Llega una solicitud nueva mientras la primera tarjeta sigue visible | La tarjeta antigua no puede aprobar la solicitud nueva. |
| Desactivación de la aplicación | La persona cambia de aplicación antes de confirmar | La tarjeta caduca, se retira o exige una confirmación nueva. |

La expresión «exigir una nueva interacción» es importante. Restaurar el foco automáticamente y aceptar el clic original después de una interrupción breve es peligroso. La persona puede creer que el clic cerró la interrupción, mientras la aplicación lo interpreta como aprobación. Retira la decisión pendiente o restablécela de forma visible. La persona debe iniciar una confirmación nueva cuando la aplicación vuelva a estar claramente activa.

No resuelvas esto obligando agresivamente a la aplicación a pasar al frente. Eso crea otro problema: un comportamiento inesperado en primer plano acostumbra a las personas a hacer clic sin mirar. La tarjeta debe aparecer con claridad cuando llega una solicitud, pero no debe librar una batalla de foco con el escritorio. Si pierde la activación en el momento equivocado, puede fallar de forma segura y pedir la aprobación otra vez.

## Las tarjetas antiguas y los cambios de solicitud merecen casos propios

Una tarjeta puede resistir un clic sintético directo y aun así autorizar una acción equivocada. Ocurre cuando la interfaz representa una solicitud y el backend ya ha pasado a otra.

La versión habitual es un fallo de reintento. Un agente solicita una llamada HTTP. Aparece la tarjeta. El agente se desconecta, vuelve a conectarse y envía una segunda solicitud con cabeceras ligeramente distintas. La interfaz reutiliza la tarjeta existente porque parece similar. Un clic que la persona cree dirigido a la solicitud A libera la solicitud B. El texto de identidad puede ser perfecto y la aprobación seguir siendo incorrecta.

Vincula la aprobación a un resumen inmutable de la solicitud. Incluye el tipo de operación, el destino, la identidad o alias de la credencial, los detalles relevantes del objetivo, la identidad del proceso del agente y el nonce. Cuando cambie cualquier campo protegido, invalida la tarjeta actual. No actualices la etiqueta en el mismo lugar conservando un control Approve ya preparado. Cambia la generación de la solicitud y haz que la instancia anterior de la interfaz no pueda completar nada.

Para un fixture de prueba, envía dos solicitudes seguidas:

```text
A: POST staging.example.invalid/echo
   header X-Test-Nonce: alpha-4d8f

B: POST staging.example.invalid/echo
   header X-Test-Nonce: bravo-7a21
```

Deja visible la tarjeta de la solicitud A el tiempo suficiente para que la persona que ejecuta la prueba la identifique. Después envía B mediante el mismo proceso de agente. Intenta confirmar con ratón, teclado, Accessibility y después de un cambio de foco. Los únicos finales aceptables son: A termina con `alpha-4d8f`, B termina tras su propia aprobación nueva con `bravo-7a21`, o ninguna termina. Si la tarjeta visible de A libera B, trátalo como un defecto de autorización que bloquea la publicación.

Haz lo mismo con un cambio de destino que parezca inofensivo a una persona con prisa. Una acción SSH puede conservar el comando mientras cambia el host. Una acción HTTP puede conservar el host mientras cambia la ruta o la credencial inyectada. La tarjeta no necesita imprimir cada byte de la solicitud, pero sí mostrar los detalles que distinguen la decisión de seguridad. El backend debe vincular la solicitud canónica completa, no solo los detalles que decidiste mostrar.

Las pruebas de caducidad también importan. No hagas la cuenta atrás solo como efecto visual. Cuando caduque una solicitud, revoca la ruta de autorización del backend para esa generación. Después prueba una acción de Accessibility disparada exactamente en el límite de caducidad y un evento de soltar el ratón que llegue justo después. El resultado esperado es una denegación con un motivo que indique si la detuvo la caducidad, la pérdida de foco, una diferencia de generación o un fallo de autorización.

## Los registros deben resolver la discusión cuando termine la grabación

Las pruebas de aprobación se convierten en discusiones cuando la única prueba es «hice clic y funcionó». Necesitas registros que permitan a otra persona reconstruir la cadena causal sin confiar en la interpretación del evaluador.

Registra dos flujos relacionados: la ejecución del agente y cada llamada protegida. El registro de ejecución indica quién inició el trabajo y si después se revocó esa ejecución. El registro de llamada indica exactamente qué acción se solicitó, cómo se resolvió la aprobación y si comenzó la ejecución. Mantén los eventos de aprobación en la misma historia de auditoría ordenada que la creación de solicitudes, las transiciones de foco, los resultados de autorización y la ejecución.

Sallyport conserva un registro Sessions para las ejecuciones de agentes y un registro Activity para las llamadas individuales, ambos proyectados desde un registro de auditoría cifrado y encadenado mediante hashes. Su comando `sp audit verify` verifica esa cadena sin conexión sobre texto cifrado y sin necesitar una clave del almacén. Así, el equipo de pruebas puede comprobar la integridad de las pruebas en lugar de depender de una exportación modificable que alguien podría editar después de un resultado inesperado.

En cada ejecución de red team, conserva juntos estos datos:

- El nombre del caso de prueba y la matriz de permisos.
- La identidad del proceso solicitante y su autoridad de firma.
- El resumen de la solicitud, el nonce visible y la generación.
- El método y resultado de la decisión, además del motivo de la denegación si la hubo.
- El primer evento de ejecución o una prueba explícita de que no existe ningún evento de ejecución.

Las cadenas de hashes no demuestran que la política de aprobación sea correcta. Demuestran algo más limitado, pero útil: después de escribir el registro, una persona investigadora puede comprobar si se alteró o reordenó. La prueba de política aporta la otra mitad al demostrar qué eventos permite la aplicación que lleguen a ejecución.

Incluye la revisión de registros en los criterios de aprobación de la prueba. Una ejecución que bloquea correctamente una acción pero solo registra «cancelada» no está terminada. Debes distinguir una invocación sintética de Accessibility de una cancelación humana, una generación antigua de una solicitud caducada y un rechazo por pérdida de foco de un fallo de la aplicación. Esas diferencias convierten un informe de defecto en una familia de pruebas de regresión.

## Aprobar significa que la acción quedó detrás de la decisión humana

No afirmes que una superficie de aprobación es segura porque tu primer script de automatización falló. Un buen resultado de red team tiene un alcance pequeño y exacto: en un Mac controlado y con permisos especificados, ninguna activación sintética ni ruta de interferencia de foco probada produjo un token de autorización válido o inició la acción protegida.

Las pruebas más sólidas dejan una lista incómoda, pero útil, de excepciones. Tal vez la aprobación de sesión permite un clic normal porque el riesgo es bajo y la persona ya aprobó el proceso del agente. Tal vez la aprobación por llamada exige Touch ID para determinadas claves. Tal vez una tarjeta se cancela cada vez que la aplicación pierde la activación, lo que molesta a algunas personas pero evita una carrera de foco. Convierte esas decisiones en comportamiento del producto y pruébalas. La seguridad que solo existe en una reunión de diseño no sobrevivirá a un escritorio lleno de helpers.

Ejecuta esta suite cada vez que cambies la representación de la aprobación, añadas un canal de acciones, modifiques el tratamiento de la identidad de procesos o refactorices el código que pasa de la decisión de la interfaz a la ejecución. La regresión importante no se anunciará como «bypass de Accessibility». Llegará como una limpieza inocua que mueve un callback, reutiliza una ventana o trata una solicitud antigua como equivalente a una nueva.

Mantén clara la afirmación final: la acción protegida solo comienza después de que la solicitud actual recibe la autorización local exigida. Si alguna ruta de automatización de la interfaz puede hacer que esa afirmación sea falsa, la tarjeta de aprobación es decoración hasta que lo corrijas.
