Why Security Matters for Agent Skills
AI agents execute code based on unpredictable LLM outputs. A single overlooked vulnerability can lead to:
- Data exfiltration - Leaked API keys, passwords, or PII
- Remote code execution (RCE) - Attackers running arbitrary commands
- Denial of service - Resource exhaustion or infinite loops
- Privilege escalation - Agents accessing restricted systems
At AgentSkillsHub.dev, 23% of scanned skills contain at least one critical vulnerability. Don't be part of that statistic.
The 10 Security Commandments
1. Never Trust LLM Output
LLMs can be manipulated via prompt injection. Always validate inputs:
# BAD
def delete_file(path: str):
os.remove(path) # No validation!
# GOOD
import os.path
def delete_file(path: str):
# Whitelist allowed directory
allowed_dir = "/workspace/temp"
abs_path = os.path.abspath(path)
if not abs_path.startswith(allowed_dir):
raise ValueError("Path outside allowed directory")
if not os.path.exists(abs_path):
raise FileNotFoundError("File does not exist")
os.remove(abs_path)
2. Use Environment Variables for Secrets
Never hardcode API keys, passwords, or tokens:
# BAD
OPENAI_KEY = "sk-proj-abc123..."
# GOOD
import os
OPENAI_KEY = os.environ.get('OPENAI_KEY')
if not OPENAI_KEY:
raise EnvironmentError("OPENAI_KEY not set")
3. Pin All Dependencies
Prevent typosquatting and supply chain attacks:
# requirements.txt
requests==2.31.0
pydantic==2.5.3
# NOT: requests>=2.0 (too broad)
4. Sandbox File System Access
Restrict file operations to a specific directory:
import os.path
SANDBOX_DIR = "/workspace"
def is_safe_path(path: str) -> bool:
abs_path = os.path.abspath(path)
return abs_path.startswith(SANDBOX_DIR)
def read_file(path: str) -> str:
if not is_safe_path(path):
raise PermissionError("Access denied")
with open(path, 'r') as f:
return f.read()
5. Avoid Shell Execution
Never use os.system() or subprocess with shell=True:
# BAD
os.system(f"cat {filename}")
# GOOD
import subprocess
subprocess.run(["cat", filename], shell=False, capture_output=True)
6. Rate Limit External API Calls
Prevent abuse and cost overruns:
import time
from collections import deque
class RateLimiter:
def __init__(self, max_calls: int, period: int):
self.max_calls = max_calls
self.period = period
self.calls = deque()
def allow_request(self) -> bool:
now = time.time()
# Remove old calls
while self.calls and self.calls[0] < now - self.period:
self.calls.popleft()
if len(self.calls) < self.max_calls:
self.calls.append(now)
return True
return False
limiter = RateLimiter(max_calls=10, period=60) # 10 calls per minute
7. Log Security Events
Track suspicious activity for auditing:
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def sensitive_operation(user_id: str):
logger.info(f"Sensitive operation by user {user_id}")
# ... operation logic
8. Implement Timeouts
Prevent infinite loops and resource exhaustion:
import requests
def fetch_data(url: str):
response = requests.get(url, timeout=5) # 5 second timeout
return response.json()
9. Validate Output Before Returning
Don't accidentally leak sensitive data:
def sanitize_output(data: dict) -> dict:
# Remove sensitive keys
sensitive_keys = ['password', 'api_key', 'token', 'secret']
return {k: v for k, v in data.items() if k not in sensitive_keys}
10. Test with Adversarial Inputs
Try to break your own skill:
# Test cases
test_inputs = [
"../../../etc/passwd", # Path traversal
"'; DROP TABLE users;--", # SQL injection
"__import__('os').system('rm -rf /')", # Code injection
"A" * 1000000, # Resource exhaustion
]
for malicious_input in test_inputs:
try:
your_function(malicious_input)
except Exception as e:
print(f"Blocked: {e}")
Security Checklist
Before publishing your skill:
- [ ] No hardcoded secrets
- [ ] All inputs validated
- [ ] Dependencies pinned
- [ ] File access sandboxed
- [ ] No shell=True in subprocess
- [ ] API rate limiting implemented
- [ ] Timeouts on all network requests
- [ ] Security events logged
- [ ] Output sanitized
- [ ] Tested with adversarial inputs
Automated Security Scanning
Use our free security scanner to catch vulnerabilities: