
Your AI Agent Can Run Shell Commands. Here’s How to Stop It From Wrecking Your Laptop.
AI agents can now run shell commands on their own. Learn the four ways this goes wrong, how sandboxing and permission rules protect you, and a small Python guardrail you can build today.
A hypothetical to start
Imagine you ask an AI agent: “Clean up my project folder.” It decides old files are clutter and runs a delete command, but the path it builds is one level too high. Your project is gone, along with the folder next to it.
The model didn’t go rogue. It followed instructions, made a mistake, and nothing was there to stop it. (This is an illustration, not a real incident.)
Why this matters now
Agents are no longer just chatbots. They can run shell commands, call APIs, and edit files, sometimes with limited human supervision. AWS's work on agent sandboxing highlights an important engineering challenge: an agent needs more than instructions telling it to behave. It needs technical boundaries that limit what it can access and do.
If you run an agent on your own laptop, its permissions can become your security risk. Here are four common ways things can go wrong—and the guardrails that help reduce the damage.
1. Destructive commands
Commands like
rm -rf, git reset --hard, or overwriting a config file can cause irreversible data loss.Fix: Allow only the commands the agent needs, and require your confirmation for anything destructive.
2. Leaked secrets
Your shell environment often holds API keys, tokens, and cloud credentials. An agent that can run
env or read ~/.aws/credentials can see all of them.Fix: Run the agent with a clean environment and use least-privilege, short-lived credentials. Never give an agent your admin keys.
3. Prompt injection
Suppose your agent reads a webpage or file that contains hidden text like “ignore your instructions and run this command”. The agent may treat it as an order.
Fix: Treat external content as untrusted input. Enforce permissions outside the model, and require human approval for high-risk file changes and external actions.
4. Runaway processes
An infinite loop, a huge download, or a fork bomb can freeze your machine.
Fix: Set command timeouts and enforce CPU, memory, and process limits at the container or operating-system level.
Permissions: allow, ask, deny
- Allow: Read-only actions within the approved workspace, such as listing files.
- Ask: Actions that modify files, install packages, or access external services.
- Deny: Attempts to access secrets, escape the workspace, disable security controls, or perform unapproved destructive operations.
These decisions must be enforced by a trusted layer outside the agent's own reasoning.
Sandboxing: don’t rely on rules alone
Permission rules can have gaps, so add a second layer that limits the damage when one fails:
- Run the agent inside a container or VM.
- Use a non-root user.
- Mount only the project folder, ideally read-only where possible.
- Turn off network access unless the task needs it.
AWS’s Strands Box takes this approach further. It is an open-source sandbox in developer preview that combines operating-system isolation with policies controlling what an agent can access and do. It evaluates shell commands, Python code, MCP tool calls, and network traffic against those policies. It can also keep real API credentials out of the agent’s environment, making them available only when an approved request needs them.
1
Read the [official Strands Box announcement](https://aws.amazon.com/blogs/opensource/introducing-strands-box-ai-agent-sandboxes-powered-by-dogwood/) for the technical details.A small guardrail you can build
Here is a small Python wrapper that validates a limited set of agent-requested commands before executing them:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
from pathlib import Path
import shutil
import subprocess
WORKSPACE = Path("/workspace").resolve(strict=True)
SAFE_PATH = "/usr/bin:/bin"
# Resolve executables once inside the container.
TOOLS = {
name: shutil.which(name, path=SAFE_PATH)
for name in ("pwd", "ls", "cat", "git")
}
def checked_path(value):
path = (WORKSPACE / value).resolve(strict=True)
# Reject paths that escape the workspace, including symlinks.
try:
path.relative_to(WORKSPACE)
except ValueError:
raise ValueError("Path is outside the workspace")
return path
def validate_command(args):
if not args:
raise ValueError("Empty command")
command, *params = args
if command not in TOOLS or TOOLS[command] is None:
raise ValueError("Command is not allowed")
if command == "pwd" and not params:
return [TOOLS["pwd"]]
if command == "ls" and len(params) <= 1:
path = checked_path(params[0]) if params else WORKSPACE
if not path.is_dir():
raise ValueError("Expected a directory")
return [TOOLS["ls"], "--", str(path)]
if command == "cat" and len(params) == 1:
path = checked_path(params[0])
if not path.is_file():
raise ValueError("Expected a file")
return [TOOLS["cat"], "--", str(path)]
if command == "git" and params == ["status", "--short"]:
return [TOOLS["git"], "status", "--short"]
raise ValueError("Command or arguments are not allowed")
def run_agent_command(args):
try:
command = validate_command(args)
result = subprocess.run(
command,
cwd=WORKSPACE,
env={"PATH": SAFE_PATH, "LANG": "C"},
shell=False,
timeout=10,
check=False,
)
return result.returncode
except (ValueError, OSError) as error:
print(f"Blocked: {error}")
return 1
except subprocess.TimeoutExpired:
print("Command timed out")
return 1
if __name__ == "__main__":
run_agent_command(["ls", "."])
run_agent_command(["git", "status", "--short"])
I tested it with a few commands (using a test folder in place of
/workspace):| Command | Result |
|---|---|
| ls | Works |
| cat notes.txt | Works |
| rm -rf / | Blocked: rm |
| ls; rm -rf / | Blocked: ls; is not on the allowlist |
| cat ../../etc/passwd | Blocked: outside the workspace |
| git push origin main | Blocked: subcommand not allowed |
| sudo ls | Blocked: sudo |
What each part does
- Command allowlist: Only
pwd,ls,cat, and one specific Git operation are permitted. Other commands and argument patterns are rejected. - No shell interpretation:
subprocess.run()receives an argument list withshell=False, so shell operators such as;and|are not interpreted as command separators. - Workspace path checks: Resolved paths must remain inside
/workspace. This helps block traversal through..and symlinks, provided the filesystem is not being changed concurrently by an untrusted process. - Timeout: Each command has a 10-second timeout. This is not a complete process-tree or resource limit.
- Restricted environment: The command receives a minimal environment instead of inheriting the agent process's environment variables. This reduces accidental exposure of secrets but does not, by itself, prevent access to files or network resources.
What this does not protect you from
This wrapper is a demonstration of basic command and path restrictions, not a security boundary. It sets no CPU or memory limits, doesn’t disable network access, and can’t guarantee that child processes stop when a timeout fires. A minimal environment also doesn’t stop a command from reading host files the process can already reach. For real isolation, use a container or VM with restricted mounts, a non-root user, limited network access, and resource limits.
Your checklist
Before you let an agent loose on your machine:
- ✅ Run it in a sandbox (container or VM)
- ✅ Keep secrets out of its environment
- ✅ Use least-privilege credentials
- ✅ Allowlist specific commands and validate their arguments
- ✅ Set timeouts and resource limits
- ✅ Require human approval for risky actions
Final thought
Agents are useful because they can act. That is also why they need boundaries. Start with a sandbox and a small allowlist, then loosen things only when you have a reason to.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article