Análisis multicapa
Objetivo
StaticCodeAudit aplica un análisis multicapa: cada archivo se examina mediante 3 estrategias independientes. Los resultados se cruzan para producir hallazgos de alta confianza, acompañados de una cadena de evidencias verificable.
El problema del análisis monocapa
Un análisis monocapa utiliza un único modo (por ejemplo, una sola búsqueda por patrón) por regla:
Regla sql_injection_fstring
-> Modo regex: busca ".execute(f""
-> ¿Coincide? -> Hallazgo (confidence 80, fija)
-> ¿No coincide? -> Nada
Limitaciones:
- Si el patrón es demasiado estrecho, se pierde la vulnerabilidad (falso negativo)
- Si el patrón es demasiado amplio, se detecta ruido (falso positivo)
- Sin prueba del flujo de datos (usted ve "SQL injection" pero no sabe POR QUÉ)
- La confidence es fija, no se basa en evidencias reales
La arquitectura multicapa
Para cada archivo, 3 capas se ejecutan en paralelo:
+-----------------+
| Archivo fuente |
+--------+--------+
|
+--------------+--------------+
| | |
v v v
+------------+ +------------+ +------------+
| Capa 1 | | Capa 2 | | Capa 3 |
| Pattern | | Taint | | Context |
| matching | | analysis | | analysis |
+------+-----+ +------+-----+ +------+-----+
| | |
v v v
+-----------------------------------------+
| Fusionador de evidencias |
| |
| Cruza los resultados de las 3 capas. |
| Calcula la confidence real. |
| Construye la cadena de evidencias. |
+----------------+------------------------+
|
v
+---------------+
| Hallazgo |
| + evidencias |
| + confidence |
+---------------+
Capa 1: Pattern matching
Lo que hace
Busca patrones sintácticos en el código (texto o regex). Capa rápida, con amplia cobertura.
Lo que produce
{
"layer": "pattern",
"rule_key": "sql_injection_fstring",
"file": "src/db.py",
"line": 42,
"code": "cursor.execute(f\"SELECT * FROM users WHERE id = {user_id}\")",
"evidence": "Pattern '.execute(f\"' coincide en la línea 42"
}
Confidence aislada: 65 %
Un pattern match aislado es una señal, no una prueba. El patrón puede coincidir con código seguro (por ejemplo, una cadena de documentación o un comentario).
Capa 2: Taint analysis
Lo que hace
Rastrea el flujo de datos desde una source no confiable (entrada HTTP, CLI, archivo) hasta un sink peligroso (execute, open, system).
Lo que produce
{
"layer": "taint",
"rule_key": "taint_sqli",
"file": "src/db.py",
"flow": {
"source": {
"line": 12,
"code": "user_id = request.args.get('id')",
"kind": "http"
},
"propagation": [
{"line": 15, "code": "user_id = int(user_id) if safe else user_id"},
{"line": 38, "code": "query = f\"SELECT * FROM users WHERE id = {user_id}\""}
],
"sink": {
"line": 42,
"code": "cursor.execute(query)",
"type": "sql_execute"
},
"sanitizers_found": false
},
"evidence": "Datos HTTP (request.args) propagados sin sanitizer hasta cursor.execute()"
}
Confidence aislada: 85 %
Un taint flow es una prueba sólida: muestra el camino completo. Puede ser un falso positivo si el sanitizer está en una función llamada indirectamente (fuera del archivo).
Capa 3: Context analysis
Lo que hace
Analiza el contexto del archivo para reforzar o refutar los hallazgos de las capas 1 y 2. Responde a preguntas como:
- ¿El archivo utiliza un ORM (SQLAlchemy, Django ORM)? Si es así, una inyección SQL es menos probable.
- ¿El archivo importa un framework de sanitización?
- ¿El archivo es un test, un mock, una fixture? Si es así, ignorar.
- ¿El archivo gestiona peticiones HTTP (rutas, handlers)?
- ¿El archivo contiene validaciones de entrada (esquemas, validadores)?
Lo que produce
{
"layer": "context",
"file": "src/db.py",
"signals": {
"is_test_file": false,
"has_orm": false,
"has_raw_sql": true,
"has_http_handler": true,
"has_input_validation": false,
"has_sanitizer": false,
"framework": "flask"
},
"evidence": "Archivo Flask con SQL en bruto, sin ORM, sin validación de entrada"
}
Fusionador de evidencias
Cálculo de la confidence
La confidence final no es un promedio: es un cálculo basado en evidencias independientes.
Confidence = base + bonus_taint + bonus_context - penalizaciones
Casos concretos:
Pattern aislado, sin contexto:
65 % (señal débil, puede ser un falso positivo)
Pattern + contexto que confirma (raw SQL, sin ORM, handler HTTP):
65 % + 15 % = 80 %
Pattern + taint flow (source -> sink confirmado):
65 % + 25 % = 90 %
Pattern + taint + contexto (triple confirmación):
65 % + 25 % + 10 % = 95 % (casi seguro)
Pattern + contexto que REFUTA (ORM presente, archivo de test):
65 % - 30 % = 35 % (probablemente falso positivo -> eliminado del informe)
Reglas de fusión
1. Si la capa de contexto detecta un archivo de test -> ELIMINAR el hallazgo
(los archivos de test contienen código vulnerable de forma intencionada)
2. Si la capa taint confirma el flujo -> AUMENTAR la confidence
y AÑADIR la cadena de evidencias al hallazgo
3. Si la capa de contexto refuta (ORM presente, sanitizer encontrado) ->
DISMINUIR la confidence. Si < 40 % -> ELIMINAR el hallazgo
4. Si dos reglas diferentes detectan la misma vulnerabilidad en el mismo lugar ->
FUSIONAR en un único hallazgo con la confidence más alta
5. Si el taint flow atraviesa varios archivos -> AÑADIR la etiqueta
"cross-file" y AUMENTAR la confidence en un 5 %
Cadena de evidencias en el informe
Lo que vería sin análisis multicapa
[HIGH] SQL Injection (f-string) — src/db.py:42
Risk: An attacker can inject malicious SQL...
Solution: Use parameterized queries.
Sin contexto. Le tocaría a usted averiguar por qué es un problema.
Lo que ve con el análisis multicapa
[HIGH] SQL Injection (f-string) — src/db.py:42
Confidence: 95 % (pattern + taint + context)
Evidencias:
1. Source (línea 12):
user_id = request.args.get('id')
-> Datos HTTP no confiables (kind: http)
2. Propagación (línea 38):
query = f"SELECT * FROM users WHERE id = {user_id}"
-> Variable inyectada en una consulta SQL mediante f-string
3. Sink (línea 42):
cursor.execute(query)
-> Ejecución de la consulta sin parametrización
4. Contexto:
- Archivo Flask con handler HTTP (@app.route)
- SQL en bruto (sin ORM)
- Sin validación de entrada detectada
- Sin sanitizer detectado
Solution: Use parameterized queries.
Playbook: 3 pasos (diagnóstico -> corrección -> verificación)
References: CWE-89, OWASP A03:2021, ISO A.8.28
Usted puede verificar cada paso de la prueba. Si piensa que se trata de un falso positivo, sabe exactamente qué comprobar.
Impacto sobre los falsos positivos
La capa de contexto reduce drásticamente los falsos positivos:
| Situación | Sin multicapa | Con multicapa |
|---|---|---|
| Pattern en un archivo de test | Hallazgo (FP) | Eliminado (is_test_file) |
| Pattern en un archivo con ORM | Hallazgo (FP) | Confidence 35 % -> eliminado |
| Pattern + taint confirmado | Hallazgo (80 %) | Hallazgo (95 %) |
| Pattern en un comentario/cadena | Hallazgo (FP) | Confidence 35 % -> eliminado |
Estimación de calidad
| Métrica | Monocapa | Multicapa |
|---|---|---|
| Precisión | 81 % | 93 %+ (el contexto filtra los FP) |
| Recall | 99 % | 99 % (misma cobertura) |
| Score F1 | 89 % | 96 %+ |
Lo que el análisis multicapa NO resuelve
El análisis multicapa mejora la calidad de las detecciones existentes. No puede detectar:
- Los bugs lógicos puros (por ejemplo, un IDOR donde el problema es un WHERE ausente en una consulta, no un patrón sintáctico)
- Las vulnerabilidades en las dependencias (cubiertas por pip-audit / npm audit, no por SAST)
- Las vulnerabilidades en código compilado u ofuscado
Para estos casos, las soluciones complementarias son:
- IDOR: regla específica "CRUD sin ownership check"
- Dependencias: opciones
--with-deps(escaneo CVE) y--sbom - Código compilado: fuera del scope SAST