Skip to main content

secret-output-guard

Guardrail conductual cross-repo que previene exposición de tokens, secrets y API keys en outputs de agentes. Usar antes de commitear, publicar retros, escribir mailbox, pegar salida de az/gh/kubectl/curl, o generar cualquier output visible. Motivada por incidente AF-1k.16i1c (gate-token leak).

Quellinformationen

Repository
Umbral-Bot/umbral-agent-stack
Letzte Quellaktivität
28. Juli 2026 um 02:25
Erkannte Sprache von SKILL.md
Spanisch
Sterne
0
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
secret-output-guard
description
Guardrail conductual cross-repo que previene exposición de tokens, secrets y API keys en outputs de agentes. Usar antes de commitear, publicar retros, escribir mailbox, pegar salida de az/gh/kubectl/curl, o generar cualquier output visible. Motivada por incidente AF-1k.16i1c (gate-token leak).
# Skill: secret-output-guard ## Purpose Evitar que tokens, claves API, refresh tokens, contraseñas o credenciales aparezcan en outputs de agentes (chat transcripts, commits, mailbox messages, audit docs, retros). ## Why this skill exists Incidente real **2026-04-26** (sesión `umbral-bot-copilot` AF-1k.16i1c → i1d): un gate token de Container App se imprimió completo en el transcript local del Copilot Chat. Costó una rotación inmediata + commit explícito de revocación. Plan Q2 §6 lista esto como riesgo activo. Este skill es la **regla operativa cross-agent**: cualquier output sustantivo pasa por este checklist antes de quedar persistido. ## When to invoke **Antes de cualquier de estos:** - Imprimir output de comando que pueda incluir credenciales (`az`, `gh`, `curl` con headers, scripts que leen `.env`, `kubectl get secrets`, etc.). - Commitear archivo (incluido `.md`, `.yaml`, `.json`, `.ps1`, `.py`). - Escribir mensaje en `.agents/mailbox/`. - Cerrar Friday retro. - Mover páginas o adjuntos en Notion via MCP (un adjunto puede contener un screenshot con tokens). ## The checklist (8 patrones a buscar) 1. **Tokens explícitos:** `[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{6,}` (JWT-like), `ghp_`, `ghs_`, `gho_`, `github_pat_`, `sk-`, `sk_test_`, `sk_live_`, `Bearer `, `Basic `. 2. **Azure:** `DefaultEndpointsProtocol=`, `AccountKey=`, claves de Cognitive Services / OpenAI / Search (~32–88 chars base64), `client_secret=`. 3. **Google:** `AIza...`, `ya29.`, `1//0`, `GOOGLE_REFRESH_TOKEN=`. 4. **Notion / Granola:** `secret_...`, `ntn_`, integration tokens. 5. **MercadoPago / Stripe / pasarelas:** `APP_USR-...-...-...`, `whsec_`, `pi_`. 6. **SSH / GPG:** `BEGIN OPENSSH PRIVATE KEY`, `BEGIN PGP PRIVATE KEY`, `BEGIN RSA PRIVATE KEY`. 7. **Connection strings:** `mongodb+srv://`, `postgres://user:pass@`, `mysql://`, cualquier URL con `:.+@`. 8. **Variables de entorno con nombre sospechoso** mostradas con valor: cualquier env que contenga `KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL|API_KEY` en su nombre. ## What to do if you find one | Situación | Acción | |---|---| | Token apareció en chat output (no commiteado) | Pedir a David rotación inmediata. Reemplazar valor por `[REDACTED]` antes de continuar. NO commitear el output. | | Token ya está commiteado | **Stop.** Avisar a David. Rotar primero, después usar `git filter-repo` o equivalente. NO basta con un commit que borre — el token sigue en historia. | | Variable de entorno con nombre sospechoso pero sin valor mostrado | OK — solo el nombre es información pública. | | Comando va a imprimir token (ej: `az ad sp create-for-rbac`) | Redirigir output a archivo en `.gitignore`, leer con redacción, o usar flag `--query` para extraer solo lo no-secreto. | ## Reglas operativas para agentes 1. **Default-deny.** Si tenés duda de si un valor es sensible, asumí que sí. 2. **Nunca pegar `.env` completo** en chat ni en commits, ni siquiera con valores fake. 3. **Comandos `az`/`gcloud`/`kubectl`/`gh` que retornan credenciales:** siempre con `--query` o `| ConvertFrom-Json | Select` para acotar. 4. **Si pedís a David un secreto:** que lo paste en input efímero (terminal interactivo), no en chat history. 5. **Workspace settings:** `.vscode/settings.json` no debe tener `terminal.integrated.persistentSessionReviveProcess: never` desactivado (los buffers de terminal pueden persistir en disco). 6. **Friday retro:** sección `Quality gate` incluye checkbox `[ ] No hay tokens/secrets en este doc`. No se commitea retro sin esa marca. 7. **Si un agente externo (Codex, otro hilo Copilot) pasó un secret en handoff via mailbox:** rechazar el mensaje con `status: cancelled` + razón, pedir reenvío sin secreto. 8. **Inspección de `/proc/$PID/environ` (Linux runtime forensics):** NUNCA `cat`, `strings`, ni `grep` con patrones que matcheen el VALOR del secret (ej: `grep "COPILOT"`, `grep "TOKEN"`, `grep "sk-"`). El environ contiene los secretos en plaintext y cualquier match imprime el valor completo. **Incidente motivante F-INC-001 (2026-05-07):** durante audit OpenClaw, un agente ejecutó `grep "COPILOT" /proc/$PID/environ` y volcó `COPILOT_GITHUB_TOKEN=ghp_...` al output → rotación inmediata. **Patrón aprobado** — listar solo nombres de var, o nombre + longitud + fingerprint: ```bash # Solo nombres (seguro siempre): tr '\0' '\n' < /proc/$PID/environ | awk -F= '{print $1}' | sort -u # Nombre + longitud + sha8 del valor (para confirmar fingerprint sin leak): tr '\0' '\n' < /proc/$PID/environ \ | awk -F= '/(KEY|TOKEN|SECRET|PASSWORD|CREDENTIAL)/ { val=$0; sub(/^[^=]*=/, "", val); "printf %s \"" val "\" | sha256sum | cut -c1-8" | getline h; printf "%s=<len=%d sha8=%s>\n", $1, length(val), h }' | sort ``` Regla equivalente para cualquier dump de entorno (`env`, `printenv`, `ps eww`, `systemctl show --property=Environment`): aplicar el mismo filtrado nombre-only o nombre+fingerprint. ## Tool-emitted partial leaks Algunas herramientas oficiales imprimen prefijos parcialmente enmascarados de secretos por defecto. Aunque "solo" sea el prefijo (ej: `gho_abcd******`), es suficiente para correlacionar tokens en audit logs y queda en transcripts de chat. Filtrar SIEMPRE. | Herramienta | Patrón que leakea | Mitigación | |---|---|---| | `gh auth status` | ` - Token: gho_<REDACTED>` (prefijo parcial enmascarado) | `gh auth status 2>&1 \| grep -v "^ - Token:"` | | `git config --list` (cuando hay creds en URLs o `extraheader`) | `url.https://USER:<REDACTED>@github.com/...`, `http.extraheader=AUTHORIZATION: bearer <REDACTED>` | `git config --list \| grep -v "url\.\\\|extraheader"` | | `az account show` con credenciales en cache | algunos campos (`tenantDefaultDomain`, `homeAccountId`) son fingerprints válidos para correlación | `az account show --query "{name:name, id:id, state:state}" -o table` (whitelist explícita) | | `gh api .../actions/secrets` | nombres de secrets son metadata pero pueden filtrar topología | OK exponer nombres si la org/repo es público; redactar en docs externos | | `kubectl get secret <name> -o yaml` | volca `data.<key>: <base64>` que es trivialmente decodificable | NUNCA imprimir; usar `kubectl get secret <name> -o jsonpath='{.metadata.creationTimestamp}'` o describir solo metadata | | `docker inspect <container>` | `Config.Env` lista todas las env vars con valor | `docker inspect <c> --format '{{range .Config.Env}}{{println (index (split . "=") 0)}}{{end}}'` (solo nombres) | | `systemctl show <unit>` | `Environment=KEY=VALUE` con valor | `systemctl show <unit> --property=Environment \| sed 's/=[^[:space:]]*/=<REDACTED>/g'` o filtrar nombres con awk como en regla 8 | **Regla general:** si una herramienta oficial imprime "parte" del secreto por conveniencia de UX, tratar ese prefijo como secreto completo. Mitigar con un pipe de filtrado documentado aquí; agregar entradas nuevas a esta tabla cuando se descubran. **Origen de esta sección:** task O7c (`.agents/tasks/2026-05-08-O7c-gh-auth-status-leak-pattern.md` en `umbral-agent-stack`), durante el smoke O7 (PR #378) Copilot-VPS observó el leak de `gh auth status` y no había mitigación documentada. **Ejemplos en esta tabla:** todos usan placeholder `<REDACTED>`. Nunca pegar tokens reales (ni siquiera enmascarados) como ejemplo. ## Triage de hallazgos: real vs falso positivo No todo match del guardrail es un leak. Cada hallazgo debe pasar por triage **antes** de decidir qué hacer. ### Si el hallazgo es un leak real 1. **Rotar la credencial** afectada en su sistema de origen (Azure, GitHub, Notion, etc.). 2. **Redactar el output** donde apareció: reemplazar con `<REDACTED>` en logs, PRs, evidencia y memoria de chat si es posible. 3. **Reportar a David** con: qué secreto, dónde apareció, ya rotado sí/no, qué superficies pudieron leerlo. 4. Abrir issue o task de seguimiento para verificar que no quedó cacheado en otro lado (CI logs, artifacts, screenshots). ### Si el hallazgo es un falso positivo Documentar explícitamente (no silenciar sin justificación): - **(a) Patrón que matcheó** — qué regla del guardrail disparó (ej. `sk-...`, `ghp_...`, prefijo `eyJ`). - **(b) Origen del valor** — por qué no es un secreto activo (placeholder en docs, ejemplo de la propia skill, hash público, fixture de test, token expirado/revocado y verificado como tal). - **(c) Evidencia de que no es secreto activo** — link al doc fuente, commit que introdujo el placeholder, o verificación de revocación. ### Regla dura **Nunca silenciar el guardrail sin justificación escrita.** Si no podés producir (a) + (b) + (c) para un falso positivo, tratá el hallazgo como leak real hasta demostrar lo contrario. Inclinar la duda hacia rotar/redactar es siempre más barato que asumir que era un placeholder y equivocarse. ## What this skill does NOT do - No reemplaza un escáner real (`gitleaks`, `trufflehog`). Es un guardrail conductual. - No es una garantía — los humanos siguen siendo la última defensa. - No se aplica a runtime real de aplicaciones (donde los secrets sí existen y se manejan via env vars / key vault). Aplica solo a **outputs visibles para humanos**. ## Sugerencia futura Cuando el repo tenga >50 commits con outputs estructurados, evaluar instalar `gitleaks` como pre-commit hook (`policies/05` §"Live agent contract changes" implica formalizar esto en `pre-commit-config.yaml`). ## Topología canónica y transición E1 Desde 2026-07-27, la fuente canónica de esta skill es `umbral-skills-registry/notion-governance-skills/secret-output-guard/`. GitHub `origin/main` se actualiza antes que cualquier superficie y los deploys user-level se escriben exclusivamente mediante el gate `tools/ship_skill.py` del registry. Existe temporalmente un writer legacy en `umbral-agent-stack/scripts/maintenance/check_skill_mirrors.py`: todavía toma `notion-governance/.agents/skills/secret-output-guard/SKILL.md` como fuente y puede copiarla hacia `~/.copilot`, `~/.codex` y el mirror repo-level de `umbral-agent-stack`. En el momento de la decisión H1b, las cuatro copias completas tenían el mismo contenido normalizado (`a5085d…f459`); la única diferencia observada era LF frente a CRLF. El seguimiento **E1** debe reapuntar ese checker al registry y retirarle los destinos user-level, conservándolo solo para los mirrors repo-level que todavía lo necesiten. Hasta cerrar E1, no usar `--fix` como mecanismo de despliegue. David aceptó que E1 no es una urgencia operativa inmediata, pero debe cerrarse antes del próximo cambio de contenido de esta skill. Un `--fix` previo a E1 podría revertir esta declaración de topología aunque el cuerpo operativo del guardrail siga siendo equivalente. ## Cross-references - Friday retro: [`q-friday-retro`](../q-friday-retro/SKILL.md) aplica este guardrail antes de commitear la retro.
Auf GitHub ansehen