Back to skills
extension
Category: AI Agent CapabilitiesNo API key required

secure-agent

Locks down an AI agent by configuring platform-level tool restrictions (deniedTools) and Earl network egress rules. Use after Earl is working and templates are created, to make Earl's security guarantee enforceable rather than advisory.

personAuthor: jakexiaohubgithub

Secure Agent

After Earl is set up and working, this skill restricts the agent's ability to bypass Earl and make raw API/CLI calls directly. Without this step, CLAUDE.md is just a suggestion.

What This Does

  1. deniedTools: Blocks specific bash commands (curl, gh, stripe-cli, etc.) at the platform level for Claude Code. Agents cannot run these commands at all.
  2. Egress rules: Restricts which URLs Earl templates can contact, preventing Earl itself from being used as an open proxy.

Important Limitation

deniedTools pattern matching can be bypassed via alternative tools (python3 -c "import urllib...", node -e "fetch(...)", etc.). This blocks accidental or habitual CLI use — the common case. For stronger containment, pair with OS-level firewall rules or a network proxy.

Platform Support

| Platform | Mechanism | Hard restriction? | | ---------------------- | -------------------------------------------- | ------------------------------------------------------- | | Claude Code | deniedTools in .claude/settings.json | Yes — platform enforced | | Cursor | .cursor/mcp.json or Cursor settings UI | Partial — check Cursor docs for per-tool restrictions | | Windsurf | .windsurf/mcp.json or Windsurf settings UI | Partial — check Windsurf docs for per-tool restrictions | | Claude Desktop | No bash access | N/A | | Non-MCP CLI agents | CLAUDE.md instructions only | No — advisory only |

This skill primarily targets Claude Code. Instructions for other platforms are best-effort.


Step 1: Check Coverage

Only deny tools for services that have Earl templates. Denying a tool for a service with no Earl template would leave the agent unable to interact with that service at all.

earl templates list --json

Note which providers are covered. Map each to the CLI tools to deny:

| Earl template covers | Deny these tools | | --------------------- | ---------------------------------------------------------------- | | github | Bash(gh *), Bash(hub *) | | stripe | Bash(stripe *) | | slack | Bash(slack *) | | openai | Bash(openai *) | | vercel | Bash(vercel *) | | Any HTTP API template | Bash(curl *), Bash(wget *), Bash(http *), Bash(httpie *) | | Any SQL template | Bash(psql *), Bash(mysql *), Bash(sqlite3 *) | | Any gRPC template | Bash(grpcurl *) |

Do NOT deny Bash entirely. Earl's bash protocol and legitimate shell operations still need it.

Note on Bash(curl *) and Bash(wget *): Denying these also blocks all non-API curl uses — downloading binaries, health probes (curl http://localhost:8080/health), fetching install scripts, etc. If the agent legitimately needs curl for non-API tasks, add a narrow allowedTools override for those specific patterns, or use earl call instead for all HTTP operations.

Note on Bash(gh *): Denying gh also blocks all gh CLI uses that are not API calls: gh pr create, gh release upload, gh repo clone, branch management, etc. If the agent needs gh for repository operations that don't have Earl templates, add narrow exceptions or create templates for those commands before denying Bash(gh *).


Step 2: Generate and Present Denylist

Based on the covered providers, generate the deniedTools array. Show it to the user before applying anything:

"Based on your Earl templates, I'd add these restrictions to .claude/settings.json:

{
  "deniedTools": [
    "Bash(curl *)",
    "Bash(wget *)",
    "Bash(gh *)",
    "Bash(stripe *)"
  ]
}

This blocks the listed tools for this agent in this project. Other projects are unaffected. Shall I apply this?"

Do not apply until the user explicitly approves.


Step 3: Apply Denylist

For Claude Code: read .claude/settings.json, merge the deniedTools array (do not overwrite other keys), write it back.

If deniedTools already exists, merge arrays — do not duplicate entries.


Step 4: Verify

For Claude Code: Attempt to run a denied command:

curl https://example.com

Claude Code will refuse to run this command. The "tool denied" or "not allowed" error message from Claude Code is the success signal — it means the denylist is active. You cannot distinguish success from failure by looking at the exit code; look at whether Claude Code blocked it before the shell ran it.

If Claude Code runs curl without blocking it, the deniedTools pattern syntax is wrong. Check the format against current Claude Code documentation (search "deniedTools settings" in the Claude Code docs or at https://docs.anthropic.com/en/docs/claude-code) — the exact pattern syntax may vary by version. Then re-apply with the corrected format.

For other platforms: Ask the user to attempt a denied command manually in their agent session and confirm it is blocked.

Test that Earl still works:

earl templates list

Expected: succeeds and lists available templates (Earl is not in the denylist).


Step 5: Configure Egress Rules (Strongly Recommended)

Without egress rules, Earl is an open proxy — any HTTP template with a parameterized URL can reach any public endpoint. Add [[network.allow]] rules to ~/.config/earl/config.toml (macOS/Linux) or %APPDATA%\earl\config.toml (Windows) to restrict which hosts Earl templates can contact.

Note: Egress rules are global — they apply to all projects using this Earl install. Earl does not currently support per-project config files.

For each provider template, add a rule:

[[network.allow]]
hosts = ["api.github.com"]

[[network.allow]]
hosts = ["api.stripe.com"]

[[network.allow]]
hosts = ["api.slack.com", "slack.com"]

Security note on environments: If any template uses allow_environment_protocol_switching = true in its annotations, an environment override can silently switch protocols (e.g. from HTTP to bash). Review templates with this annotation carefully — a staging environment that switches to bash bypasses the HTTP egress rules above. Prefer vars.* for environment differences (e.g. different base URLs) over full protocol switching where possible.

After editing, verify:

earl doctor

Earl doctor checks that the config is valid. Any [[network.allow]] parse errors will be reported.


What This Does Not Cover

  • Bash templates: The bash protocol runs user-defined scripts in Earl's sandbox. The denylist does not restrict what Earl's own bash protocol can do. Ensure bash templates explicitly set sandbox.network = false unless network access is required.
  • Agents without per-tool restriction: For non-Claude-Code agents, the CLAUDE.md instruction is the only constraint. This is advisory, not enforced.
  • Alternative interpreters: Python, Node, Ruby, and other interpreters are not blocked by a curl-specific denylist. Pair with OS-level firewall rules for stronger containment.

Next Steps

  • Earl is now the enforced channel for all covered API calls
  • To add egress rules for a new provider: add [[network.allow]] blocks to ~/.config/earl/config.toml
  • If the agent is blocked from a call it needs: add an Earl template for that service, or remove the specific deny rule for that tool
  • If something breaks: invoke troubleshoot-earl