Trailmark 0.2.4 · Structural Analysis

Análisis Estructural — AgentCore v2.4.1

📁 agentcore/
🐍 Python 3.11
📄 18.247 LOC · 214 archivos
🕐 2026-06-16 09:43 UTC
🔍 4 pases de preanálisis completados
Risk Score
8.4
CRÍTICO
Resumen Estructural
7
Hotspots críticos
Complejidad > 35
23
Taint flows activos
9 sin sanitizar
41
Entrypoints
Superficie de ataque
5
Fronteras de privilegio
2 cruzadas sin validar
214
Archivos analizados
18.247 LOC totales
1.847
Nodos en grafo
312 aristas críticas
68%
Blast radius máx.
orquestador central
9
Sinks sin sanitizar
SQL / exec / prompt
Hotspots de Complejidad (Top 10)
# Función / Método Módulo Complejidad ciclomática LOC Blast Radius Severidad Riesgo principal
1
OrchestratorEngine.dispatch_task()
core/orchestrator.py:287
core
72
341
68%
CRÍTICO Prompt injection · tenant bypass
2
WebhookProcessor.handle_event()
api/webhooks.py:134
api
53
218
44%
CRÍTICO SSRF · deserialization
3
QueryBuilder.build_dynamic()
db/query_builder.py:89
db
45
167
37%
CRÍTICO SQL injection potencial
4
RBACMiddleware.check_permissions()
auth/rbac.py:56
auth
41
143
34%
HIGH Privilege escalation
5
CeleryTask.execute_agent_run()
workers/agent_runner.py:201
workers
38
189
29%
HIGH Async race condition
6
OAuthFlow.exchange_token()
auth/oauth2.py:112
auth
36
121
26%
HIGH Token leakage · CSRF
7
PromptRenderer.render_template()
core/prompts.py:67
core
35
98
23%
HIGH Prompt injection directa
8
TenantIsolator.filter_queryset()
db/tenancy.py:44
db
27
82
20%
MEDIUM Cross-tenant data leak
9
APIRouter.route_request()
api/router.py:78
api
24
71
16%
MEDIUM Request smuggling
10
CredentialStore.retrieve_secret()
core/credentials.py:33
core
19
54
13%
MEDIUM Secret exposure en logs
Taint Flows — 9 Sin Sanitizar
TF-01 · Prompt Injection → LLM Exec
CRÍTICO
api/agents.py:POST /agents/run core/orchestrator.py:dispatch_task() core/prompts.py:render_template() LLM API call
Riesgo: user_input fluye sin escape a prompt LLM — exfiltración de datos de otros tenants
TF-02 · SQL Injection → DB Query
CRÍTICO
api/search.py:GET /search?q= db/query_builder.py:build_dynamic() db.execute() raw SQL
Riesgo: parámetro q sin parameterización — SQLi clásica confirmada
TF-03 · SSRF vía Webhook URL
CRÍTICO
api/webhooks.py:POST /webhooks api/webhooks.py:handle_event() httpx.get(user_url)
Riesgo: URL de callback sin validación de allowlist — acceso a metadata AWS
TF-04 · Token Leakage en Logs
HIGH
auth/oauth2.py:exchange_token() partial sanitize logger.debug(token)
Riesgo: sanitización incompleta — access_token en logs de producción
TF-05 · Tenant ID Bypass
HIGH
api/agents.py:PUT /agents/{id} core/orchestrator.py:dispatch_task() db.query sin tenant_filter
Riesgo: tenant_id de payload no verificado contra JWT — IDOR entre tenants
TF-06 · Deserialization RCE
HIGH
workers/agent_runner.py:Celery task pickle.loads(redis_data)
Riesgo: datos de Redis deserializados con pickle — RCE si Redis comprometido
TF-07 · PII en Response Body
MEDIUM
db/models.py:User.to_dict() api/users.py:GET /users/{id} JSONResponse(full_object)
Riesgo: password_hash y api_keys incluidos en respuesta — oversharing de PII
TF-08 · Path Traversal en File Export
MEDIUM
api/export.py:GET /export?path= open(base_dir + path)
Riesgo: path no normalizado — lectura de /etc/passwd posible
TF-09 · Command Injection via Shell
MEDIUM
api/tools.py:POST /tools/exec workers/shell_runner.py subprocess.run(shell=True)
Riesgo: command sin shlex.quote() — inyección de comandos OS
Subgrafos del Codebase
🔥
taint_sources
41
Entrypoints con datos externos
⚠️
taint_sinks
23
Sinks de datos sensibles
🛡️
sanitizers
14
Funciones de sanitización
🔑
privilege_boundaries
5
Cambios de contexto privilegiado
🕸️
high_fan_out
18
Nodos con >10 dependencias salientes
💣
blast_radius_gt_30
7
Funciones con radio > 30%
🔗
async_boundaries
31
Cruces sync/async
validated_inputs
67
Inputs con validación Pydantic
Superficie de Ataque — Top 12 Entrypoints
POST /agents/run — OrchestratorEngine.dispatch_task()
api/agents.py:203 → core/orchestrator.py:287
HTTP AUTH REQUIRED ADMIN
CRÍTICO TF-01, TF-05
POST /webhooks — WebhookProcessor.handle_event()
api/webhooks.py:89 · Sin autenticación en algunos providers
HTTP WEBHOOK PUBLIC
CRÍTICO TF-03, TF-06
GET /search?q= — QueryBuilder.build_dynamic()
api/search.py:45 → db/query_builder.py:89
HTTP AUTH REQUIRED
CRÍTICO TF-02
POST /oauth/token — OAuthFlow.exchange_token()
auth/oauth2.py:78 · CSRF mitigación parcial
HTTP AUTH PUBLIC
HIGH TF-04
POST /tools/exec — ShellRunner.execute_command()
api/tools.py:156 → workers/shell_runner.py:34
HTTP ADMIN ONLY ASYNC
HIGH TF-09
GET /export?path= — FileExporter.export_data()
api/export.py:67 · Path traversal detectado
HTTP AUTH REQUIRED
HIGH TF-08
Celery: execute_agent_run — AgentRunner.run()
workers/agent_runner.py:201 · pickle.loads sin validación
ASYNC INTERNAL
HIGH TF-06
GET /users/{id} — UserService.get_user()
api/users.py:88 · PII oversharing en response
HTTP AUTH REQUIRED
MEDIUM TF-07
Payload JSON — engine.preanalysis() Output (extracto)
"languages" "python" "summary" "total_nodes" 1847 "total_edges" 5213 "total_files" 214 "total_loc" 18247 "languages" "python" 214 "preanalysis" "complexity_hotspots" "computed" true "count" 43 "threshold_cyclomatic" 15 "taint_flows" "computed" true "total_paths" 23 "unsanitized" 9 "critical_sinks" "sql_exec" "llm_call" "http_request" "pickle_loads" "blast_radius" "computed" true "max_radius_pct" 0.68 "max_node" "core.orchestrator.OrchestratorEngine.dispatch_task" "privilege_boundaries" "computed" true "boundaries_found" 5 "unvalidated_crossings" 2 "attack_surface" "id" "api.agents.run_agent" "type" "http_post" "auth" true "risk" "critical" "id" "api.webhooks.handle_event" "type" "http_post" "auth" false "risk" "critical" "id" "api.search.search_entities" "type" "http_get" "auth" true "risk" "critical" "id" "auth.oauth2.exchange_token" "type" "http_post" "auth" false "risk" "high" "subgraphs" "taint_sources" "count" 41 "sample_ids" "api.agents.run_agent" "api.webhooks.handle_event" "api.search.search_entities" "taint_sinks" "count" 23 "sample_ids" "db.query_builder.build_dynamic" "core.prompts.render_template" "workers.shell_runner.execute_command" "privilege_boundaries" "count" 5 "sample_ids" "auth.rbac.check_permissions" "core.credentials.retrieve_secret" "blast_radius_gt_30" "count" 7 "sample_ids" "core.orchestrator.OrchestratorEngine.dispatch_task" "api.webhooks.WebhookProcessor.handle_event"