Skip to main content

Command Palette

Search for a command to run...

Deepsec (Vercel Security Harness): guía práctica de configuración, troubleshooting y triaje de findings

Published
17 min read

Guía práctica de deepsec

Guía completa para usar deepsec — el security harness de Vercel — entendiendo qué hace cada comando, cómo configurarlo correctamente para usar tu suscripción de Claude Code (en vez del AI Gateway de Vercel), y cómo triar los findings que produce.

Contexto: deepsec es una herramienta de análisis de seguridad que usa coding agents (Claude o Codex) para investigar vulnerabilidades en tu codebase. Usa Opus 4.7 a thinking máximo por defecto, lo que la hace cara — pero también muy capaz.

Esta guía cubre deepsec ≥ 2.0.2. Versiones anteriores tenían un bug crítico de billing que se resolvió en PR #43. Si estás en versión previa, actualiza con pnpm update deepsec antes de seguir.


TL;DR del flujo correcto

# 1. Setup limpio
claude login                                   # tu suscripción Pro/Max

# 2. Init y bootstrap
cd ~/proyectos/mi-app
npx deepsec init
cd .deepsec
pnpm install

# 3. Pipeline (en orden)
pnpm deepsec scan                              # gratis, sin IA
pnpm deepsec process --project-id mi-app       # CARO, usa IA
pnpm deepsec revalidate                        # CARO, usa IA
pnpm deepsec export --format md-dir --out ./findings   # gratis, sin IA

📝 Requiere deepsec ≥ 2.0.2. Versiones anteriores tenían un bug que ruteaba el tráfico al Vercel AI Gateway aunque el usuario no lo hubiera elegido. Si estás en una versión previa, actualiza con pnpm update deepsec.


Contexto histórico: cambio de comportamiento en v2.0.2

✅ Si tu versión de deepsec es ≥ 2.0.2, puedes saltarte esta sección. Se documenta solo para usuarios que vengan de versiones previas y para explicar por qué algunos repos pueden tener residuos como un .vercel/ stub dentro de .deepsec/.

En versiones pre-2.0.2, la lógica de autenticación local tenía un comportamiento poco intuitivo. El README indicaba que deepsec usaría las suscripciones locales de claude o codex automáticamente, pero en la práctica el orden de prioridad hacía que la presencia del Vercel CLI logueado (común en proyectos Next.js) dirigiera todo el tráfico al AI Gateway de Vercel, cobrando contra la cuenta del usuario sin opt-in explícito.

El flujo interno era aproximadamente:

  1. Deepsec llamaba a getVercelOidcToken() durante preflight

  2. Esa función caminaba hacia arriba desde .deepsec/ buscando un .vercel/project.json

  3. Si lo encontraba, leía la auth del CLI de Vercel

  4. Solicitaba un OIDC token fresco

  5. Lo expandía a ANTHROPIC_AUTH_TOKEN + ANTHROPIC_BASE_URL=https://ai-gateway.vercel.sh

Resultado típico para usuarios sin crédito en Vercel AI Gateway:

Agent SDK error: Claude Code returned an error result:
API Error: 402 Insufficient funds. Please add credits to your account...
Visit https://vercel.com/d?to=...

Adicionalmente, intentar evitar el Gateway poniendo ANTHROPIC_BASE_URL=https://api.anthropic.com sin token producía un modo de falla silenciosa: cada archivo terminaba en ~5 segundos con 0 tokens, 0 turns, 0 findings, marcado como analyzed. La herramienta reportaba el escaneo como exitoso sin haber llamado a la IA.

Ambos comportamientos cambiaron en PR #43 ("Use the vercel OIDC token for the gateway if no primary API token is present"), mergeado el 5 de mayo de 2026 y publicado en deepsec 2.0.2.

A partir de v2.0.2, el OIDC token solo se usa como fallback explícito cuando ninguna otra credencial está presente, y la suscripción local de claude CLI tiene precedencia. Esto significa que puedes tener Vercel CLI logueado y deepsec correctamente usará tu suscripción de Claude Code sin desviar tráfico al Gateway.


Cómo configurar para usar tu suscripción de Claude Code

Paso 1: Setup limpio del entorno

# Verifica que no haya variables de entorno conflictivas
env | grep -iE "anthropic|openai|gateway"
# No debería retornar nada relevante

# Verifica que claude CLI esté autenticado con tu suscripción
claude --version
claude --print "test"
# Debería responder normalmente

Paso 2: Borrar instalaciones previas si las hay

Si ya intentaste correr deepsec antes y falló, hay residuos que pueden contaminar el setup:

cd tu-proyecto
rm -rf .deepsec

Paso 3: Init desde cero

npx deepsec init
cd .deepsec
pnpm install

No hagas cp .env.example .env ni edites .env.local. El init te dirá:

# Set AI_GATEWAY_API_KEY in .env.local (or skip if claude/codex CLI is logged in)

Saltea ese paso. Tu claude CLI logueado debería ser suficiente.

Paso 4: Verificar que está usando tu suscripción

Antes de correr el pipeline completo, haz una prueba pequeña:

pnpm deepsec process --project-id mi-app --filter apps/api/src/middleware --limit 2

Espera ver algo como:

Investigating 2 file(s) with Claude Agent SDK (claude-opus-4-7)
Turn 1 (15s, 4 tool calls)
Turn 2 (22s, 3 tool calls)

Lo que NO quieres ver:

  • Turn 1 (4s, 0 tool calls) repetido (silent failure — indicaría versión antigua)

  • 402 Insufficient funds con link a Vercel (Gateway activo — indicaría versión antigua)

Si ves uno de esos dos, probablemente estás en una versión vieja de deepsec. Actualiza con pnpm update deepsec y verifica con cat node_modules/deepsec/package.json | grep version.


Pipeline de comandos: qué hace cada uno

scan — Análisis estático con regex

pnpm deepsec scan
Aspecto Detalle
¿Usa IA? ❌ No
Costo Gratis
Tiempo ~15s para 2k archivos
Output Archivos marcados como pending con candidates

Qué hace: Recorre tu codebase con ~110 patrones regex predefinidos y marca archivos sospechosos. No produce findings finales — solo "candidatos" que merecen investigación con IA.

Qué esperar: Lista de archivos con candidatos (ej: "este archivo tiene template literals en HTML, posible XSS"). Sin veredicto, sin severidad.


process — Investigación con IA

pnpm deepsec process --project-id mi-app
Aspecto Detalle
¿Usa IA? ✅ Sí (Opus 4.7 a thinking máximo)
Costo $$$ — quemas tokens de tu suscripción
Tiempo Minutos a horas según tamaño
Output Archivos con findings[] y status analyzed

Qué hace: Por cada archivo candidato del scan, lanza un agente de IA que lee el código, traza el flujo de datos, busca mitigaciones, considera el contexto y produce findings con severidad (CRITICAL, HIGH, MEDIUM, LOW, BUG).

Qué esperar: Lista de hallazgos por archivo, cada uno con:

  • Categoría de vuln (xss, sql-injection, auth-bypass, etc.)

  • Severidad

  • Razonamiento

  • Líneas afectadas

Flags útiles:

# Solo procesar archivos en una ruta específica (útil para testing)
pnpm deepsec process --project-id mi-app --filter apps/api/src

# Procesar máximo N archivos
pnpm deepsec process --project-id mi-app --limit 10

# Re-analizar todo desde cero (BORRA findings previos, usar con cuidado)
pnpm deepsec process --project-id mi-app --reinvestigate

# Cambiar concurrencia (default suele estar bien)
pnpm deepsec process --project-id mi-app --concurrency 5 --batch-size 5

Idempotencia: process sin --reinvestigate solo procesa archivos en estado pending. Si se interrumpe (rate limit, sesión muerta), simplemente vuelves a correr el comando y reanuda donde quedó.


revalidate — Reduce falsos positivos

pnpm deepsec revalidate --project-id mi-app
Aspecto Detalle
¿Usa IA? ✅ Sí (mismo modelo, similar costo a process)
Costo $$$ — comparable a process
Tiempo Similar a process
Output Findings etiquetados con veredicto

Qué hace: Por cada finding generado, lanza al agente a re-evaluarlo: ¿es realmente explotable? ¿Está mitigado upstream? ¿Se arregló en commits recientes? Asigna un veredicto:

  • TP (True Positive) — bug real, vale la pena arreglar

  • FP (False Positive) — el agente del process se equivocó

  • Fixed — ya fue arreglado en algún commit

  • Uncertain — no se puede determinar sin más contexto

Qué esperar:

Revalidation complete. Run: 20260506180136-...
TP: 116  FP: 4  Fixed: 0  Uncertain: 0

Flags útiles:

# Re-revalidar findings específicos (por path)
pnpm deepsec revalidate --filter apps/api/src/routes/auth

# Forzar re-revalidación de findings ya revalidados
pnpm deepsec revalidate --force

Idempotencia: revalidate sin --force solo procesa findings que aún no tienen veredicto. Si terminó con "120/122 revalidated", correrlo de nuevo procesa solo los 2 faltantes.


export — Exportar findings a archivos legibles

pnpm deepsec export --format md-dir --out ./findings
Aspecto Detalle
¿Usa IA? ❌ No
Costo Gratis
Tiempo Segundos
Output Carpeta con un .md por finding

Qué hace: Lee los findings + veredictos del data/<id>/files/ y los exporta a un formato leíble para humanos. Sin IA, sin costo.

Formatos disponibles:

  • md-dir — un Markdown por finding (recomendado para revisión humana)

  • json — un solo JSON con todos los findings (para pipeline / herramientas)


status — Ver estado del proyecto

pnpm deepsec status --project-id mi-app
Aspecto Detalle
¿Usa IA? ❌ No
Costo Gratis

Muestra:

  • Cuántos archivos hay en cada status (analyzed, pending, processing)

  • Conteo de findings por severidad

  • Resultados de revalidation

  • Runs recientes con su costo equivalente

Output esperado al terminar pipeline:

Project: mi-app
  Files tracked: 650
  Status
    analyzed:   650    ← 100%
    pending:    0
  Findings
    CRITICAL: 1  |  HIGH: 11  |  MEDIUM: 41  |  BUG: 64
    Revalidated: 122/122  TP: 116  FP: 4  Fixed: 0  Uncertain: 0

Importante: Los costos en USD que muestra (\(24.10, \)10.93) son valor equivalente si pagaras la API directa, no lo que te cobró Anthropic. Si usas tu suscripción, todo sale de tu cuota — $0 reales.


Verificar que el análisis fue real (no zombi del bug del Gateway)

Después de process, especialmente si tuviste problemas previos con el Gateway, verifica que los archivos hayan sido analizados de verdad:

# Cuenta archivos "analyzed" pero con 0 tokens (zombis del bug)
find data/<project-id>/files -name "*.json" -type f -exec python3 -c "
import json, sys
try:
    d = json.load(open(sys.argv[1]))
    h = d.get('analysisHistory', [])
    if h and d.get('status') == 'analyzed':
        last = h[-1]
        if last.get('usage', {}).get('inputTokens', 0) == 0:
            print(sys.argv[1])
except: pass
" {} \; 2>/dev/null | wc -l

Si este conteo es > 0, esos archivos fueron marcados como analizados sin haber pasado por la IA. Re-procesa el proyecto:

pnpm deepsec process --project-id mi-app --reinvestigate

Costo estimado

Para una codebase mediana (~650 archivos):

Stage Tokens (aprox) $ equivalente
scan 0 $0
process 300k - 500k $20 - $30
revalidate 100k - 200k $8 - $15
export 0 $0
Total ~500k - 700k ~$30 - $45

Si usas tu suscripción de Claude Max (\(100/mes), este pipeline cabe cómodamente y te deja margen para uso normal el resto del mes. Plan Pro (\)20/mes) probablemente se sature en mitad de un proyecto grande.


Triaje de findings: qué hacer después de exportar

Con 116 TPs (true positives) confirmados, necesitas un plan de revisión sistemático:

Prioriza por severidad

  1. CRITICAL → revísalo hoy, no espera

  2. HIGH → esta semana

  3. MEDIUM → próximas 2 semanas

  4. BUG / HIGH_BUG → backlog de calidad de código (no bloquean seguridad)

Agrupa por categoría, no por archivo

Muchos findings son el mismo bug repetido en múltiples archivos. Agrupar por vulnSlug (ej: todos los xss juntos) te permite arreglar 5 findings con un solo cambio (ej: agregar sanitización en el helper compartido).

Filtra por superficie de ataque real

Un finding en apps/api/src/routes/public.ts (recibe input de internet) pesa muchísimo más que uno en apps/web/src/components/InternalDashboard.tsx. Pregunta crítica para cada finding: ¿el input que llega aquí viene de un atacante?

Para cada TP, tres preguntas

  1. ¿Es realmente explotable, no teórico?

  2. ¿Hay mitigación upstream que el agente no vio? (validación en middleware, rate limiting, auth)

  3. ¿El arreglo introduce regresiones? (¿hay tests que cubran el camino?)


Errores comunes y soluciones

Causa: Estás en una versión vieja de deepsec (pre-2.0.2) donde el OIDC token fallback estaba activo por defecto.

Solución: Actualiza deepsec:

pnpm update deepsec
cat node_modules/deepsec/package.json | grep version
# Debe ser >= 2.0.2

"0 tool calls, 0 tokens" en cada batch

Causa: El SDK no tiene credenciales válidas y está fallando en silencio. Suele pasar en versiones pre-2.0.2 cuando se intentaba evitar el Gateway con ANTHROPIC_BASE_URL=https://api.anthropic.com sin token.

Solución:

  1. Actualiza a deepsec ≥ 2.0.2 (pnpm update deepsec)

  2. Verifica claude --print "test" responde normalmente

  3. Asegúrate de NO tener ANTHROPIC_BASE_URL ni ANTHROPIC_AUTH_TOKEN en .env.local ni en el shell (a menos que sí quieras usar el Gateway o una API key directa)

Rate limit de Claude durante process

Causa: Tu plan se topó con el límite de uso (5h Pro, ventanas más largas Max).

Solución: Espera a que se resetee la ventana y vuelve a correr pnpm deepsec process. Es idempotente y reanuda donde quedó.

Archivos quedan en processing para siempre

Causa: Run anterior murió sin liberar locks.

Solución:

# Limpia los runs zombi
rm -rf data/<project-id>/runs/*
# Re-corre process — los archivos en "processing" se re-procesarán
pnpm deepsec process --project-id mi-app

Qué commitear y qué no

.deepsec/ contiene una mezcla de archivos de configuración (que sí van a git) y output de runs (que NO debe ir a git por seguridad). El init ya genera un .gitignore razonable, pero hay que verificar que cubra los exports también.

Lo que SÍ se commitea

Estos archivos son la "receta" del escaneo y permiten que cualquier persona del equipo replique el setup:

  • deepsec.config.ts — config del proyecto (matchers, paths, plugins)

  • package.json + pnpm-lock.yaml — para reproducibilidad

  • pnpm-workspace.yaml

  • AGENTS.md — instrucciones para coding agents

  • README.md

  • .gitignore

  • data/<id>/INFO.md — contexto del proyecto inyectado a los prompts (¡el archivo más valioso!)

  • data/<id>/SETUP.md — instrucciones por proyecto

  • data/<id>/config.json (si existe) — priorityPaths, promptAppend, ignorePaths

Lo que NO se commitea

# .deepsec/.gitignore
node_modules/
.env*.local
.vercel/

# Scan output — regenerated by `deepsec scan` / `process`. INFO.md
# and SETUP.md (manually edited) stay tracked.
data/*/files/
data/*/runs/
data/*/reports/
data/*/project.json

# Auto-detected metadata (regenerated each run, contains absolute paths)
data/*/tech.json

# Exported findings (regenerated by `deepsec export`)
findings/
exports/

El init genera las primeras secciones automáticamente, pero NO incluye tech.json, findings/ ni exports/. Agrégalos tú:

cat >> .deepsec/.gitignore << 'EOF'

# Auto-detected metadata (regenerated each run, contains absolute paths)
data/*/tech.json

# Exported findings (regenerated by `deepsec export`)
findings/
exports/
EOF

💡 Por qué ignorar tech.json: Contiene el rootPath absoluto de la máquina donde se corrió el scan (ej: /Users/jdoe/proyectos/mi-app), lo cual revela el username y estructura local del desarrollador. Además se regenera en cada run, así que versionarlo solo crea ruido en diffs.

Por qué los findings y exports NO van a git

Los archivos en data/<id>/files/ y la carpeta de export contienen:

  • Líneas exactas de código vulnerable con snippets

  • Razonamiento detallado del agente sobre cómo explotar cada vuln

  • Veredictos de revalidate

Si tu repo es público, eso es básicamente una guía paso a paso para atacar tu app. Incluso en repos privados, considera que el historial de git es para siempre — un finding puede estar fixeado hoy pero el commit sigue mostrando cómo era el bug.

Verifica que no haya residuos en tracking

Si ya commiteaste algo antes de configurar el gitignore correctamente:

git ls-files .deepsec/ | grep -E "findings/|/files/|/runs/|/reports/"

Si retorna algo, sácalos del trackeo:

git rm -r --cached .deepsec/findings/ 2>/dev/null
git rm -r --cached .deepsec/data/*/files/ 2>/dev/null
git rm -r --cached .deepsec/data/*/runs/ 2>/dev/null
git rm -r --cached .deepsec/data/*/reports/ 2>/dev/null
git commit -m "chore: untrack deepsec output files"

⚠️ git rm --cached solo los saca del trackeo futuro, no los borra del historial. Si los findings exponen vulns aún sin arreglar y el repo tiene muchos colaboradores, considera limpiar el historial con git filter-repo o BFG.

Dónde viven los findings entonces

Los findings exportados son locales y temporales. Tres patrones comunes para gestionarlos:

  1. Locales sin persistir — para escaneos puntuales, cada quien revisa los suyos

  2. Repo privado separadomi-proyecto-security/ con acceso restringido

  3. Sistema de tickets (recomendado) — los findings TP relevantes se convierten en issues de Linear/Jira/GitHub con responsable y deadline; los FPs se descartan

Para equipos serios, la opción 3 es la estándar: deepsec genera la lista, humanos triamos, y los hallazgos importantes pasan al flujo normal de trabajo.


Recursos


Buenas prácticas para equipos

Recomendaciones generales basadas en la experiencia de implementar deepsec en un proyecto real.

Designar un responsable

Aunque cualquier persona del equipo puede correr deepsec siguiendo esta guía (los archivos en .deepsec/ están versionados y el setup es reproducible con pnpm install), conviene designar a alguien como responsable del proceso para evitar duplicaciones y mantener consistencia en el triaje.

Cuándo escanear

Casos típicos donde tiene sentido correr un escaneo completo:

  • Después de mergear una feature grande que toca auth, manejo de inputs, o flujos de pago/datos sensibles

  • Antes de un release a producción que incluya cambios significativos

  • Cuando se reporta una vuln en alguna de las dependencias críticas del stack

  • Periódicamente (cada 3-6 meses) como higiene general

  • Cuando se decida re-evaluar findings previos después de aplicar fixes

Para cambios pequeños o aislados, basta con escanear el directorio modificado:

pnpm deepsec process --project-id mi-app --filter apps/api/src/routes/<modulo>

Gestión de findings

Recomendamos integrar los findings críticos y altos al sistema de tickets que use el equipo (Linear, Jira, GitHub Issues privados, sistema interno, etc). Después de cada escaneo, el responsable triagea los TPs y crea entradas para los que requieren acción, asignándolos a los devs correspondientes.

Los findings exportados (./findings/) deben mantenerse solo localmente en la máquina de quien escaneó — no se commitean al repo (ver sección "Qué commitear y qué no").

Costos esperados

Para una codebase mediana (~650 archivos), un pipeline completo (process + revalidate) consume entre $30-50 de equivalente API y toma 15-30 minutos de wall-clock.

Opciones para cubrir el costo:

  • Suscripción personal de Claude Max (\(100/mes) — viable para escaneos ad-hoc del equipo. \)0 reales si cabe en la cuota.

  • AI Gateway de Vercel con AI_GATEWAY_API_KEY — recomendado para CI/CD o escaneos frecuentes. Permite cap de gasto y atribución a la cuenta corporativa.

  • API key directa de Anthropic — si el equipo ya paga API directa para otros propósitos.

Política de escalación

Para findings de severidad CRITICAL o HIGH, conviene tener un canal directo de escalación al responsable de seguridad antes de cualquier release. Los MEDIUM y BUG pueden seguir el flujo normal del sistema de tickets.


Notas finales

  • Si encuentras secretos en findings, rótalos inmediatamente. La presencia de un secreto en un finding implica que estaba en el código fuente.

  • El reporte de costos en USD que muestra deepsec es siempre el equivalente API, no lo cargado a tu cuenta. Si usas suscripción, los $ son referenciales.

  • --reinvestigate borra trabajo previo. Solo úsalo cuando realmente quieras re-empezar.


Esta guía cubre deepsec ≥ 2.0.2 — última actualización: mayo 2026.