# Archivos temporales de herramientas de agentes: encuentra y limpia rastros secretos

Los archivos temporales de una herramienta de agente merecen el mismo cuidado que un repositorio de código fuente. Un agente de programación puede crear el cuerpo de una solicitud, ejecutar un comando, repetirlo con registros detallados, copiar la salida en una caché y dejar el directorio de trabajo original aparentemente limpio. El material sensible sigue en el equipo, a menudo en lugares que nadie incluyó en la revisión.

El supuesto equivocado es que un secreto solo se filtra cuando el agente lo muestra en una conversación. En la práctica, la filtración más habitual es una solicitud con credenciales copiada en un archivo temporal para que un asistente pueda enviarla, o un comando fallido registrado porque alguien activó el modo de depuración la semana pasada. La limpieza importa, pero la prevención importa más: no entregues credenciales sin procesar a un agente si otro proceso puede realizar la acción.

## Las herramientas de agentes crean copias fuera del árbol de trabajo

Una ejecución del agente puede dejar datos en varias capas aunque el directorio del proyecto no contenga ningún rastro evidente. El árbol de trabajo es solo una ubicación de escritura, y los desarrolladores suelen revisarlo porque les resulta familiar. El agente, su entorno de ejecución, el shell, el gestor de paquetes, el editor, el terminal y el sistema operativo tienen sus propios lugares donde escribir.

Empieza separando tres tipos de material. Los archivos de carga contienen lo que el agente pretendía enviar, como cuerpos JSON de solicitudes, lotes SQL, exportaciones de prompts, archivos de parches o configuraciones SSH. Los archivos de salida contienen lo que devolvió el sistema remoto, como respuestas de API, salida de comandos, exportaciones de bases de datos y páginas de error. El material de diagnóstico contiene las pruebas que rodean la operación: argumentos de comandos, detalles del entorno, trazas de pila, reintentos y trazas.

Los tres tipos pueden contener datos sensibles. Una carga puede incluir un token porque un script lo insertó antes del envío. La salida puede contener un registro de cliente o un secreto de despliegue. El diagnóstico puede capturar ambos en la misma línea, por eso un registro de depuración suele causar más daño que el propio comando fallido.

Un inventario útil incluye estos lugares:

- El directorio temporal del proceso y `/private/tmp` en macOS.
- Los directorios de soporte de aplicaciones, cachés y registros bajo `~/Library`.
- El historial del shell, las exportaciones del desplazamiento del terminal y los envoltorios de comandos.
- Carpetas locales del proyecto como `.cache`, `tmp`, `logs`, `.agent` y los directorios de datos de prueba.
- Capas escribibles de contenedores, montajes vinculados, espacios de trabajo de CI y artefactos de compilación subidos.

No des por hecho que un agente usa un único directorio predecible. Las distintas versiones, extensiones, entornos de ejecución y rutas de error toman decisiones diferentes. Una herramienta puede usar el directorio indicado por `TMPDIR`; un asistente invocado por ella puede usar `/tmp`; una biblioteca puede colocar una caché en el proyecto actual. La única respuesta defendible sale de observar tu propia ejecución.

Lo mismo se aplica a la salida de los comandos. Una redirección del shell como `command > result.txt` es evidente. Menos evidentes son los registros de multiplexores de terminal, los archivos de depuración del gestor de paquetes, las trazas del cliente HTTP y los archivos de recuperación del editor escritos después de que el agente modifica un documento. Si un agente puede llamar a varias herramientas, supone que cada una conserva datos de forma independiente hasta que lo pruebes.

## Mapea las ubicaciones de escritura reales antes de limpiarlas

No puedes limpiar una ruta que has supuesto mal. Ejecuta el agente con un marcador único e inofensivo y después busca ese marcador en todos los lugares donde la ejecución podría escribir. Usa datos falsos que se parezcan lo suficiente a los reales para recorrer las mismas rutas de código, pero nunca uses un token de producción en este ejercicio.

En macOS, empieza registrando el directorio temporal heredado por el shell que inicia el agente:

```sh
echo "TMPDIR=$TMPDIR"
ls -ld "$TMPDIR" /private/tmp /tmp
```

macOS suele asignar a cada usuario un directorio bajo `/var/folders`, y `/tmp` apunta a `/private/tmp`. La ruta generada exacta no es una frontera de seguridad y puede cambiar. Registra el valor en lugar de fijar una ruta en un script de limpieza.

Crea un marcador fácil de encontrar y haz que el agente ejecute una acción representativa que lo pase por un cuerpo de solicitud, un argumento de comando y la salida del comando. Este ejemplo usa deliberadamente una cadena que no es secreta:

```sh
export AGENT_TRACE_MARKER='TEMP-PAYLOAD-CANARY-9f2a7c'
printf '%s\n' "$AGENT_TRACE_MARKER" > /tmp/agent-canary.txt
```

Después de la ejecución, busca en las ubicaciones habituales del usuario. `grep` puede encontrar archivos binarios y errores de permisos, así que úsalo como herramienta de descubrimiento, no como prueba de que no existe nada.

```sh
grep -RIl --exclude='*.sqlite*' \
  'TEMP-PAYLOAD-CANARY-9f2a7c' \
  "$TMPDIR" /private/tmp \
  "$HOME/Library/Caches" \
  "$HOME/Library/Logs" \
  "$HOME/Library/Application Support" 2>/dev/null
```

La salida debería tener la forma de una lista de rutas de archivo. Para cada ruta, responde cuatro preguntas: qué proceso la escribió, qué categoría de contenido contiene, quién puede leerla y cuándo desaparece. Si no puedes vincular un archivo a un proceso, revisa su fecha de modificación y repite la prueba con un marcador nuevo. No borres primero los archivos que no entiendes. Podrías eliminar la pista que indica qué componente necesita una nueva configuración.

Usa `fs_usage` cuando un archivo aparezca solo durante un instante. Puede mostrar la actividad del sistema de archivos de un proceso mientras se ejecuta el agente:

```sh
sudo fs_usage -w -f filesystem | grep -iE 'agent|tmp|cache|log'
```

Este comando genera mucho ruido. Ejecútalo durante una prueba breve, guarda las rutas observadas fuera de un directorio de proyecto compartido y detenlo después. Un proceso que escribe y elimina un archivo en un segundo no aparecerá en un listado posterior, pero su contenido puede haber llegado a una copia de seguridad, un observador o cualquier otro recopilador de registros.

## La construcción de solicitudes es donde suelen filtrarse las credenciales

El archivo temporal más peligroso suele crearse antes de la llamada de red. Muchos scripts construyen una solicitud en un archivo porque entrecomillar JSON en un shell resulta incómodo. El archivo empieza siendo inofensivo, pero alguien añade después un campo `Authorization`, una cookie o una cadena de conexión completa para que la solicitud funcione. Por accidente, se convierte en un contenedor permanente de secretos.

Evita este patrón:

```sh
cat > /tmp/request.json <<'EOF'
{"endpoint":"https://api.example.invalid/export","token":"$PRODUCTION_TOKEN"}
EOF
```

El heredoc entre comillas simples no expande la variable, lo que puede parecer seguro. Una modificación posterior suele cambiar el delimitador o utilizar otro método de construcción. Y lo más importante: el diseño sigue enseñando a colocar una credencial en un artefacto de solicitud. Un volcado de depuración de la solicitud completa la expondrá.

Coloca las credenciales en la capa de transporte cuando el protocolo lo permita y asegúrate de que el transporte no registre las cabeceras. Las cabeceras de autorización HTTP son preferibles a un token en una cadena de consulta, pero no son mágicamente seguras. Los clientes detallados, la configuración del proxy, los gestores de excepciones y el código personalizado de reintentos aún pueden registrarlas.

La especificación HTTP Semantics, RFC 9110, indica que los agentes de usuario no deberían enviar una URI con información sensible en una cabecera Referer. Esa advertencia refleja un hecho más amplio: las URL llegan más lejos de lo que esperamos. Terminan en registros de acceso, historial del navegador, comandos de terminal copiados, tickets de soporte y sistemas de analítica. No pongas tokens de portador, URL firmadas con permisos amplios, contraseñas ni cadenas de conexión de bases de datos en una URL, salvo que el protocolo no deje otra alternativa y la credencial tenga una vida muy corta.

Los argumentos del shell requieren el mismo cuidado. En sistemas tipo Unix, otro proceso local puede observar los argumentos según los permisos y la configuración de la plataforma. Los argumentos también llegan al historial del shell si una persona los copia, al registro de un ejecutor de tareas y a la transcripción de herramientas del agente. Las variables de entorno reducen algunas de esas rutas, pero crean otras, como los procesos secundarios heredados y los informes de diagnóstico. Ninguna de las dos opciones es una caja fuerte.

El límite adecuado es sencillo: el agente solicita una operación indicando el destino y las entradas no sensibles. Un responsable de credenciales independiente añade la autenticación justo antes de la llamada. El agente recibe la respuesta o un error redactado, no la credencial utilizada para obtenerlo.

Sallyport sigue ese límite para acciones HTTP y SSH: la credencial permanece en su bóveda cifrada y el agente pide a la aplicación local que realice la acción. Esto elimina el secreto sin procesar del contexto del agente, pero no vuelve inofensivos los cuerpos de respuesta ni los archivos de depuración creados por el agente. Aún debes controlar qué devuelve la acción y dónde lo escribe el agente.

## El modo de depuración convierte los fallos habituales en registros de secretos

Los registros de depuración son útiles para diagnosticar una integración rota. También están diseñados para conservar las evidencias que los registros normales omiten. Eso suele incluir cabeceras, cuerpos completos de solicitudes y respuestas, líneas de comandos, configuraciones derivadas del entorno, estados de reintento y trazas de pila.

El error consiste en dejar activada una opción de depuración amplia porque el problema ocurrió una vez. La opción se aplica después a un trabajo no relacionado, semanas más tarde, cuando alguien ejecuta una exportación o un despliegue con permisos reales. El archivo resultante puede quedar bajo una caché que nadie sabe que pertenece a la herramienta.

Trata los diagnósticos como una clase de datos diferente, con una vida útil corta y explícita. Antes de activar una traza, decide qué pregunta debe responder. Si quieres saber si DNS resuelve, captura la salida del resolvedor. Si quieres saber si el servidor rechaza un campo JSON, registra el código de estado y un fragmento de respuesta redactado. El registro completo del tráfico debería ser excepcional porque conserva material que no necesitabas para resolver el problema.

Integra la redacción en el componente que escribe, no después. Un trabajo de limpieza que busque `Authorization:` en los registros no encontrará cabeceras personalizadas, campos JSON, parámetros de URL, valores multilínea, bloques base64 ni contenido de respuesta que contenga secretos. Cuando un recopilador de registros o un servicio de copias de seguridad copia el archivo sin redactar, limpiar después el original sirve de poco.

Un envoltorio de diagnóstico seguro tiene una lista permitida de campos que puede escribir. Por ejemplo, registra el método HTTP, el host, la ruta sin parámetros de consulta, el código de estado, la duración, el número de bytes de respuesta y un identificador de solicitud. No registres todas las cabeceras con la intención de ocultar después las peligrosas. Los formatos de las credenciales cambian más rápido que los scripts de limpieza.

Esta diferencia importa con las herramientas que muestran un comando antes de ejecutarlo. Mostrar un comando no equivale a registrar el entorno efectivo del comando, y ninguna de las dos cosas equivale a una traza de paquetes. Averigua qué representación guarda la herramienta. Puede redactar lo que muestra en la consola y dejar intacto un registro detallado.

Comprueba deliberadamente las rutas de error. Cancela una solicitud a mitad de camino. Envía JSON incorrecto. Provoca un fallo de autenticación con una credencial sintética. Fuerza un tiempo de espera para activar los reintentos. Estas rutas crean archivos temporales de solicitudes y excepciones que las llamadas correctas quizá no creen. También son las rutas que un atacante puede provocar para que el sistema revele más de lo que revela durante su funcionamiento normal.

## En macOS, borrar exige contención, no una destrucción mágica

En el almacenamiento moderno de macOS, el borrado seguro no significa sobrescribir repetidamente un nombre de archivo. El nivelado de desgaste de los SSD, el comportamiento de copia al escribir, las instantáneas, la sincronización en la nube y las copias de seguridad impiden que el software prometa de forma fiable que una sobrescritura ha tocado todos los restos físicos. El viejo hábito de ejecutar una utilidad de destrucción hace que las personas crean que han terminado sin abordar las copias importantes.

Usa el borrado para eliminar archivos activos y accesibles. Usa la contención para limitar desde el principio dónde puede existir el material sensible. Usa el cifrado y la rotación de credenciales cuando sospeches que ha habido exposición.

Para un directorio temporal específico del agente, créalo con permisos restrictivos antes de la ejecución y elimínalo después:

```sh
run_dir="$(mktemp -d "${TMPDIR%/}/agent-run.XXXXXX")" || exit 1
chmod 700 "$run_dir"
trap 'rm -rf "$run_dir"' EXIT HUP INT TERM
export TMPDIR="$run_dir"

# launch the agent from this same shell
# agent-command
```

Así la ejecución tiene un área temporal conocida y las salidas normales la eliminan. No obliga a todas las dependencias a obedecer `TMPDIR` ni elimina el contenido copiado a otro directorio. Por eso el ejercicio de descubrimiento va primero.

Evita `rm -rf /tmp/*` y comandos generales de limpieza similares. Pueden romper otros procesos, borrar material que necesitas para investigar y crear la falsa sensación de que `/tmp` era el único lugar que había que revisar. Elimina solo los directorios creados por tu lanzador y los nombres cuya propiedad pueda demostrar tu código de limpieza.

Si un secreto pudo escapar, rótalo o revócalo antes de iniciar una larga campaña de limpieza. Un token de API activo copiado en un registro desconocido es una vía de acceso vigente. Su nombre de archivo es menos urgente que la autoridad que concede. Después busca copias derivadas en herramientas de copia de seguridad, unidades compartidas, artefactos de CI, agregación de registros y sistemas de búsqueda de endpoints. Eliminar el original sin abordar las copias deja intacta la exposición.

El almacenamiento local cifrado reduce el riesgo de perder un dispositivo, pero no protege frente a otro proceso que se ejecute bajo la misma cuenta de usuario desbloqueada. Los permisos de archivo siguen importando. Un modo de directorio `700` indica que otras cuentas locales no deberían explorarlo. No detiene un proceso del agente que ya se ejecuta como tú ni un cliente de sincronización al que autorizaste a leer tu directorio personal.

## Un límite de credenciales elimina las cargas más peligrosas

La limpieza tiene un límite. Si el agente recibe un token de producción en un prompt, variable de entorno, archivo de configuración o salida de comando, puede colocar ese token en cualquier archivo que pueda escribir. Puedes reducir las consecuencias, pero no puedes volver segura esa arquitectura mediante una buena limpieza.

Mantén la propiedad de los secretos fuera del proceso del agente. El agente debería expresar una intención como «envía esta solicitud de despliegue a este endpoint aprobado» o «ejecuta este comando SSH usando esta conexión con nombre». Un responsable local de credenciales debe decidir si autoriza la acción, insertar la credencial, ejecutarla y registrar el resultado. Si el agente no necesita el token, nunca necesita recibir siquiera un token ficticio.

Esto también hace más concreta la revisión de archivos temporales. Puedes inspeccionar los directorios temporales del agente en busca de entradas del usuario, código generado y datos devueltos. No tienes que suponer que cada archivo puede contener todos los secretos de producción disponibles para el agente.

Un modelo fijo de aprobación tiene una ventaja operativa frente a una colección de reglas personalizadas. Las personas pueden explicarlo durante un incidente. Sallyport mantiene su bóveda bloqueada hasta que la autenticación local la abre, solicita autorización cuando un proceso de agente actúa por primera vez y puede exigir aprobación para cada uso de determinadas credenciales. Es un control claro sobre la autoridad, no un intento de adivinar si un comando generado parece sospechoso.

No confundas la autorización de una acción con la minimización de datos. Una llamada aprobada puede devolver un cuerpo de respuesta sensible, y el agente puede escribirlo en un archivo del proyecto, una caché o una transcripción. Diseña las respuestas para obtener el resultado útil más pequeño. Si una tarea solo necesita el identificador y el estado del despliegue, no devuelvas un documento de configuración completo. Si una API permite filtrar en el servidor, úsalo.

SSH merece una atención especial porque la salida de los comandos remotos no tiene límites claros. Un comando que lee un archivo de configuración, muestra variables de entorno tras un error o ejecuta una utilidad de despliegue detallada puede enviar secretos de vuelta al agente. Trata la salida SSH como datos que necesitan un destino, un periodo de conservación y una regla de revisión. La credencial puede estar protegida mientras la salida devuelta sigue siendo peligrosa.

## Asigna a cada ejecución un responsable, un directorio y una caducidad

Una política de limpieza funciona cuando vincula los archivos a una ejecución concreta. Un script nocturno genérico no puede saber si un directorio temporal pertenece a un proceso activo, a un trabajo fallido que merece investigación o a otra aplicación. Un lanzador de agentes sí puede.

Usa un identificador de ejecución en el nombre del directorio temporal, registra la hora de inicio y conserva fuera de ese directorio un pequeño manifiesto con solo metadatos no sensibles. El manifiesto debe indicar qué directorio creó el lanzador, qué proceso es responsable, cuándo debe caducar y si la ejecución terminó. No coloques argumentos de comandos, cuerpos de solicitudes ni valores del entorno en ese manifiesto.

Un ciclo de vida práctico tiene cuatro acciones:

1. Crea un directorio temporal privado antes de iniciar el agente.
2. Configura las variables de entorno de rutas temporales para el agente y los asistentes que controles.
3. Elimina el directorio al salir normalmente y marca el manifiesto como completado.
4. Haz que un trabajo programado señale los directorios abandonados que superen un umbral definido para que una persona los revise.

El trabajo programado debe señalar, no borrar automáticamente, los directorios de ejecuciones incompletas. Puede que un proceso aún esté escribiendo. Un despliegue fallido puede necesitar evidencias. Cuando una persona confirme que el directorio está abandonado y que no hace falta para un incidente, elimínalo aplicando la misma regla de propiedad.

No coloques el directorio temporal dentro de un repositorio. Las búsquedas del proyecto, los comandos de estado del control de versiones, la indexación del IDE, los observadores de archivos y los clientes de copias de seguridad prestan atención a los repositorios. Un directorio hermano en una ubicación temporal controlada por el usuario suele ser más fácil de excluir de las herramientas de desarrollo y de destruir como una unidad.

Ten cuidado con la limpieza automática iniciada por el propio agente. Un agente que puede elegir rutas de limpieza arbitrarias puede borrar archivos de código o evidencias. El lanzador debe construir la ruta, conservarla en su propio estado y eliminar únicamente rutas que coincidan con su patrón de nombres generado después de canonicalizarlas. La interpolación del shell en el código de limpieza ya causa suficientes accidentes sin añadirle generación autónoma de texto.

El objetivo no es conservar nada. Necesitas suficientes evidencias operativas para responder qué ejecución hizo una solicitud y si tuvo éxito. Mantén ese registro separado de las cargas sin procesar y de la salida completa, y haz que sus campos sean deliberadamente aburridos.

## Los registros y las copias de seguridad necesitan una decisión de conservación

Un archivo temporal se convierte en un registro conservado en cuanto otro sistema lo copia. El software de copias de seguridad, las carpetas de sincronización en la nube, las herramientas de protección de endpoints, los informes de fallos, la subida de artefactos de CI y los registros centralizados pueden prolongar su vida. La ruta original puede desaparecer mientras la copia útil sigue en otro lugar.

Enumera todos los procesos que pueden leer los directorios utilizados por las ejecuciones del agente. En un equipo de desarrollo suelen estar incluidos un cliente de copias de seguridad, un indexador del editor, una interfaz de control de código fuente, un grabador de terminal y un analizador de malware. Algunas copias son convenientes. La cuestión es elegirlas y establecer su conservación, en lugar de descubrirlas después de que un token aparezca en un archivo restaurado.

Mantén la salida de diagnóstico sin procesar fuera de los directorios que se sincronizan automáticamente. Esto incluye las carpetas del escritorio, las carpetas de proyectos compartidas y cualquier espacio de trabajo que un cliente de CI empaquete como artefacto. Si una herramienta debe producir una respuesta grande para revisarla, guárdala en un directorio privado con permisos restrictivos, establece una caducidad y exclúyela de la sincronización habitual cuando tus herramientas permitan exclusiones.

Un registro de auditoría encadenado mediante hashes es diferente de un volcado de depuración. Un registro de auditoría debe responder quién solicitó una acción, cuándo se ejecutó y qué categoría de resultado tuvo, sin duplicar secretos. Sallyport proyecta sus diarios Sessions y Activity a partir de un registro de auditoría cifrado, y `sp audit verify` puede verificar la cadena de hashes sin conexión y sin una clave de la bóveda. Así puedes conservar evidencias de la actividad del agente sin tratar cada carga sin procesar como un requisito de auditoría.

La conservación debe cubrir las excepciones. Durante un incidente, no permitas que el temporizador normal de limpieza destruya las evidencias necesarias para entender lo ocurrido. Restringe el acceso, conserva deliberadamente los archivos relevantes y documenta su ubicación. Después rota las credenciales afectadas y elimina el material conservado cuando el proceso del incidente ya no lo necesite. Conservar datos durante un incidente no justifica volcar toda la salida habitual del agente en almacenamiento permanente.

Examina también las copias de seguridad después de cambiar la política. Excluir ahora un directorio temporal solo afecta a las futuras ejecuciones de copia. Las instantáneas existentes pueden contener material antiguo hasta que caduque el periodo de conservación de su proveedor. Registra ese hecho en la respuesta a la exposición en lugar de fingir que un comando `rm` reescribió la historia.

## Prueba la limpieza como si fueras un atacante local curioso

Una política de limpieza que nunca ha afrontado una prueba de búsqueda es una promesa, no un control. Pruébala con un marcador falso que se parezca a las cadenas que temes perder y después inspecciona el equipo como si otro proceso local quisiera encontrarlo.

Ejecuta primero la prueba en condiciones normales. Coloca el marcador en un archivo de entrada, un campo de solicitud y una salida de comando simulada. Ejecuta el agente hasta el final, espera a que actúe el mecanismo de limpieza y busca en la ubicación temporal, las cachés, los registros, el directorio del proyecto, el historial del shell y las carpetas de soporte habituales. Registra cada coincidencia y explí cala.

Después prueba los casos incómodos. Fuerza un fallo del proceso. Cancélalo durante una solicitud de red. Activa los registros más detallados que admita la herramienta. Ejecútalo desde un IDE en lugar de un terminal. Usa un contenedor con un montaje vinculado al host. Cada variación puede dirigir la salida a un lugar diferente.

Busca por el marcador, no por el nombre del archivo. Una carga copiada puede recibir un nombre aleatorio, entrar en una base de datos SQLite o comprimirse en un archivo. Si un archivo binario o una base de datos contiene el marcador, identifica quién lo escribió y decide si necesita configuración, exclusión, redacción o un límite de ejecución distinto.

Conserva un pequeño registro de prueba con la versión del agente, la versión del sistema operativo, las extensiones activadas, los comandos probados, las rutas encontradas y el resultado de la limpieza. Actualízalo cuando añadas una herramienta capaz de ejecutar comandos o enviar solicitudes. Es un trabajo poco llamativo, pero detecta regresiones que una revisión de seguridad del código fuente no descubrirá.

Haz hoy la primera prueba con un marcador inofensivo. Si aparece en un lugar inesperado, corrige el componente que escribe antes de crear un script de borrado más elaborado. El archivo temporal más limpio es el que nunca recibió una credencial ni una respuesta sensible en primer lugar.
