Currently supported versions for security updates:
| Version | Supported |
|---|---|
| 0.1.x | ✅ |
We take the security of Graphton seriously. If you believe you have found a security vulnerability, please report it to us responsibly.
DO NOT open a public GitHub issue for security vulnerabilities.
Instead, please email security details to:
Email: security@planton.ai
Include in your report:
- Description of the vulnerability
- Steps to reproduce the issue
- Potential impact of the vulnerability
- Suggested fix (if you have one)
- Your contact information for follow-up
- Acknowledgment: Within 48 hours of your report
- Initial Assessment: Within 5 business days
- Status Updates: Regular updates on progress
- Resolution: We aim to resolve confirmed vulnerabilities within 30 days
- We will investigate and validate the report
- We will develop and test a fix
- We will release a security patch
- We will credit the reporter (unless they wish to remain anonymous)
- We will publish a security advisory
# Use environment variables
import os
from graphton import create_deep_agent
agent = create_deep_agent(
model="claude-sonnet-4.5",
system_prompt="...",
)
# API key is loaded from environment by LangChain
# ANTHROPIC_API_KEY=... python script.py# Use secrets management
import boto3
def get_secret(secret_name):
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=secret_name)
return response['SecretString']
os.environ['ANTHROPIC_API_KEY'] = get_secret('anthropic-key')# Use .env files (not committed to git)
from dotenv import load_dotenv
load_dotenv() # Loads from .env file
# .gitignore should include:
# .env
# .env.local# NEVER hardcode API keys
agent = create_deep_agent(
model="claude-sonnet-4.5",
system_prompt="...",
)
os.environ['ANTHROPIC_API_KEY'] = "sk-ant-hardcoded-key" # DON'T DO THIS!# NEVER commit API keys to git
# Bad: config.py
ANTHROPIC_API_KEY = "sk-ant-..." # DON'T DO THIS!# NEVER log API keys
import logging
logging.info(f"Using key: {os.getenv('ANTHROPIC_API_KEY')}") # DON'T DO THIS!# ✅ Good: Per-user tokens
agent = create_deep_agent(
model="claude-sonnet-4.5",
system_prompt="...",
mcp_servers={
"api": {
"url": "https://api.example.com",
"headers": {
"Authorization": "Bearer {{USER_TOKEN}}" # Template variable
}
}
},
mcp_tools={"api": ["list_resources"]},
)
# Each user gets their own token
result = agent.invoke(
{"messages": [...]},
config={
"configurable": {
"USER_TOKEN": get_user_token(user_id) # Per-user
}
}
)# ⚠️ Use only when appropriate
# (shared credentials, internal services, etc.)
agent = create_deep_agent(
model="claude-sonnet-4.5",
system_prompt="...",
mcp_servers={
"api": {
"url": "https://api.example.com",
"headers": {
"X-API-Key": os.getenv("API_KEY") # From environment, not hardcoded
}
}
},
mcp_tools={"api": ["list_resources"]},
)# ✅ Good: Validate and sanitize input
def safe_invoke(agent, user_input):
# Validate input length
if len(user_input) > 10000:
raise ValueError("Input too long")
# Sanitize input
user_input = user_input.strip()
# Check for obvious injection attempts
if any(pattern in user_input for pattern in ["{{", "}}", "${"]):
raise ValueError("Invalid input pattern")
return agent.invoke({
"messages": [{"role": "user", "content": user_input}]
})# ✅ Good: Don't allow user input in templates
mcp_servers = {
"api": {
"url": "https://api.example.com",
"headers": {
"Authorization": f"Bearer {{USER_TOKEN}}" # Fixed template
}
}
}
# ❌ Bad: User input in template
user_server_url = request.get("server_url") # DON'T DO THIS!
mcp_servers = {
"api": {
"url": user_server_url, # DANGEROUS!
}
}# ✅ Good: Implement rate limiting
from functools import wraps
from time import time
def rate_limit(max_calls, period):
calls = []
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
now = time()
# Remove old calls
calls[:] = [call for call in calls if call > now - period]
if len(calls) >= max_calls:
raise Exception("Rate limit exceeded")
calls.append(now)
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(max_calls=10, period=60) # 10 calls per minute
def invoke_agent(agent, message):
return agent.invoke({"messages": [{"role": "user", "content": message}]})# ✅ Good: Don't leak sensitive info in errors
try:
result = agent.invoke(
{"messages": [...]},
config={
"configurable": {
"USER_TOKEN": user_token
}
}
)
except Exception as e:
# Log full error internally
logger.error(f"Agent invocation failed: {str(e)}", exc_info=True)
# Return generic error to user
return {"error": "An error occurred processing your request"}
# ❌ Bad: Exposing internal details
except Exception as e:
return {"error": str(e)} # May contain tokens, URLs, etc.# Check for security vulnerabilities
pip install safety
safety check
# Or with Poetry
poetry show --outdated# Update dependencies regularly
pip install --upgrade git+https://github.com/plantoncloud/graphton.git
# Or with Poetry
poetry update graphton# pyproject.toml
[tool.poetry.dependencies]
graphton = {git = "https://github.com/plantoncloud/graphton.git", tag = "v0.1.0"}# requirements.txt
git+https://github.com/plantoncloud/graphton.git@v0.1.0
# ✅ Good: HTTPS URLs
mcp_servers = {
"api": {
"url": "https://api.example.com", # HTTPS
}
}
# ❌ Bad: HTTP URLs (unless local dev)
mcp_servers = {
"api": {
"url": "http://api.example.com", # Insecure!
}
}# Default behavior is to verify SSL
# Only disable for local development
# ⚠️ Development only
import os
if os.getenv("ENV") == "development":
# Disable SSL verification (dev only!)
pass# Kubernetes: Use secrets
apiVersion: v1
kind: Secret
metadata:
name: api-keys
type: Opaque
data:
ANTHROPIC_API_KEY: <base64-encoded-key>
PLANTON_API_KEY: <base64-encoded-key># Reference in deployment
env:
- name: ANTHROPIC_API_KEY
valueFrom:
secretKeyRef:
name: api-keys
key: ANTHROPIC_API_KEY# Kubernetes: Network policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: graphton-agent
spec:
podSelector:
matchLabels:
app: graphton-agent
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
name: allowed-namespace
- to:
- podSelector:
matchLabels:
app: allowed-service
- to:
# Allow external MCP servers
ports:
- port: 443
protocol: TCP# ✅ Good: Load only necessary tools
mcp_tools = {
"planton-cloud": [
"list_organizations", # Only what's needed
]
}
# ❌ Bad: Loading all tools
mcp_tools = {
"planton-cloud": [
"list_organizations",
"create_cloud_resource", # Not needed? Don't load!
"delete_cloud_resource", # Dangerous if not needed
# ... many more tools
]
}import logging
logger = logging.getLogger(__name__)
# Log security-relevant events
logger.info(f"Agent invoked by user {user_id}")
logger.warning(f"Rate limit exceeded for user {user_id}")
logger.error(f"Authentication failed for user {user_id}")
# DON'T log sensitive data
# logger.info(f"Token: {user_token}") # DON'T DO THIS!# Monitor usage patterns
def monitor_usage(user_id, action):
# Track API calls per user
# Alert on unusual patterns
# Block suspicious activity
passSecurity Model:
- Template variables are substituted at runtime
- No arbitrary code execution
- Simple string replacement only
Risks:
- User input should never be used directly as template values without validation
- Template syntax itself should not be exposed to users
Mitigations:
- Validate all input
- Use fixed template patterns
- Don't allow user-defined templates
Security Model:
- Tools are loaded from configured MCP servers only
- Tool names must be explicitly listed
- No arbitrary tool loading
Risks:
- MCP server compromise could provide malicious tools
- Tool invocation with malicious parameters
Mitigations:
- Only connect to trusted MCP servers
- Use HTTPS for all connections
- Validate tool parameters
- Implement rate limiting
Security Model:
- Agent runs within LangGraph framework constraints
- Recursion limit prevents infinite loops
- Tools are sandboxed by MCP protocol
Risks:
- Excessive API usage (cost)
- Long-running operations
- Resource exhaustion
Mitigations:
- Set appropriate recursion limits
- Implement timeouts
- Monitor resource usage
- Rate limit invocations
If handling EU user data:
- Data Minimization: Only collect necessary data
- Purpose Limitation: Use data only for stated purposes
- Storage Limitation: Delete data when no longer needed
- Right to Erasure: Provide mechanism to delete user data
- Data Portability: Allow users to export their data
For SOC 2 compliance:
- Access Controls: Implement role-based access
- Audit Logging: Log all security-relevant events
- Encryption: Encrypt data in transit and at rest
- Incident Response: Have a security incident response plan
- Vendor Management: Assess security of dependencies
Before deploying to production:
- API keys stored securely (not hardcoded)
- Environment variables or secrets management used
- Input validation implemented
- Rate limiting configured
- Error messages don't leak sensitive info
- HTTPS used for all external connections
- Dependencies pinned to specific versions
- Security scanning tools integrated (safety, bandit)
- Logging configured (without sensitive data)
- Monitoring and alerting set up
- Backup and recovery procedures documented
- Security review completed
- Penetration testing performed (if applicable)
- OWASP Top 10: https://owasp.org/www-project-top-ten/
- LangChain Security: https://python.langchain.com/docs/security
- MCP Security: Consult MCP server documentation
For security concerns or questions:
- Email: security@planton.ai
- GitHub Security Advisories: https://github.com/plantoncloud/graphton/security/advisories
This security policy is reviewed quarterly and updated as needed. Last updated: November 2025.