Pular para conteúdo

Capítulo 4.7 — Policy-as-code

🧠 Conceito

Políticas críticas são codificadas, testáveis e versionadas. Exemplos:

  • "Agente não pode enviar e-mail externo sem aprovação dupla."
  • "Tool de CRM só acessa campos permitidos por role."
  • "Documentos confidenciais não vão para modelo externo sem contrato."

🧰 Engines comuns

  • Open Policy Agent (OPA) / Rego - engine declarativo de propósito geral, usado em Kubernetes, microsserviços, gateways e agentes.
  • AWS Cedar - linguagem de política focada em authorization, com semântica de "policy + entity store".
  • AuthZed SpiceDB - ReBAC distribuído, modelo inspirado em Zanzibar (Google).

🧪 Exemplo prático - autorização de tool de agente em Rego

Cenário: um harness chama OPA passando o input abaixo antes de executar qualquer tool_call. A policy precisa:

  • negar tool com status deprecated ou removed;
  • negar cross-tenant (user.tenant_id != resource.tenant_id);
  • exigir aprovação humana para risk_level == "high";
  • permitir apenas tools na allowlist da role.

Input típico enviado pelo executor:

{
  "user": {
    "id": "u-1042",
    "tenant_id": "tenant-abc",
    "roles": ["support-agent"]
  },
  "agent": {
    "id": "support-agent@2.1.0",
    "tenant_id": "tenant-abc"
  },
  "tool": {
    "name": "crm.update_contact",
    "version": "1.4.0",
    "status": "active",
    "risk_level": "high"
  },
  "action": "execute",
  "resource": {
    "type": "contact",
    "tenant_id": "tenant-abc"
  },
  "approval": {
    "required": true,
    "granted_by": "manager-7",
    "granted_at": "2026-05-10T18:22:00Z"
  }
}

Policy Rego (exemplo educacional):

package agent.tools.authz

default allow := false

# Allowlist de tools por role.
allowed_tools := {
  "support-agent": {
    "crm.search_contact",
    "crm.update_contact",
    "kb.search",
  },
  "billing-agent": {
    "crm.search_contact",
    "billing.create_invoice",
  },
}

# Tools com status proibido nunca podem rodar.
blocked_status := {"deprecated", "removed", "blocked"}

# Helper: a tool está na allowlist da role do usuário?
tool_in_allowlist {
  some role
  role := input.user.roles[_]
  allowed_tools[role][input.tool.name]
}

# Helper: tenant do usuário == tenant da agente == tenant do recurso.
same_tenant {
  input.user.tenant_id == input.agent.tenant_id
  input.user.tenant_id == input.resource.tenant_id
}

# Helper: aprovação humana válida para alto risco.
approval_ok {
  input.tool.risk_level != "high"
}

approval_ok {
  input.tool.risk_level == "high"
  input.approval.required
  input.approval.granted_by != ""
}

# Regra principal.
allow {
  not blocked_status[input.tool.status]
  tool_in_allowlist
  same_tenant
  approval_ok
}

# Motivos de negação (úteis para audit log).
deny_reason["tool_status_blocked"] {
  blocked_status[input.tool.status]
}

deny_reason["tool_not_in_allowlist"] {
  not tool_in_allowlist
}

deny_reason["cross_tenant"] {
  not same_tenant
}

deny_reason["approval_missing"] {
  input.tool.risk_level == "high"
  not input.approval.granted_by
}

O que essa policy garante:

  • Bloqueia tool deprecated/removed independentemente do que o modelo propõe.
  • Bloqueia chamada cross-tenant mesmo se o prompt dissesse o contrário.
  • Bloqueia tool fora da allowlist da role.
  • Exige aprovação humana registrada para risk_level == "high".
  • Devolve motivos estruturados para o audit log.

O que essa policy NÃO garante:

  • Que o conteúdo dos argumentos é válido (precisa schema/Pydantic).
  • Que a aprovação humana foi consciente (depende de UX e treinamento).
  • Que o backend da tool aplica RBAC interno (precisa autorização no serviço chamado também - defesa em profundidade).
  • Que o input enviado ao OPA é honesto (depende do harness; quem controla o input controla a decisão).

🧪 Testes da policy

Toda policy precisa de testes positivos e negativos versionados no mesmo repo. Em OPA, isso pode ser feito com opa test e arquivos *_test.rego:

package agent.tools.authz_test

import data.agent.tools.authz

test_allow_simple_case {
  authz.allow with input as {
    "user": {"id": "u1", "tenant_id": "t1", "roles": ["support-agent"]},
    "agent": {"id": "a@1", "tenant_id": "t1"},
    "tool": {"name": "kb.search", "status": "active", "risk_level": "low"},
    "action": "execute",
    "resource": {"type": "doc", "tenant_id": "t1"},
    "approval": {"required": false, "granted_by": "", "granted_at": ""}
  }
}

test_deny_cross_tenant {
  not authz.allow with input as {
    "user": {"id": "u1", "tenant_id": "t1", "roles": ["support-agent"]},
    "agent": {"id": "a@1", "tenant_id": "t1"},
    "tool": {"name": "kb.search", "status": "active", "risk_level": "low"},
    "action": "execute",
    "resource": {"type": "doc", "tenant_id": "t2"},
    "approval": {"required": false, "granted_by": "", "granted_at": ""}
  }
}

test_deny_deprecated_tool {
  not authz.allow with input as {
    "user": {"id": "u1", "tenant_id": "t1", "roles": ["support-agent"]},
    "agent": {"id": "a@1", "tenant_id": "t1"},
    "tool": {"name": "crm.update_contact", "status": "deprecated", "risk_level": "high"},
    "action": "execute",
    "resource": {"type": "contact", "tenant_id": "t1"},
    "approval": {"required": true, "granted_by": "m7"}
  }
}

🧰 Exemplos práticos relacionados (planejados)

  • EX-AGT-06 - agente com policy Rego sidecar bloqueando tool calls.
  • EX-SEC-03 - tool misuse mitigado por policy.

📌 Checklist

  • [ ] Políticas críticas estão em código e versionadas?
  • [ ] Há testes de policy (positivos e negativos)?
  • [ ] Há observabilidade de policy decisions (decisão + motivo)?
  • [ ] Decisões são logadas com versão do bundle de policy?
  • [ ] Backend chamado pelas tools aplica autorização independente (defesa em profundidade)?

📚 Referências