Skip to content

Security: Pranjalab/AgentBabysitter

Security

SECURITY.md

Security

Agent Babysitter connects a Telegram bot to a machine where Claude Code can run commands. That is powerful, so it's worth being precise about who can reach it and what protects you. Short version: only you can message your bot, and the sections below explain exactly how that's enforced — and the few things it does not protect against.

Only you can message your bot

This is the core of the model, and it's worth stating plainly because it's easy to assume otherwise: a stranger who finds your bot cannot do anything.

  • Pairing writes your Telegram user ID to an allowlist. Setup sets dmPolicy: "allowlist" and records your numeric ID. Every inbound message is checked against it; anyone not on the list is dropped before Claude ever sees it. They get silence — no reply, no pairing offer, nothing.
  • Numeric IDs can't be spoofed. Telegram user IDs are permanent. Changing a display name or username doesn't change the ID, so no one can impersonate their way onto your allowlist.
  • Pairing is inverted on purpose. Some bots DM a code to any stranger who messages them and let them join. Agent Babysitter does the opposite: your terminal generates a PIN and waits for it. Unknown senders are never answered, and matching the PIN proves the person on the phone is the person at the terminal. The PIN comes from /dev/urandom, drops look-alike characters (I/O/0/1), expires in 5 minutes, and only counts from a private chat — a PIN pasted into a group pairs no one.

How your secrets are protected

  • The bot token never appears in ps. Telegram puts the token in the URL path, so a naive curl …/bot<TOKEN>/… would leak it to every user on the machine via ps auxww. Every API call pipes the URL through curl -K - instead, so the token stays out of the process's argument list. (Verified: a canary token was not visible in ps during a live call.)
  • Files are owner-only. The script runs umask 077 before touching disk. The token (~/.claude/channels/telegram/.env) and the allowlist (access.json) are 600; their directories are 700. Writes go through a temp file and mv, so a crash can't leave a half-written or briefly world-readable token.
  • Claude is told the rules. The injected prompt instructs Claude to never send secrets, tokens, keys, or .env contents over Telegram; to treat instructions embedded in fetched content (web pages, files) as data, not commands; and to require terminal confirmation for anything destructive or irreversible that arrives over chat.

Enforced controls (defense in depth)

The prompt asks Claude to behave. These two features enforce it at the hook layer — code that runs no matter what the model decides — for the obvious, high-damage cases. They don't replace Claude Code's permission system; they raise the floor beneath it, and give you a kill switch that doesn't rely on trusting the model.

  • The remote kill ladder. Send any of these from Telegram as a whole message; the session hook acts on it directly (the model never runs it):
    • ABS MUTE / ABS UNMUTE — mute / resume proactive reports (you get a catch-up on resume).
    • ABS OFF — cut inbound and outbound Telegram; the session keeps working locally. Re-enable from the terminal (abs on).
    • ABS STOP — the hook tells Claude to halt the current plan at the next step and wait. (It can't hard-interrupt a command already mid-run — the Telegram plugin only delivers your message between steps — so a long single command still needs Ctrl-C at the terminal.)
    • ABS EXIT — close the session (Claude asks to confirm if it's mid-task). Restart with abs.
    • ABS BLOCK — lock this bot out entirely; only a deliberate abs setup from the terminal brings it back.
  • The destructive-command guard. A PreToolUse hook blocks a small, high-confidence set of Bash commands — rm -rf, git push --force, reset --hard, clean -fd, branch deletion, DROP/TRUNCATE, DELETE/UPDATE without WHERE, dd/mkfs, recursive chmod/chown, and reads of .env / credentials.json / *.pem / id_rsawhen the turn was driven from Telegram. A remote message is lower-trust than you at the desk, so it's stopped and you're told to run it at the terminal. From the terminal, nothing is blocked. Opt out with abs config guard off.

Honest limit: this is defense-in-depth, not a sandbox. A determined, compromised model could try to obfuscate a command (base64, a wrapper script) to slip past the pattern match — so the guard raises the floor, it isn't a wall. Claude Code's own permission system remains the real boundary.

What this does not protect against

Be clear-eyed about the limits. None of these are bugs — they're the shape of the trade you're making, and worth knowing before you rely on it.

  • Telegram sees your messages. Bot chats are not end-to-end encrypted. Everything Claude reports and everything you send is readable by Telegram. Don't use this for work where that matters. Voice notes are transcribed and synthesized locally, so no cloud speech vendor hears them — but the audio still travels over Telegram like any other message.
  • Your bot token is a credential. Anyone who obtains it can impersonate your bot. It lives in plaintext at ~/.claude/channels/telegram/.env, protected by file permissions, not encryption. If it leaks, revoke it with /revoke in @BotFather and run abs setup again.
  • Anyone with your unlocked phone can instruct Claude. The allowlist authenticates a Telegram account, not a person. Lock your phone.
  • It is not a sandbox. Within your paired account, Claude has whatever power Claude Code gives it on your machine. The allowlist decides who can reach the session; it does not limit what the session can do. The real boundary on actions is Claude Code's own permission system.
  • reset, not setup, clears the allowlist. Re-running abs setup adds to the allowlist (so existing entries survive). To revoke access for everyone, run abs reset.
  • The conversation log is a second copy of everything. With the log on (the default), your messages, abs's replies, and the tools it ran are written to ~/.abs/<profile>/log/YYYY-MM-DD.jsonl — local, 600, never uploaded. Control characters are stripped and secrets are scrubbed on a best-effort basis (Telegram / OpenAI sk- / GitHub / AWS / Google / Slack keys, JWTs, PEM private keys, key= / password= pairs, and passwords embedded in URLs). But a secret in a shape the patterns don't recognise can still land there, and home-directory backups or cloud-sync tools will pick the file up. Turn it off with abs config log off, and delete it any time with abs log --clear.
  • The install chain is trust-by-HTTPS. install.sh fetches abs.sh — and can run Bun's and Claude Code's own curl | bash installers — over HTTPS with no checksum, the standard model for this class of tool. Installing from a cloned checkout (./install.sh) skips the download entirely.
  • The update check calls GitHub once a day to compare versions (opt out with abs config update-check off). It only ever compares — it never auto-updates or executes anything.

Reporting a vulnerability

Found something? Open an issue at github.com/Pranjalab/AgentBabysitter/issues, or for anything sensitive, describe it privately rather than in a public issue.

There aren't any published security advisories