This document provides security guidelines for handling LangChain Hub path loading to prevent directory traversal attacks.
Vulnerability: Directory traversal in load_chain, load_prompt, and load_agent functions when an attacker controls the path parameter.
Affected Versions:
langchain <= 0.1.10langchain-core < 0.1.29
Patched Versions:
langchain >= 0.1.11langchain-core >= 0.1.29
Impact:
- High: Disclosure of API keys for LLM services
- Critical: Remote Code Execution (RCE)
- Bypasses intended behavior of loading only from
hwchase17/langchain-hubGitHub repository
Attack Vector:
# Vulnerable code
load_chain("lc://chains/../../something") # Directory traversal
load_chain(user_input) # If user_input contains ../ sequences✅ This codebase is NOT affected
- Current version:
langchain-core>=1.2.6(well above patched version 0.1.29+, includes CVE-2025-68664 fix) - No usage: The codebase does NOT use
load_chain,load_prompt, orload_agentfunctions - No LangChain Hub: The codebase does NOT load chains from LangChain Hub (
lc://paths) - Safe usage: Only uses
langchain_core.messagesandlangchain_core.toolswhich are safe
The security module includes path validation utilities:
from src.api.services.mcp.security import validate_chain_path, safe_load_chain_path
# Validate a path
is_valid, reason = validate_chain_path("lc://chains/my_chain", allow_lc_hub=True)
if not is_valid:
raise SecurityError(f"Invalid path: {reason}")
# Use allowlist mapping (recommended)
ALLOWED_CHAINS = {
"summarize": "lc://chains/summarize",
"qa": "lc://chains/qa",
}
safe_path = safe_load_chain_path("summarize", ALLOWED_CHAINS)The following patterns are automatically blocked:
../- Directory traversal (Unix/Linux)..\\- Directory traversal (Windows)..- Any parent directory reference/- Absolute paths (Unix)C:- Absolute paths (Windows drive letters)\\- UNC paths (Windows network)
Even with patched versions, implement additional protections:
A. Use Allowlist Mapping
# ❌ DON'T: Direct user input
chain = load_chain(user_input) # Vulnerable to path traversal
# ✅ DO: Use allowlist
ALLOWED_CHAINS = {
"summarize": "lc://chains/summarize",
"qa": "lc://chains/qa",
"chat": "lc://chains/chat",
}
def safe_load_chain(user_chain_name: str):
if user_chain_name not in ALLOWED_CHAINS:
raise ValueError(f"Chain '{user_chain_name}' not allowed")
hub_path = ALLOWED_CHAINS[user_chain_name]
# Additional validation
from src.api.services.mcp.security import validate_chain_path
is_valid, reason = validate_chain_path(hub_path, allow_lc_hub=True)
if not is_valid:
raise SecurityError(f"Invalid path: {reason}")
return load_chain(hub_path)B. Reject Traversal Tokens
def validate_chain_path(path: str) -> bool:
"""Validate chain path before loading."""
# Check for traversal patterns
if ".." in path:
raise ValueError("Directory traversal detected")
if path.startswith(("/", "\\")):
raise ValueError("Absolute paths not allowed")
# Only allow lc:// hub paths
if not path.startswith("lc://"):
raise ValueError("Only lc:// hub paths allowed")
return TrueC. Pin Hub Assets
If loading from LangChain Hub in production:
# Pin to specific commit/ref to prevent loading attacker-modified configs
chain = load_chain(
"lc://chains/my_chain",
hub_ref="abc123def456" # Specific commit hash
)D. Treat Loaded Configs as Untrusted
Remember: Chain configs can include:
- Tool definitions
- Prompt templates
- Model settings
- API keys (if stored in config)
Prefer:
- Internal "known good" registry
- Signed artifacts
- Bundling chains with your app
If you need to load chains from LangChain Hub:
# Minimum secure versions
pip install "langchain>=0.1.11" "langchain-core>=0.1.29"# Define allowed chains
ALLOWED_CHAINS = {
"summarize": "lc://chains/summarize",
"qa": "lc://chains/qa",
}
# Use allowlist function
from src.api.services.mcp.security import safe_load_chain_path
def load_user_chain(chain_name: str):
"""Safely load a chain using allowlist."""
hub_path = safe_load_chain_path(chain_name, ALLOWED_CHAINS)
return load_chain(hub_path)from src.api.services.mcp.security import validate_chain_path
def load_chain_safely(path: str):
"""Load chain with path validation."""
is_valid, reason = validate_chain_path(path, allow_lc_hub=True)
if not is_valid:
raise SecurityViolationError(f"Invalid path: {reason}")
return load_chain(path)import logging
from datetime import datetime
logger = logging.getLogger("security")
def log_chain_load(chain_name: str, hub_path: str, user: str):
"""Log all chain loading attempts for audit."""
logger.warning(
f"CHAIN_LOAD: user={user}, "
f"chain_name={chain_name}, "
f"hub_path={hub_path}, "
f"timestamp={datetime.utcnow().isoformat()}"
)- Always use allowlist mapping for user-provided chain names
- Validate all paths before passing to
load_chain - Pin hub assets to specific commits/refs in production
- Treat loaded configs as untrusted and validate them
- Log all chain loading attempts for audit
- Use internal registries instead of external hubs when possible
- Bundle chains with your app for production deployments
- Never pass raw user input directly to
load_chain - Never trust paths from external sources
- Never skip path validation even with patched versions
- Never load chains dynamically based on user input without allowlist
- Never store chain paths in user-editable databases without validation
- Never allow LLMs to dynamically decide which
lc://path to load - Never skip audit logging for chain loading
# ❌ VULNERABLE
user_chain = request.json["chain_name"] # User input: "lc://chains/../../secrets"
chain = load_chain(user_chain) # Loads from wrong location# ❌ VULNERABLE
chain_path = db.get_chain_path(user_id) # User can edit: "../malicious"
chain = load_chain(chain_path)# ❌ VULNERABLE
llm_response = llm.generate("Which chain should I load?")
chain_path = extract_path(llm_response) # LLM suggests: "lc://chains/../../evil"
chain = load_chain(chain_path)# ✅ SECURE
ALLOWED_CHAINS = {
"summarize": "lc://chains/summarize",
"qa": "lc://chains/qa",
}
user_chain = request.json["chain_name"]
if user_chain not in ALLOWED_CHAINS:
raise ValueError("Chain not allowed")
hub_path = ALLOWED_CHAINS[user_chain]
chain = load_chain(hub_path)Set up monitoring for:
- Chain loading attempts: Alert on any
load_chaincalls - Path validation failures: Monitor for traversal attempts
- Suspicious patterns: Detect
../sequences in paths - Unusual chain names: Alert on chains not in allowlist
- Failed validations: Monitor security violations
If path traversal is detected:
- Immediately disable chain loading functionality
- Review audit logs to identify attack vector
- Assess impact: Check for API key exposure, config access
- Rotate credentials: Change all API keys, tokens
- Patch immediately: Upgrade to secure versions
- Review security controls: Strengthen path validation
- Document incident: Create post-mortem report
For security concerns, contact the security team or refer to SECURITY.md.