Allowlist vs Blocklist (чому default-deny перемагає) + Код

Blocklist гниє. Allowlist масштабується. Модель tool policy, яка не дозволяє випадково наступний небезпечний інструмент.
На цій сторінці
  1. Проблема (з реального продакшену)
  2. Чому це ламається в продакшені
  3. 1) Неможливо наперед перелічити “всі небезпечні tools”
  4. 2) Blocklist ламається на назвах і непрямості
  5. 3) Default-allow тихо збільшує blast radius
  6. 4) Allowlist змушує приймати явне рішення
  7. Приклад реалізації (реальний код)
  8. Реальний інцидент (з цифрами)
  9. Компроміси
  10. Коли НЕ варто
  11. Чекліст (можна копіювати)
  12. Безпечний дефолтний конфіг (JSON/YAML)
  13. FAQ (3–5)
  14. Пов’язані сторінки (3–6 лінків)
Інтерактивний флоу
Сценарій:
Крок 1/3: Execution

Action is proposed as structured data (tool + args).

Проблема (з реального продакшену)

Починаєш з blocklist, бо це швидко: “забороняємо небезпечне”.

Потім додаєш новий tool. Забуваєш оновити blocklist. Агент його знаходить.

І тепер ти on-call, бо “забороняємо небезпечне” було радше історією, ніж контролем.

Blocklist здається контролем. У агентних системах це майже завжди пастка. І вона швидко гниє: через два тижні ніхто не пам’ятає, що саме цей список “захищав”.

Чому це ламається в продакшені

1) Неможливо наперед перелічити “всі небезпечні tools”

Сьогодні це db.delete_user. Завтра — crm.merge_accounts. Наступного тижня — tickets.close_all.

Tool, який ти забудеш заблокувати, і стане проблемою.

2) Blocklist ламається на назвах і непрямості

Блокуєш db.write — хтось релізить db.patch. Блокуєш email.send — з’являється email.send_bulk.

Гірше: wrapper’и. Агент викликає workflow.run("close_ticket"), і твій “blocklist” навіть не бачить реальний side effect.

3) Default-allow тихо збільшує blast radius

Якщо policy каже “дозволено все, окрім…”, то кожен новий tool — це розширення прав за замовчуванням. Це інцидент, який просто чекає моменту.

4) Allowlist змушує приймати явне рішення

Allowlist дратує. Добре. Вона змушує сказати: “так, агент може викликати цей tool за цих умов”.

Приклад реалізації (реальний код)

Маленький policy evaluator:

  • default-deny
  • опційний deny list для incident mode (екстрене гальмо)
  • “write tools → approval”
PYTHON
from dataclasses import dataclass


WRITE_TOOLS = {"email.send", "db.write", "ticket.close"}


@dataclass(frozen=True)
class Policy:
  allow: set[str]
  deny: set[str] = None  # for incident mode
  require_approval_for_writes: bool = True


class Denied(RuntimeError):
  pass


def evaluate(policy: Policy, tool: str) -> str:
  deny = policy.deny or set()
  if tool in deny:
      raise Denied(f"denied: {tool} (incident mode)")
  if tool not in policy.allow:
      raise Denied(f"not allowed: {tool}")
  if policy.require_approval_for_writes and tool in WRITE_TOOLS:
      return "approve"
  return "allow"
JAVASCRIPT
const WRITE_TOOLS = new Set(["email.send", "db.write", "ticket.close"]);

export class Denied extends Error {}

export function evaluate(policy, tool) {
const deny = new Set(policy.deny || []);
if (deny.has(tool)) throw new Denied("denied: " + tool + " (incident mode)");
if (!policy.allow.includes(tool)) throw new Denied("not allowed: " + tool);
if (policy.requireApprovalForWrites && WRITE_TOOLS.has(tool)) return "approve";
return "allow";
}

Реальний інцидент (з цифрами)

Ми бачили команду, яка запустила агента з policy: “deny dangerous tools”.

Потім вони додали tool: ticket.close_bulk. Його не було в deny list. Агент використав його, бо це був найкоротший шлях до “resolve”.

Імпакт:

  • ~200 тікетів закрито помилково
  • ~5 годин інженерного часу на reopen/пояснення/патч
  • агента вимкнули на тиждень, бо ніхто йому не довіряв

Fix:

  1. default-deny allowlist
  2. write tools за approvals
  3. deny list — тільки для incident mode (тимчасово)

Blocklist — ок як аварійне гальмо. Як кермо — погано.

Компроміси

  • Allowlist сповільнює “just ship it”. У проді це фіча.
  • Потрібні інструменти для менеджменту allowlist (не хардкод у 12 місцях).
  • Люди спробують обійти через wrapper’и. Не давай.

Коли НЕ варто

  • Для локальних прототипів можна стартувати ліберальніше — але не тягни це в прод.
  • Навіть якщо tools read-only, маленький allowlist зменшує “випадкові” експозиції.
  • Якщо твій “tool” — це універсальний RPC “зроби будь-що”, виправ tool: розбий за capabilities.

Чекліст (можна копіювати)

  • [ ] Default-deny allowlist (явні назви tools)
  • [ ] Розділення tools за capability (read vs write)
  • [ ] Approvals для незворотних write’ів
  • [ ] Deny list тільки для incident mode (тимчасово)
  • [ ] Логувати denied спроби (це сигнал)
  • [ ] Не експонувати “do anything” tools

Безпечний дефолтний конфіг (JSON/YAML)

YAML
policy:
  default: "deny"
  allow: ["search.read", "kb.read", "http.get"]
  require_approval_for_writes: true
incident_mode:
  deny: ["browser.run"] # тимчасове гальмо
logging:
  log_denies: true

FAQ (3–5)

Blocklist взагалі корисний?
Так: для incident mode і тимчасових відключень. Це гальмо, а не модель permission’ів.
Можна wildcard allowlist?
Обережно. Wildcards майже завжди дрейфують у default-allow. Якщо треба — роби вузько і регулярно рев’ю.
А якщо є tool типу ‘workflow.run’?
Він ховає side effects. Краще мати явні tools для явних дій, щоб policy могла міркувати.
Яка мінімальна safe policy?
Default-deny allowlist + read-only tools. Writes додавати пізніше, за approvals.

Q: Blocklist взагалі корисний?
A: Так: для incident mode і тимчасових відключень. Це гальмо, а не модель permission’ів.

Q: Можна wildcard allowlist?
A: Обережно. Wildcards майже завжди дрейфують у default-allow. Якщо треба — роби вузько і регулярно рев’ю.

Q: А якщо є tool типу ‘workflow.run’?
A: Він ховає side effects. Краще мати явні tools для явних дій, щоб policy могла міркувати.

Q: Яка мінімальна safe policy?
A: Default-deny allowlist + read-only tools. Writes додавати пізніше, за approvals.

Пов’язані сторінки (3–6 лінків)

Не впевнені, що це ваш кейс?

Спроєктувати агента →
⏱️ 5 хв читанняОновлено Бер, 2026Складність: ★★★
Реалізувати в OnceOnly
Budgets + permissions you can enforce at the boundary.
Використати в OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
writes:
  require_approval: true
  idempotency: true
controls:
  kill_switch: { enabled: true }
Інтегровано: продакшен-контрольOnceOnly
Додай guardrails до агентів з tool-calling
Зашип цей патерн з governance:
  • Бюджетами (кроки / ліміти витрат)
  • Дозволами на інструменти (allowlist / blocklist)
  • Kill switch та аварійна зупинка
  • Ідемпотентність і dedupe
  • Audit logs та трасування
Інтегрована згадка: OnceOnly — контрольний шар для продакшен агент-систем.
Автор

Цю документацію курують і підтримують інженери, які запускають AI-агентів у продакшені.

Контент створено з допомогою AI, із людською редакторською відповідальністю за точність, ясність і продакшн-релевантність.

Патерни та рекомендації базуються на постмортемах, режимах відмов і операційних інцидентах у розгорнутих системах, зокрема під час розробки та експлуатації governance-інфраструктури для агентів у OnceOnly.