flawopen.com/Referencia/Seguridad de bots de PR
Bots de revisión de PR: evita SSRF por prompt injection
Cómo el texto no confiable de una pull request puede influir en herramientas de IA y cómo limitar permisos del bot y acceso a la red.
ELI5 — Un asistente revisor con acceso a la red privada
Una pull request es como un paquete entregado al equipo revisor: su contenido puede ser útil, pero no se convierte en política de la empresa. Si el revisor de IA también puede consultar URLs arbitrarias desde una red privada, una instrucción engañosa puede influir en el destino. El riesgo depende de los permisos de la herramienta y de la red; leer texto hostil no concede acceso a la red por sí solo.
Conceptos básicos y términos clave
- Entrada no confiable:
- Títulos, descripciones, comentarios, diffs y enlaces de pull requests de colaboradores.
- Prompt injection indirecta:
- Texto no confiable influye en el agente porque se incluye en el contexto del modelo.
- Límite de SSRF:
- Una herramienta de consulta del servidor puede alcanzar destinos a los que el colaborador no llega directamente.
- Condiciones de impacto:
- También se necesitan un servicio interno accesible, credenciales disponibles y controles insuficientes de autorización o salida de red.
Mecanismo y causa raíz
Un bot revisor puede combinar texto controlado por el colaborador con instrucciones de revisión confiables. El modelo puede proponer una llamada a herramienta basándose en cualquiera de ellos. Los prompts y delimitadores no autorizan el acceso a la red. La SSRF solo es posible si el código acepta un destino elegido por el modelo y una herramienta con acceso a la red puede alcanzarlo. El acceso a metadatos de nube o credenciales es un impacto adicional condicionado, no un resultado automático de la prompt injection.
Ruta de ataque condicionada
El colaborador controla contenido de la PR
Un colaborador externo puede influir en títulos, descripciones, comentarios, texto modificado y material enlazado.
El agente lee ese contenido
El servicio incluye algunos campos no confiables en el contexto del modelo; esto puede influir en la respuesta o en la propuesta de herramienta.
La herramienta determina el alcance
Si el agente puede consultar URLs arbitrarias, el runner puede contactar destinos permitidos por la red. Sin esa herramienta, esta ruta SSRF no existe.
El impacto depende del despliegue
Los datos internos o las credenciales solo quedan expuestos si lo permiten los servicios accesibles, la configuración de metadatos, los permisos y los controles de salida.
Código: despacho sin control frente a lectura autorizada
# INERT DEMO: model-selected URL reaches a mock tool without policy checks
from dataclasses import dataclass
@dataclass
class ToolCall:
name: str
arguments: dict[str, str]
class MockFetch:
def __init__(self):
self.calls = []
def __call__(self, url: str) -> str:
self.calls.append(url)
return "fixture response"
def dispatch_unchecked(call: ToolCall, tools: dict):
# The mock records a string; it makes no network request.
return tools[call.name](**call.arguments)
mock_fetch = MockFetch()
call = ToolCall("fetch_url", {"url": "https://docs.example.invalid/"})
assert dispatch_unchecked(call, {"fetch_url": mock_fetch}) == "fixture response"
assert mock_fetch.calls == ["https://docs.example.invalid/"]
# SAFE DEMO: the agent receives only a read-only PR capability
from dataclasses import dataclass
@dataclass(frozen=True)
class ReviewContext:
repository: str
pull_request: int
@dataclass
class ToolCall:
name: str
arguments: dict[str, object]
READ_ONLY_TOOLS = {"read_pr_diff"}
def dispatch_review_call(call: ToolCall, context: ReviewContext, policy, tools):
if call.name not in READ_ONLY_TOOLS:
raise PermissionError("Tool is not available to this review")
expected = {"repository": context.repository,
"pull_request": context.pull_request}
if call.arguments != expected:
raise ValueError("Arguments must match the authorized review")
if not policy.can_read_pr(context.repository, context.pull_request):
raise PermissionError("Review principal cannot read this pull request")
# The injected read_pr_diff function is trusted app code, not model output.
return tools["read_pr_diff"](**expected)
Lista de controles de ingeniería
- No presentes este escenario como un incidente confirmado de un bot de PR. La página modela un riesgo de diseño condicionado.
- No conectes una herramienta de consulta de URLs arbitrarias a un revisor que solo necesita inspeccionar un diff.
- Si necesitas previsualizar enlaces externos, usa un servicio de salida dedicado con allowlist de destinos, validación de DNS/IP al conectar, nueva validación de redirecciones y límites estrictos de tiempo y tamaño, sin credenciales implícitas.
- Bloquea el acceso del runner a redes privadas y metadatos de nube. Desactiva los metadatos si la carga no los necesita; de lo contrario, exige IMDSv2 y valida el hop limit para contenedores.
- Asigna al bot una identidad de solo lectura y limitada al repositorio. No expongas secretos en jobs de PR no confiables y separa los flujos privilegiados de la ejecución de código externo.
- En GitHub Actions, prefiere `pull_request` con privilegios mínimos para código no confiable. Trata `pull_request_target` como privilegiado y nunca ejecutes código de forks con sus secretos o token de escritura.
- Registra llamadas rechazadas y decisiones de política de destino sin guardar tokens, diffs privados completos ni cuerpos de respuesta sensibles.