Skip to content
C.W.K.
Stream
Lesson 01 of 04 · published

The Allowlist That Did Nothing

~12 min · security, agents, verification, bugs

Level 0Unsorted
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

A Control That Silently Removed the Capability It Was Protecting

The sampler runs a headless agent and asks it to search a social platform. The obvious hardening is a tool allowlist: name the four search tools, exclude everything else, done. It reads like a textbook least-privilege configuration.

What actually happened is that naming those tools left them unwired in the session. The agent ran, produced a confident, well-formatted answer, and never searched anything. A session with no restriction flag at all used the same tools without difficulty. The lockdown had removed the very capability it was meant to permit, and nothing anywhere reported a problem.

Why This Class of Failure Is So Dangerous

Consider the two possible states. In the first, the allowlist works: the agent searches, and returns an answer grounded in what it found. In the second, the allowlist has broken the tools: the agent cannot search, and returns an answer from its own prior knowledge. Both produce a successful run with plausible output. The exit code is the same, the format is the same, and the content is confident either way.

So the general rule is uncomfortable but simple: a control that restricts capability needs a positive check that the capability survived. Verifying the restriction is not enough — you have to verify what remains still works, or an over-restrictive configuration is indistinguishable from a correct one.

The Denylist, and Why It Is Not a Downgrade Here

The replacement inverts the shape: keep the tools available, and explicitly remove the dangerous ones — anything that runs commands, reads or writes files, spawns further agents, or schedules work. That looks like weaker security by the usual argument, since a denylist cannot cover what you have not thought of.

Two things make it the right trade. First, the allowlist did not work, and a control that does not function offers no security at all. Second, the actual threat here is specific and enumerable: the danger is a tool that acts on the machine, and that set is small, known, and checkable. Meanwhile the capability being preserved — search — is verified separately by a purity gate, which is the positive check the allowlist version never had.

Probe Before You Trust the Configuration

The process fix is a live probe. Before building on any capability-restricting flag, run one invocation with it and confirm the restricted capability actually functions — not that the command exits zero, but that the specific thing you allowed happened. Here, that means checking the response contains a real citation from the platform. Ten minutes of probing replaced an assumption that had already shipped.

Verify what a control permits, not only what it forbids. A misconfiguration that removes too much is silent by construction: nothing errors, the output looks fine, and you find out when someone asks how the answer was obtained.

Code

The allowlist that broke the capability, and the denylist plus positive check that replaced it·bash
# WHAT WAS TRIED: least privilege by allowlist. Reads correctly and
# silently left the four search tools UNWIRED -- the agent answered
# from prior knowledge, exit 0, plausible output, no search performed.
grok -p "$PROMPT" \
  --tools 'x_user_search,x_keyword_search,x_semantic_search,x_thread_fetch' \
  --yolo --output-format json


# WHAT SHIPPED: a denylist of everything that ACTS on the machine.
# Mandatory under --yolo: nothing pauses to ask a human, and the
# material being processed is text written by strangers -- so a tool
# that writes files or runs commands is a path from a stranger's post
# to an action on this host.
DISALLOWED='run_terminal_command,read_file,list_dir,grep,search_replace,write,
spawn_subagent,kill_command_or_subagent,get_command_or_subagent_output,
scheduler_create,scheduler_delete,scheduler_list,workflow,
image_gen,image_edit,image_to_video,reference_to_video,Agent'

grok -p "$PROMPT" \
  --disallowed-tools "$DISALLOWED" \
  --yolo --output-format json \
  # ...and the capability that REMAINS is verified separately, by the
  # purity gate: a card citing no real post from the platform is
  # rejected. That positive check is what the allowlist never had.

External links

Exercise

Find a capability restriction in your systems — an allowlist, a scoped token, a sandbox policy — and write the positive test that proves the permitted capability still works. Run it. If you cannot easily write that test, note that you currently have no way to distinguish a working restriction from one that removed too much.
Hint
The positive test is usually harder to write than the negative one, which is exactly why it is missing. Proving something is forbidden needs only an attempt that fails; proving something is permitted needs a successful end-to-end use, which means real credentials and a real dependency. That difficulty is the reason the gap exists everywhere.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.