Security Guide

Best Practices for Building Secure Agent Skills [2026 Security Guide]

Learn the essential security principles for building safe AI agent skills. From input validation to secret management, protect your users and systems.

2026-02-059 min readSecurity Team

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:

Scan your skill now ->

Additional Resources

Need high-signal skills faster?

Use our directory view to filter by safety grade, workflow fit, and usage popularity, then continue to GitHub if the exact keyword is not indexed on-site yet.