Si ejecutas ESLint en un archivo JavaScript con una vulnerabilidad de inyección SQL, la prueba se completará sin problemas. Si ejecutas CodeQL en el mismo código fuente, detectará la inyección con el archivo, la línea y la ruta de flujo de datos exactos. Ambas son herramientas de análisis estático. Ambas examinan el mismo código fuente sin ejecutarlo. La diferencia en sus hallazgos no radica en la configuración de las reglas ni en la compatibilidad con el lenguaje, sino en el alcance de su análisis. ESLint examina una función a la vez e informa sobre lo que encuentra dentro de ese límite. CodeQL sigue los datos a través de las llamadas a funciones, los módulos y el grafo de llamadas completo hasta que detecta algo peligroso.
Esa distinción, entre análisis intraprocedimental e interprocedimental, es el concepto técnico más importante para comprender por qué las distintas herramientas de análisis estático detectan problemas diferentes. Explica por qué un linter puede declarar un código fuente limpio, mientras que una herramienta más sofisticada encuentra vulnerabilidades de seguridad críticas en el mismo código. Explica por qué las herramientas SAST de seguridad son costosas de ejecutar. Explica por qué la tasa de falsos negativos de las herramientas "rápidas" es estructuralmente mayor que la de las "lentas". Y es el concepto que los equipos empresariales deben comprender antes de evaluar cualquier conjunto de herramientas de análisis estático.
Vea el gráfico completo de llamadas, no solo un archivo.
SMART TS XL Realiza un seguimiento del flujo de datos a través de COBOL, Java, Python y todos los lenguajes que utilice en sus productos.
MÁS INFORMACIÓN¿Qué es el análisis intraprocedimental?
El análisis intraprocedimental examina lo que sucede dentro de una función, método o procedimiento, sin considerar lo que ocurre cuando dicha función llama a otras funciones o es llamada por ellas. El límite del análisis es el límite de la función. Cuando una herramienta intraprocedimental llega a una llamada a una función, se detiene, no sigue la llamada hasta la función llamada y desconoce qué hace esta con los datos que recibe.
Los ejemplos canónicos de análisis intraprocedimental son el análisis estático de código y el flujo de datos básico dentro de una sola función:
pitón
def process_input(user_data):
# Intraprocedural analysis sees: user_data used here
query = "SELECT * FROM users WHERE id = " + user_data # flagged: string concat in SQL
return query
Una regla simple que detecta la concatenación de cadenas en consultas SQL funciona intraprocedimentalmente porque el patrón peligroso es visible dentro de una sola función. Pero cambie ligeramente la estructura del código:
pitón
def get_user_id(request):
return request.args.get("id") # untrusted input
def build_query(user_id):
return "SELECT * FROM users WHERE id = " + user_id # looks safe in isolation
def handle_request(request):
uid = get_user_id(request)
query = build_query(uid) # vulnerability spans three functions
db.execute(query)
Ahora la fuente de datos contaminados (request.args.get("id")) tiene una función diferente a la del sumidero peligroso (db.execute(query)). Ninguna función por sí sola contiene información suficiente para que una herramienta intraprocedimental detecte la vulnerabilidad. get_user_id Parece estar bien, simplemente lee desde una solicitud. build_query Parece correcto, toma un parámetro y construye una cadena. handle_request Aparentemente funciona correctamente: llama a funciones y ejecuta una consulta. La inyección SQL es real, explotable e invisible para cualquier herramienta que no traspase los límites de las funciones.
¿Para qué sirve el análisis intraprocedimental?
Las herramientas intraprocedimentales son rápidas. Muy rápidas. Dado que analizan cada función de forma independiente, su escalabilidad es lineal con el tamaño del código fuente; analizar un código fuente de un millón de líneas cuesta proporcionalmente lo mismo que analizar uno de diez mil líneas. Esta velocidad las hace ideales para:
- Ganchos de pre-confirmación que corren en menos de un segundo
- Comentarios en línea del IDE mientras un desarrollador escribe
- Puertas de calidad de CI/CD en cada solicitud de extracción
- Aplicación del estilo y las convenciones (nomenclatura, formato, métricas de complejidad)
- Uso indebido de variables locales (variables no utilizadas, referencias no definidas, errores de tipo evidentes)
- Complejidad ciclomática y duración del método, métricas puramente intrafuncionales
Las herramientas de esta categoría incluyen ESLint, Pylint, Clippy (Rust), golangci-lint, Checkstyle y la mayoría de los analizadores de código. Son esenciales y valiosas para el tipo de problemas que pueden detectar. Su limitación es estructural, no una deficiencia de calidad.
¿Qué es el análisis interprocedimental?
El análisis interprocedimental (de programa completo) suele ser el más potente y el que consume más recursos. Este análisis amplía los grafos de flujo de control con aristas de llamada y retorno de funciones procedentes de un grafo de llamadas. El análisis puede evitar la repetición de las mismas funciones mediante la creación de resúmenes de funciones.
Mientras que el análisis intraprocedimental se detiene en los límites de las funciones, el análisis interprocedimental los traspasa. Construye un grafo de llamadas que representa qué funciones llaman a otras, y luego rastrea el flujo de datos, el flujo de control o la contaminación a través de los bordes de ese grafo. Cuando una herramienta interprocedimental encuentra una llamada a una función, la sigue hasta la función llamada, o bien utiliza un resumen precalculado del comportamiento de la función llamada, y continúa el análisis en el otro extremo.
Las tres funciones de inyección SQL anteriores son un ejemplo sencillo. El análisis interprocedimental real maneja:
- flujo de datos de múltiples saltos: entrada no confiable que pasa por cinco funciones antes de llegar a un destino peligroso
- Corrupción del valor de retorno: una función devuelve datos contaminados; quienes la llaman utilizan ese valor de retorno contaminado en operaciones no seguras.
- contaminación sensible al campo: datos no confiables almacenados en un campo de objeto, recuperados posteriormente en una función diferente
- Propagación condicional: la contaminación fluye solo a lo largo de ciertas rutas de ejecución, dependiendo de las condiciones de tiempo de ejecución que el análisis debe modelar estáticamente.
Java
// Interprocedural vulnerability: three-hop taint flow
public class UserController {
public String handleRequest(HttpServletRequest request) {
String userId = request.getParameter("id"); // source: untrusted input
User user = userService.getUser(userId); // taint flows through call
return generateReport(user); // taint reaches sink
}
}
public class UserService {
public User getUser(String id) {
// id is tainted -- passed to repository
return userRepository.findByRawId(id); // taint propagates
}
}
public class UserRepository {
public User findByRawId(String id) {
String query = "SELECT * FROM users WHERE id = " + id; // sink: SQL injection
return db.execute(query);
}
}
Una herramienta intraprocedimental para examinar UserRepository.findByRawId en aislamiento ve un parámetro id concatenado en una consulta SQL. Eso parece potencialmente inseguro, pero la herramienta no tiene forma de saber dónde id Proviene de, podría ser un valor confiable y validado del código interno. Por lo tanto, una herramienta intraprocedimental lo marca como un posible problema (produciendo falsos positivos en cada consulta parametrizada pero estructurada en el código base) o lo ignora (produciendo falsos negativos en vulnerabilidades reales). El análisis de contaminación interprocedimental detecta si los datos no confiables fluyen a través de funciones hacia operaciones sensibles. Rastrea los datos desde request.getParameter("id") atravesar userService.getUser(), A través userRepository.findByRawId(), a un db.execute(query), lo que produce un hallazgo preciso con la trayectoria exacta del flujo contaminado, sin falsos positivos ni falsos negativos.
Los tres niveles de análisis: un espectro práctico
Las herramientas de análisis estático del mundo real se sitúan en un espectro que va desde lo puramente intraprocedimental hasta lo totalmente interprocedimental. Comprender la posición de cada herramienta explica tanto lo que detecta como lo que no detecta.
| Nivel de análisis | <b></b><b></b> | Lo que ve | Lo que le falta | Accesorios |
|---|---|---|---|---|
| Léxico/sintáctico | Tokens individuales y sintaxis | Formato, nomenclatura, sintaxis básica | Todo semántico | Más bonito, rustfmt, gofmt |
| intraprocedimiento | Función única | Flujo de datos local, complejidad, flujo de control dentro de la función | Vulnerabilidades interfuncionales, problemas arquitectónicos | ESLint, Pylint, Clippy, golangci-lint |
| Interprocedimiento superficial | Un nivel de profundidad de llamada | Relaciones directas entre llamante y destinatario | Flujos de múltiples saltos profundos | Algunas reglas de Semgrep, reglas básicas de SAST |
| Interprocedimiento completo | Recorrido completo del grafo de llamadas | Flujos de contaminación de múltiples saltos, análisis sensible al contexto | Polimorfismo en tiempo de ejecución (parcial), llamadas reflectivas | CodeQL, Facebook Infer, SMART TS XL |
| Programa completo / semántico | Semántica del programa completo | Propiedades formalmente demostrables | Escalabilidad en bases de código muy grandes | Infer, Kani (Rust), herramientas de verificación formal |
El análisis interprocedimental presenta una eficacia potencial relativamente alta y un rendimiento potencial relativamente bajo. Gracias a su completo modelo de programa, es menos propenso a falsos negativos y falsos positivos, y ofrece una eficacia relativamente mayor. El análisis interprocedimental no es la mejor opción para entornos de CI/CD debido a sus limitaciones de rendimiento, que podrían reducir la velocidad de desarrollo. Por ello, suele ofrecerse como un servicio dedicado que se ejecuta en granjas de servidores o con herramientas específicas.
Cómo funciona el análisis interprocedimental: tres enfoques clave
1. Análisis basado en resúmenes
El análisis puede evitar revisar las mismas funciones repetidamente mediante la creación de resúmenes de funciones (a veces llamados modelos). Un resumen captura el comportamiento de una función de forma compacta: dado este conjunto de entradas con estas propiedades de contaminación, la función produce este conjunto de salidas con estas propiedades de contaminación. Cuando un llamador invoca la función, el análisis instancia el resumen en lugar de volver a analizar el cuerpo de la función desde cero.
El análisis basado en resúmenes es lo que hace que el análisis interprocedimental sea viable a gran escala. Sin resúmenes, analizar una función que llama a una biblioteca requeriría analizar toda la biblioteca desde el código fuente, incluyendo cada función a la que llama, de forma recursiva. Con los resúmenes, las funciones de la biblioteca se analizan previamente una sola vez y sus resúmenes se reutilizan en cada llamada.
2. Enfoque de cadena de llamada (análisis sensible al contexto)
El análisis sensible al contexto distingue entre diferentes sitios de llamada de la misma función. Si sanitize(input) Cuando se realiza una llamada con datos confiables en un lugar y datos no confiables en otro, un análisis sensible al contexto produce resultados diferentes para cada sitio de llamada en lugar de asumir de forma conservadora el peor escenario para todos ellos. Esto reduce significativamente los falsos positivos en comparación con el análisis no sensible al contexto.
El marco IFDS (Interprocedural, Finite, Distributive, Subset) formaliza este problema como un problema de alcanzabilidad de grafos. En el marco IFDS, el flujo de datos de propagación se describe mediante funciones distributivas sobre un dominio finito de hechos, y la propagación interprocedimental se reduce a la alcanzabilidad de grafos. Esta formulación hace que los análisis IFDS sean precisos y algorítmicamente eficientes; herramientas como FlowDroid (para Android) y varias consultas de CodeQL utilizan IFDS internamente.
3. Construcción del grafo de llamadas
Todo análisis interprocedimental requiere un grafo de llamadas: el grafo dirigido donde los nodos son funciones y las aristas representan llamadas. La construcción del grafo de llamadas en sí implica decisiones complejas:
Despacho estático (Llamadas no virtuales de Java, llamadas a funciones de C, llamadas a nivel de módulo de Python): fáciles de modelar, f() llamadas f.
Despacho dinámico (métodos virtuales de Java, llamadas con tipado dinámico de Python): requiere análisis de jerarquía de clases (CHA) o algoritmos de construcción de grafos de llamadas más precisos (RTA, VTA) para determinar qué implementaciones de un método podrían ser llamadas en un punto de llamada determinado.
Llamadas reflectivas y punteros a funciones : los casos más difíciles. Las herramientas deben sobreestimar (asumir que se puede llamar a cualquier función coincidente, lo que produce falsos positivos) o subestimar (omitir algunas rutas de llamada, lo que produce falsos negativos).
La precisión del grafo de llamadas determina directamente la precisión del análisis interprocedimental. Un grafo de llamadas con demasiados enlaces (sobreaproximación) produce falsos positivos; uno que omite enlaces produce falsos negativos.
Lo que el análisis interprocedimental encuentra que el intraprocedimental no puede encontrar
Vulnerabilidades de seguridad (Análisis de contaminación)
La mayoría de las vulnerabilidades del Top 10 de OWASP requieren un análisis de contaminación interprocedimental para su detección fiable:
- inyección SQL: Los flujos de entrada no confiables se realizan a través de múltiples funciones para una consulta a la base de datos.
- XSS: el HTML no confiable fluye a través de las funciones de renderizado hacia un contexto de salida
- Inyección de comando: datos controlados por el usuario alcanzan
exec()orsystem()a través de funciones intermediarias - Recorrido de ruta: las rutas de archivo derivadas de la entrada del usuario alcanzan las operaciones del sistema de archivos
- SSRFLas URL controladas por el usuario se redirigen a llamadas de cliente HTTP.
pitón
# Intraprocedural tool: sees no issue in any individual function
# Interprocedural tool: traces the full taint path and reports SSRF
def get_target_url(request):
return request.json().get("url") # source: attacker-controlled
def fetch_resource(url):
return requests.get(url) # sink: SSRF
def api_endpoint(request):
target = get_target_url(request)
data = fetch_resource(target) # full path: source -> sink
return data
Problemas relacionados con punteros nulos y seguridad de la memoria
Facebook Infer es la herramienta interprocedimental por excelencia para el análisis de seguridad de valores nulos. Utiliza la biabducción, una forma de razonamiento interprocedimental, para descubrir las precondiciones y postcondiciones de las funciones, y luego verifica que quienes las llaman cumplan dichas precondiciones. Una desreferenciación de un valor nulo que requiere saber que una función puede devolver null bajo ciertas condiciones es invisible para las herramientas intraprocedimentales.
Java
// Intraprocedural tool: no issue in either function
// Infer: finds that processResult() can receive null from fetchData()
String fetchData(boolean condition) {
if (condition) return getData();
return null; // can return null
}
void processResult(String result) {
System.out.println(result.length()); // NPE if result is null
}
void run() {
String data = fetchData(false); // passes false: fetchData returns null
processResult(data); // null dereference
}
Uso después de su liberación y fugas de recursos
En C y C++, los errores de uso de memoria liberada suelen implicar memoria asignada en una función y liberada en otra, mientras que una tercera función retiene una referencia. Ningún análisis de una sola función puede detectar este tipo de error. El análisis interprocedimental analiza la integridad del flujo de control, asegurando que las llamadas a funciones sigan los patrones esperados para prevenir vulnerabilidades.
Acoplamiento arquitectónico y violaciones de dependencia
Más allá de la seguridad y la corrección, el análisis interprocedimental se aplica al análisis arquitectónico: qué componentes llaman a cuáles, qué dependencias violan los límites de las capas definidas y qué cambios se propagarán en cascada a través del grafo de llamadas. Esta es la aplicación del análisis interprocedimental que utilizan herramientas como SMART TS XL Aplicar a bases de código empresariales, rastreando cómo se propaga un cambio en una función a través del gráfico de llamadas completo para determinar el alcance real del impacto.
Cuando se necesita un análisis interprocedimental
No todos los proyectos requieren un análisis interprocedimental completo. La decisión depende de lo que se busca y del costo de no encontrarlo.
Necesitas un análisis interprocedimental cuando:
- La seguridad es una preocupación primordial y es necesario encontrar vulnerabilidades de inyección, XSS o SSRF de forma fiable.
- Estás analizando una base de código heredada o empresarial donde la lógica de negocio abarca muchas funciones y módulos.
- Necesitas un análisis de impacto antes de una refactorización, para entender qué se verá afectado si cambia una función.
- Usted está realizando un análisis de cumplimiento o auditoría que requiere demostrar exhaustividad.
- Estás migrando o modernizando una base de código y necesitas comprender las dependencias entre componentes.
El análisis intraprocedimiento es suficiente cuando:
- El objetivo es hacer cumplir el estilo, respetar las convenciones y detectar errores locales evidentes.
- Necesitas retroalimentación rápida integrada en el ciclo de desarrollo (pre-commit, IDE, CI por PR).
- El código fuente es lo suficientemente pequeño como para que los desarrolladores conozcan todas las relaciones de llamadas.
- La seguridad se aplica en una capa diferente (marco validado, sistema de tipos) y el riesgo de vulnerabilidades entre funciones es bajo.
La respuesta práctica para la mayoría de los equipos es usar ambas. Las herramientas intraprocedimentales (ESLint, Pylint, Clippy) se ejecutan en cada commit para obtener retroalimentación rápida. Las herramientas interprocedimentales (CodeQL, Infer, Semgrep con reglas de detección de errores) se ejecutan en las solicitudes de extracción o de forma programada cada noche para un análisis exhaustivo. Ambas capas son complementarias, no competitivas.
Herramientas de análisis interprocedimental: qué abarca cada una
| Enfoque de análisis | Idiomas | Caso de uso principal | |
|---|---|---|---|
| CódigoQL | Interprocedimiento completo, basado en IFDS | C/C++, Java, JS/TS, Python, Go, Ruby | Detección de vulnerabilidades de seguridad |
| Facebook Infer | Biabducción, interprocedimiento | Java, C, C++, Objective-C | Seguridad nula, fugas de memoria, condiciones de carrera |
| Semgrep (modo de contaminación) | Contaminación interprocedimental superficial | 30+ idiomas | Coincidencia de patrones de seguridad con el flujo de datos |
| Código Snyk | Interprocedimiento basado en ML | Java, JS/TS, Python, C# | Seguridad con comentarios amigables para el desarrollador |
| Poliespacio | Interpretación abstracta | C, C++, Ada | Sistemas integrados de seguridad crítica |
| Frama-C | Interprocedimental completo + formal | C | Verificación formal, certificación de seguridad |
| SMART TS XL | Interlingüístico, intersistema | COBOL, JCL, Java, Python, RPG, SQL | Análisis de impacto empresarial, mapeo de dependencias |
Análisis interprocedimental a través de las fronteras lingüísticas
Las herramientas interprocedimentales estándar operan dentro de un único lenguaje. Una consulta CodeQL sobre una base de código Java rastrea el tráfico contaminado a través de las llamadas a funciones Java, pero se detiene cuando el código Java llama a un programa COBOL o lee datos de un conjunto generado por un trabajo por lotes JCL. Es en este límite donde las herramientas de análisis a escala empresarial se enfrentan a un problema que las herramientas de un solo lenguaje no pueden resolver.
SMART TS XL, análisis de código estático realiza un análisis de dependencias de estilo interprocedimental en todos los lenguajes del entorno empresarial simultáneamente. Cuando un servicio Java llama a un programa COBOL que lee de una tabla DB2 que otro trabajo JCL llena, SMART TS XL rastrea esa cadena y la representa como un modelo de dependencia unificado. análisis de impacto La capacidad aplica entonces este gráfico de llamadas entre lenguajes para responder: si este programa COBOL cambia, ¿qué servicios Java, qué trabajos JCL y qué consumidores posteriores se verán afectados?
Este es el problema interprocedimental aplicado a escala arquitectónica en lugar de funcional, el mismo principio (seguir el grafo de dependencias a través de los límites en lugar de detenerse en ellos) aplicado al sistema empresarial completo en lugar de a programas individuales. Como se examina en el contexto de la visualización de código y el mapeo de dependencias , hacer visible la estructura completa de dependencias es un requisito previo para cualquier análisis que pretenda comprender el impacto de un cambio.
Para las organizaciones que realizan modernización heredada, el gráfico de llamadas que SMART TS XL La creación de estructuras de datos en diferentes idiomas cumple la misma función que un gráfico de llamadas interprocedimentales dentro de un solo idioma: es el mapa de cómo se conecta todo, y sin él, cualquier análisis de impacto, riesgo o secuencia de migración se basa en suposiciones en lugar de evidencia.
La disyuntiva entre falsos positivos y falsos negativos
La razón por la que el análisis interprocedimental no se usa en todas partes no es que nadie lo haya pensado, sino que existe una tensión entre precisión y rendimiento. Un análisis interprocedimental completo y sensible al contexto de un código fuente de un millón de líneas puede llevar horas o días. El mismo código fuente analizado por ESLint tarda segundos. Esta diferencia de rendimiento es estructural: el análisis interprocedimental aumenta con la complejidad del grafo de llamadas, no solo con el tamaño del código fuente.
El beneficio de la inversión radica en una reducción considerable de las tasas de falsos positivos y falsos negativos:
- Falsos positivos (Herramientas que indican que algo está mal cuando no lo está): Las herramientas intraprocedimentales generan muchos más falsos positivos porque no pueden determinar si un patrón sospechoso es realmente peligroso sin conocer el contexto proporcionado por quienes realizan las llamadas. Una herramienta intraprocedimental que detecta una consulta SQL con una cadena concatenada no puede saber si la cadena proviene de la entrada del usuario o de una fuente interna confiable.
- Falsos negativos (herramientas que no detectan vulnerabilidades reales): las herramientas intraprocedimentales producen muchos más falsos negativos porque no pueden detectar vulnerabilidades que abarcan los límites de las funciones, que, para un análisis relevante en materia de seguridad, constituyen la mayoría de las vulnerabilidades reales.
La disciplina de elegir cuándo usar cada nivel de análisis y combinarlos en una cadena de herramientas por capas es lo que distingue la ingeniería de seguridad del mero cumplimiento de requisitos. Saber que tu linter arroja resultados limpios dice muy poco sobre tu postura de seguridad. Saber que tu análisis de contaminación interprocedimental ha rastreado cada ruta desde las fuentes de entrada externas hasta los destinos sensibles en tu aplicación y no ha encontrado ningún flujo no validado dice mucho más.


