Getting Started (Actualizado: 3/6/2026)

Checklist de auditoría de permisos Claude Code: revisar allow/deny en 5 minutos

Audita reglas allow/deny, comandos riesgosos, variables de entorno y logs de Claude Code cada mañana en 5 minutos.

Checklist de auditoría de permisos Claude Code: revisar allow/deny en 5 minutos

La auditoría define qué puede hacer Claude Code hoy

El mayor riesgo no siempre es decirle a Claude Code “hazlo todo”. En la práctica, el fallo suele ser más silencioso: ayer permitiste Bash(git push *) o un WebFetch demasiado amplio, hoy abres un repositorio de producción y olvidas que esa regla sigue activa.

Esta guía propone una auditoría matinal de cinco minutos. allow significa ejecutar sin pedir otra confirmación, ask significa pedir aprobación, y deny significa bloquear. Para quien empieza, son los bordes de la mesa de trabajo que entregas al agente.

Al 3 de junio de 2026, la documentación oficial indica que /permissions muestra los permisos de herramientas y el archivo de configuración de origen. Las reglas se evalúan deny -> ask -> allow, por lo que deny tiene prioridad. Verifica siempre las páginas oficiales de Claude Code Permissions, Settings y Environment variables.

Usa esta rutina después del checklist de los primeros 30 minutos y antes del trabajo de equipo con la plantilla CLAUDE.md. No busca burocracia, sino trabajo lo bastante pequeño como para revisarlo bien.

El flujo de cinco minutos

Empieza por el riesgo de la tarea de hoy, no por el JSON. Documentación, dependencias, formularios y despliegues no deberían compartir los mismos límites.

flowchart TD
  A[Start session] --> B[Check git status]
  B --> C[Open /permissions]
  C --> D[Review allow / ask / deny]
  D --> E[Inspect env and logs policy]
  E --> F[Run local audit script]
  F --> G[Write handoff note]
Dónde mirarQué confirmarSeñal de riesgo
git status --shortSi ya hay cambiosHay diff y nadie sabe de quién es
/permissionsQué archivo aporta cada reglaBash, WebFetch o Edit completos en allow
.claude/settings.jsonReglas compartidas del equipoDeploy, billing o links de pago están autoaprobados
.claude/settings.local.jsonReglas personales temporalesUna regla de exploración de ayer sigue viva
envHistorial y subprocesosSe necesitan logs, pero CLAUDE_CODE_SKIP_PROMPT_HISTORY=1

Un matiz oficial importa: un deny con solo Bash quita la herramienta Bash del contexto de Claude, mientras que Bash(rm *) mantiene la herramienta disponible y bloquea solo llamadas coincidentes. Una línea en CLAUDE.md guía el comportamiento, pero no impone un límite técnico.

Ejemplo mínimo de settings para el repo

Las reglas compartidas funcionan mejor si lo peligroso se bloquea pronto y lo que requiere juicio queda en ask. Este ejemplo sirve como base para .claude/settings.json.

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(npm run lint)",
      "Bash(npm run test *)",
      "Bash(git status *)",
      "Bash(git diff *)",
      "Read(./src/**)",
      "Read(./docs/**)"
    ],
    "ask": [
      "Bash(npm install *)",
      "Bash(pnpm add *)",
      "Bash(git push *)",
      "Bash(npm run deploy *)",
      "Edit(./.github/**)"
    ],
    "deny": [
      "Bash(curl *)",
      "Bash(wget *)",
      "Bash(rm -rf *)",
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "WebFetch(domain:pastebin.com)"
    ],
    "defaultMode": "default",
    "disableBypassPermissionsMode": "disable"
  },
  "env": {
    "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB": "1"
  }
}

Aquí, “harness” significa el andamiaje del agente: Claude Code, settings, hooks, sandbox y política de logs trabajando juntos. No se trata de confiar más, sino de darle un espacio de trabajo más claro.

Bash(npm run test *) usa un comodín. La documentación actual también reconoce un :* final, como Bash(ls:*), pero solo al final del patrón. En un equipo, Bash(git push *) se lee mejor.

Checklist copiables para el repositorio

La auditoría no debe convertirse en un documento enorme. Pega este bloque en el PR, issue o nota de traspaso.

claude_code_permission_audit:
  date: "2026-06-03"
  repository:
    name: "your-repo"
    branch: "feature/your-task"
    dirty_before_start: "yes/no"
  allowed_today:
    - "Read project files"
    - "Edit MDX and test files"
    - "Run npm run lint"
    - "Run npm run test -- --runInBand"
  ask_before:
    - "Install or update packages"
    - "Change auth, billing, analytics, or deploy config"
    - "Push commits or create releases"
  never_allow:
    - "Print .env, tokens, cookies, or private keys"
    - "Run curl/wget for arbitrary URLs"
    - "Delete git history or force-push"
  proof_required:
    - "git diff reviewed"
    - "test or build command captured"
    - "rollback note written"
  owner_handoff:
    reviewer: "name"
    open_questions:
      - "Which production URL should be checked?"

La línea clave es allowed_today. No es una autorización permanente; es el permiso mínimo para terminar la tarea de hoy con evidencia.

Detectar patrones peligrosos con Node

La revisión manual suele pasar por alto Bash completo o un Bash(npx *) dejado el día anterior. Guarda esto como scripts/audit-claude-permissions.mjs y ejecútalo desde la raíz del repo.

#!/usr/bin/env node
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

const repo = process.cwd();
const settingsFiles = [
  path.join(os.homedir(), ".claude", "settings.json"),
  path.join(repo, ".claude", "settings.json"),
  path.join(repo, ".claude", "settings.local.json"),
].filter((file) => fs.existsSync(file));

const riskyAllowRules = [
  { pattern: /^Bash$/i, severity: "high", reason: "all Bash commands are auto-allowed" },
  { pattern: /^PowerShell$/i, severity: "high", reason: "all PowerShell commands are auto-allowed" },
  { pattern: /^(Edit|Write)$/i, severity: "high", reason: "all file edits are auto-allowed" },
  { pattern: /^WebFetch$/i, severity: "medium", reason: "all web fetches are auto-allowed" },
  {
    pattern: /^Bash\((curl|wget|nc|ncat|ssh|scp|rsync)\b.*\)$/i,
    severity: "high",
    reason: "network or transfer command is auto-allowed",
  },
  {
    pattern: /^Bash\(.*\b(rm\s+-[^\)]*r|git\s+push|npm\s+install|pnpm\s+add|yarn\s+add|npx|docker\s+exec|devbox\s+run|mise\s+exec|terraform\s+apply|kubectl\s+apply)\b.*\)$/i,
    severity: "high",
    reason: "destructive or environment-changing command is auto-allowed",
  },
  {
    pattern: /^PowerShell\(.*\b(Remove-Item|Invoke-WebRequest|Invoke-RestMethod|Start-Process)\b.*\)$/i,
    severity: "high",
    reason: "risky PowerShell command is auto-allowed",
  },
];

const expectedDenyRules = [
  "Read(./.env)",
  "Read(./.env.*)",
  "Read(./secrets/**)",
  "Bash(curl *)",
  "Bash(wget *)",
];

const findings = [];

function add(file, severity, rule, reason) {
  findings.push({ file: path.relative(repo, file) || file, severity, rule, reason });
}

function readJson(file) {
  try {
    return JSON.parse(fs.readFileSync(file, "utf8"));
  } catch (error) {
    add(file, "high", "JSON", `cannot parse settings: ${error.message}`);
    return null;
  }
}

for (const file of settingsFiles) {
  const settings = readJson(file);
  if (!settings) continue;

  const permissions = settings.permissions ?? {};
  const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
  const ask = Array.isArray(permissions.ask) ? permissions.ask : [];
  const deny = Array.isArray(permissions.deny) ? permissions.deny : [];

  if (permissions.defaultMode === "bypassPermissions") {
    add(file, "high", "permissions.defaultMode", "session starts in bypassPermissions");
  }

  if (permissions.disableBypassPermissionsMode !== "disable") {
    add(file, "medium", "permissions.disableBypassPermissionsMode", "bypass mode is not disabled here");
  }

  if (settings.env?.CLAUDE_CODE_SKIP_PROMPT_HISTORY === "1") {
    add(file, "low", "CLAUDE_CODE_SKIP_PROMPT_HISTORY", "prompt history and transcripts are not written");
  }

  if (settings.env?.CLAUDE_CODE_SUBPROCESS_ENV_SCRUB !== "1") {
    add(file, "low", "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB", "subprocess credential scrubbing is not enabled here");
  }

  for (const rule of allow) {
    for (const risky of riskyAllowRules) {
      if (risky.pattern.test(rule)) add(file, risky.severity, rule, risky.reason);
    }
  }

  for (const required of expectedDenyRules) {
    if (!deny.includes(required)) add(file, "low", required, "consider adding this deny rule");
  }

  if (ask.length === 0) {
    add(file, "low", "permissions.ask", "no ask rules are defined");
  }

  for (const rule of [...allow, ...ask, ...deny]) {
    if (/:\*[^)]/.test(rule)) {
      add(file, "medium", rule, "the :* shorthand only behaves as a wildcard at the end of a pattern");
    }
  }
}

if (settingsFiles.length === 0) {
  console.log("No Claude Code settings files found in user or repo scope.");
} else if (findings.length === 0) {
  console.log("No risky Claude Code permission patterns found.");
} else {
  console.table(findings);
}

if (findings.some((finding) => finding.severity === "high")) {
  process.exitCode = 1;
}

En PowerShell:

New-Item -ItemType Directory -Force -Path .\scripts | Out-Null
node .\scripts\audit-claude-permissions.mjs

El script no es un motor de seguridad completo. Crea una entrada de revisión. Aunque Bash(curl *) esté bloqueado, un comando Bash permitido podría ejecutar un script Node que haga red por su cuenta. Para límites más fuertes, combina permisos con sandbox o contenedores.

Cuatro casos de uso concretos

1. Artículos y documentación

MDX, README, traducciones y screenshots suelen ser de menor riesgo. A menudo puedes permitir Read(./src/**), Edit(./site/src/content/**), Bash(npm run lint) y Bash(npm run test *), dejando npm install y git push en ask.

El problema típico está en el CTA. Links de Gumroad, formularios de contacto y URLs de PDF gratis afectan ingresos y leads, así que la comprobación de URL pública debe quedar en la definición de terminado.

2. Actualizaciones de dependencias

Las dependencias modifican package.json, lockfiles, build y a veces la postura de seguridad. Mantén instalaciones en ask y exige motivo, comando de prueba y nota de rollback.

Un buen prompt: “Limita esto a tres candidatos de actualización y muestra riesgo de breaking change, comando de verificación y rollback en una tabla.”

3. Auth, billing y analytics

Login, Stripe, Gumroad, etiquetas de anuncios, destinos de email y webhooks deben pasar por aprobación humana. Las pruebas no bastan. Revisa qué datos se envían, si un fallo puede duplicar cobros y si los logs guardan datos personales.

CLAUDE_CODE_SKIP_PROMPT_HISTORY=1 evita escribir historial de prompts y transcripciones en disco. Puede ayudar en sesiones sensibles y efímeras, pero reduce la trazabilidad del equipo.

4. Traspaso de equipo

Al pasar el trabajo a otra persona, deja el rastro de decisiones. La nota debe decir qué se permitió, qué quedó bloqueado, qué evidencia existe y qué falta revisar.

Handoff note:
- Allowed today: Edit content files, run lint, run unit tests.
- Asked before: package changes, deploy, payment links, analytics tags.
- Denied: .env reads, arbitrary curl/wget, recursive delete.
- Evidence: npm run lint passed, git diff reviewed.
- Remaining risk: production URL has not been checked after deploy.

Errores comunes

Primero, autoaprobar demasiado. Bash, PowerShell, Edit o WebFetch completos en allow parecen prácticos, pero dejan demasiada responsabilidad a la memoria y al review.

Segundo, usar patrones Bash como filtro preciso de URL. Bash(curl https://github.com *) es frágil: opciones, redirecciones, variables y espacios pueden cambiar. Bloquea curl y wget, y usa WebFetch(domain:example.com) o un hook.

Tercero, confiar de más en wrappers. La documentación describe tratamiento integrado para timeout o time, pero runners como npx, docker exec, devbox run y mise exec requieren más cuidado.

Cuarto, creer que Read/Edit deny es aislamiento de sistema operativo. Sirve para herramientas integradas y comandos reconocidos, no para cualquier script que abra archivos internamente.

Prompt de equipo para el primer turno

Empieza con auditoría, no con implementación.

Before changing files, audit Claude Code permissions for this repository.
Return:
1. allow rules that are safe for today's task
2. ask rules that should stay behind human approval
3. deny rules that protect secrets, deploys, and destructive commands
4. environment variables that affect logs or subprocess secrets
5. the smallest task you can complete with proof and rollback notes
Do not edit files until the audit is summarized.

El prompt no impone seguridad por sí solo. Eso lo hacen settings y la UI de permisos. Su valor es hacer visible el límite antes del primer cambio.

Proteger la ruta de ingresos

En un sitio que lleva a un PDF gratis, productos Gumroad o consultoría, la auditoría protege ingresos además de seguridad. Si todavía estás fijando el flujo básico, empieza con la cheatsheet gratuita. Para ordenar CLAUDE.md, permisos, hooks y MCP en equipo, el Setup Guide es más directo. Para planificar una adopción real, usa la página de training y consultoría.

Después de probarlo en trabajo real, la rutina con más valor fue revisar git status, /permissions, .claude/settings.local.json y el script antes de pedir cambios. Mantener links de Gumroad y deploys en ask ralentiza un poco, pero acelera el review porque cada cambio que afecta ingresos tiene una razón de aprobación visible.

#claude-code #permissions #security #setup #workflow #claude-md
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.