flawopen.com/Referência/Segurança de bots de PR
Bots de revisão de PR: evite SSRF por prompt injection
Como texto não confiável de pull requests pode influenciar ferramentas de IA e como limitar permissões do bot e acesso à rede.
ELI5 — Um assistente de revisão com acesso à rede interna
Uma pull request é como um pacote entregue à equipe revisora: o conteúdo pode ser útil, mas não vira política da empresa. Se o revisor de IA também puder buscar URLs arbitrárias dentro da rede privada, uma instrução enganosa poderá influenciar o destino consultado. O risco depende das permissões da ferramenta e da rede; ler texto hostil, por si só, não concede acesso à rede.
Conceitos centrais e termos-chave
- Entrada não confiável:
- Títulos, descrições, comentários, diffs e links de pull requests enviados por contribuidores.
- Prompt injection indireta:
- Texto não confiável influencia o agente porque foi incluído no contexto do modelo.
- Limite de SSRF:
- Uma ferramenta de busca no servidor pode alcançar destinos inacessíveis diretamente ao contribuidor.
- Condições de impacto:
- Também são necessários um serviço interno acessível, credenciais disponíveis e controles insuficientes de autorização ou saída de rede.
Mecanismo e causa raiz
Um bot de revisão pode combinar texto controlado pelo contribuidor com instruções confiáveis de revisão. O modelo pode propor uma chamada de ferramenta com base em qualquer um deles. Prompts e delimitadores não autorizam acesso à rede. SSRF só é possível quando o código aceita um destino escolhido pelo modelo e uma ferramenta com acesso à rede consegue alcançá-lo. Acesso a metadados de nuvem ou credenciais é um impacto adicional condicionado, não um resultado automático da prompt injection.
Caminho de ataque condicionado
O contribuidor controla o conteúdo da PR
Um contribuidor externo pode influenciar títulos, descrições, comentários, texto alterado e materiais vinculados.
O agente lê esse conteúdo
O serviço inclui alguns campos não confiáveis no contexto do modelo; isso pode influenciar a resposta ou a proposta de ferramenta.
A ferramenta define o alcance
Se o agente puder buscar URLs arbitrárias, o runner poderá contatar destinos permitidos pela rede. Sem essa ferramenta, esse caminho de SSRF não existe.
O impacto depende da implantação
Dados internos ou credenciais só ficam expostos se serviços acessíveis, metadados, permissões de identidade e controles de saída permitirem.
Código: despacho sem validação vs. leitura 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)
Checklist de hardening de engenharia
- Não apresente este cenário como um incidente confirmado de bot de PR. A página modela um risco de projeto condicionado.
- Não conecte uma ferramenta de busca de URL arbitrária a um revisor que só precisa inspecionar um diff.
- Se a prévia de links externos for necessária, use um serviço de saída dedicado com allowlist de destinos, validação de DNS/IP no momento da conexão, nova validação de redirecionamentos e limites estritos de tempo e tamanho, sem credenciais implícitas.
- Bloqueie o acesso do runner a redes privadas e metadados de nuvem. Desative metadados quando desnecessários; caso contrário, exija IMDSv2 e valide o hop limit necessário para contêineres.
- Use identidade somente leitura e limitada ao repositório. Não exponha segredos em jobs de PRs não confiáveis e separe fluxos privilegiados da execução de código externo.
- No GitHub Actions, prefira `pull_request` com privilégio mínimo para código não confiável. Trate `pull_request_target` como privilegiado e nunca execute código de forks com seus segredos ou token de escrita.
- Registre chamadas rejeitadas e decisões de política de destino sem armazenar tokens, diffs privados completos ou corpos de resposta sensíveis.