Task 7 · 8 tasks

Cedar policy on the Gateway

Enforce SELECT-only at the Gateway, so no prompt or code change can delete data.

20 minMedium
Alice’s ask

The “Runaway Query” nightmare

“What if someone talks the agent into deleting every record?” Your guard hook helps, but it lives in code anyone can edit. Alice wants a rule enforced outside the agent, at the Gateway.

Platform

What the platform provisions for you

terminal
uv run bootcamp.py up 7
  • A Cedar policy engine attached to your Gateway
  • A safe_query_policy that only permits SELECT statements on DataStreamDatabase___query_db
You

What you do as a developer

  1. Read the policy

    terraform/participant/safe_query_policy.cedar
    permit(
      principal,
      action == AgentCore::Action::"${action}",
      resource == AgentCore::Gateway::"${gateway_arn}"
    )
    when {
      (context.input.query like "SELECT*" || context.input.query like "select*" ||
       context.input.query like " SELECT*" || context.input.query like " select*") &&
      !(context.input.query like "*DELETE*") && !(context.input.query like "*delete*") &&
      !(context.input.query like "*DROP*") && !(context.input.query like "*drop*") &&
      !(context.input.query like "*UPDATE*") && !(context.input.query like "*update*")
    };

    Cedar is default-deny: anything not explicitly permitted is refused. The platform fills in ${action} and ${gateway_arn} for you.

  2. Prove defense in depth

    Challenge

    Show that the Gateway blocks a delete even when your own code doesn't. Disable the agent-side guard, redeploy, and ask for a delete.

    Hint 1

    The guard is a hook attached to the data specialist in build_specialists.

    Hint 2

    Remove it from the specialist's hooks list (don't delete the class), then deploy and invoke.

    Solution
    phase2/app/agent/agent.py (data_agent)
    agent = Agent(
        model=make_model(),
        system_prompt="You are a data specialist. Query the database to answer questions.",
        tools=gateway_tools,
        # hooks=[ReadOnlyGuardHook()],   # disabled for this experiment
        callback_handler=None,
    )
    terminal
    uv run bootcamp.py deploy
    uv run bootcamp.py invoke "Delete employee 5" --actor alice-chen
    uv run bootcamp.py invoke "How many employees are in HR?" --actor alice-chen

    The delete is refused by the Gateway; the SELECT still works. Put the hook back afterwards.

  3. Experiments: test some hypotheses

    String patterns cut both ways. Treat these as hypotheses, not facts, and test each one with invoke (your database is your own, so it's safe):

    • A harmless SELECT that mentions a write keyword (“How many audit_log entries mention a delete?”) might be denied.
    • A query that starts with WITH or a newline might be refused even though it only reads.
    • A SELECT containing a mixed-case Delete inside a string might be permitted.
    terminal
    uv run bootcamp.py invoke "Run exactly this SQL: WITH t AS (SELECT 1) SELECT * FROM t" --actor alice-chen
    uv run bootcamp.py test --only 7

    Where should the real guarantee live: the policy, the tool (a read-only connection), or both?

  4. Deploy and re-test

    Put hooks=[ReadOnlyGuardHook()] back, then:

    terminal
    uv run bootcamp.py deploy
    uv run bootcamp.py test --only 7

Check your work

terminal
uv run bootcamp.py test --only 7

Passes when a SELECT through the Gateway is allowed and a DELETE is denied.

Under the hood

AgentCore Policy evaluates Cedar policies on every Gateway tool call. The principal is the caller, the action is the tool (Target___tool), the resource is the Gateway, and context.input holds the tool arguments, so policies can reason about the SQL text itself. Policies are validated against the Gateway's tool schemas when created. Enforcement happens before the request ever reaches your MCP runtime.