# 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.

- Skill: `diegosouzapw/secure-agent` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add diegosouzapw/secure-agent`
- Raw SKILL.md: https://api.skillmd.com/api/skills/diegosouzapw/secure-agent/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: diegosouzapw (https://skillmd.com/u/diegosouzapw)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/diegosouzapw/secure-agent

---


# 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.

```bash
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`:
>
> ```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:

```bash
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:

```bash
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:

```toml
[[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:

```bash
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`

