The Vulnerability That Hid in Your AI Observability Layer
When teams add LangSmith to their LLM applications for tracing and observability, they're adding middleware that sits directly in the request path. That's a privileged position — and in LangSmith SDK versions prior to 0.8.18, it turned out to be an exploitable one.
Security advisory GHSA-f4xh-w4cj-qxq8 describes a high-severity arbitrary server-side file read in LangSmith SDK's TracingMiddleware. The vulnerability meant that a crafted request could cause the middleware to read files from the server's local filesystem — files the requester was never meant to see. This post walks through what went wrong, how it was fixed, and what Python developers building on LangChain/LangSmith should understand about middleware-level security.
The Vulnerability Explained
What Is TracingMiddleware Doing?
LangSmith's TracingMiddleware is an ASGI/WSGI-compatible middleware layer that intercepts HTTP requests to log traces of LLM interactions. Because it processes incoming request data — headers, body content, route parameters — before passing control downstream, it handles attacker-influenced input very early in the request lifecycle.
The vulnerability is a path traversal / arbitrary file read (CWE-22). In the affected versions (≤ 0.8.15), the middleware processed certain input values and used them — directly or indirectly — in file operations without adequately restricting the resulting filesystem path.
The Classic Path Traversal Pattern
A path traversal vulnerability typically looks like this in Python:
# VULNERABLE — user-controlled value used directly in file open
def read_trace_artifact(artifact_name: str) -> str:
base_dir = "/app/traces"
path = os.path.join(base_dir, artifact_name) # ← no canonicalization
with open(path, "r") as f:
return f.read()
If artifact_name is ../../../../etc/passwd, os.path.join produces /etc/passwd — completely outside /app/traces. Without a check that the resolved path still starts with the intended base directory, the application reads any file the server process can access.
In LangSmith's case, the TracingMiddleware component was the entry point where tainted request data could reach such file-read logic.
Real-World Impact for LangSmith Users
Applications using LangSmith for tracing are often:
- Deployed in cloud environments with secrets mounted as files (e.g.,
/run/secrets/db_password, Kubernetes secret volumes) - Running alongside
.envfiles containing API keys and database credentials - Hosting model weight files or proprietary prompt templates on the local filesystem
An attacker exploiting this vulnerability against a production LangSmith-instrumented application could potentially exfiltrate any of these resources by sending a crafted tracing request. Because TracingMiddleware is designed to receive external HTTP traffic, the attack surface is directly internet-exposed in many deployments.
The Fix
Upgrading from 0.8.15 to 0.8.18
The remediation is a targeted version upgrade. The poetry.lock diff shows the transition:
Before (vulnerable):
# poetry.lock — langsmith pinned at 0.8.15
name = "langsmith"
version = "0.8.15"
After (fixed):
# poetry.lock — langsmith upgraded to 0.8.18
name = "langsmith"
version = "0.8.18"
LangSmith 0.8.18 patches the TracingMiddleware to sanitize path-like values before they can reach any file-open operation. The fix in the upstream SDK ensures that user-influenced input is validated against an allowlist or canonicalized and checked against a restricted base path — preventing traversal sequences like ../ from escaping the intended directory.
What Else Changed in the Lock File?
The poetry.lock diff also reveals that several previously optional dependencies — asyncpg, backoff, boto3, botocore — were promoted from optional = true to optional = false. This reflects LangSmith 0.8.18 making these dependencies unconditional (likely because features requiring them are now always enabled). The markers = "extra == \"postgres\"" and markers = "extra == \"langfuse\"" guards were removed accordingly.
name = "asyncpg"
version = "0.31.0"
-optional = true
+optional = false
python-versions = ">=3.9.0"
groups = ["main"]
-markers = "extra == \"postgres\""
This is a packaging change, not a security change — but it's worth noting if your deployment environment has restrictions on which packages can be installed.
Why This Fix Is Sufficient
Because the vulnerability lives entirely within the LangSmith SDK's own middleware code (not in your application code), upgrading the package is a complete fix. Your application does not need to add its own path sanitization on top — though defense-in-depth is always worthwhile.
Prevention & Best Practices
1. Canonicalize Before You Check
Whenever user input influences a filesystem path, resolve it to its canonical form before validating:
import os
def safe_read(base_dir: str, user_input: str) -> str:
# Resolve symlinks and normalize the path
resolved = os.path.realpath(os.path.join(base_dir, user_input))
# Enforce the path is still inside base_dir
if not resolved.startswith(os.path.realpath(base_dir) + os.sep):
raise ValueError(f"Path traversal detected: {user_input!r}")
with open(resolved, "r") as f:
return f.read()
Using pathlib (Python 3.6+):
from pathlib import Path
def safe_read_pathlib(base_dir: str, user_input: str) -> str:
base = Path(base_dir).resolve()
target = (base / user_input).resolve()
target.relative_to(base) # raises ValueError if outside base
return target.read_text()
2. Audit Third-Party Middleware
Middleware components are uniquely dangerous because they process all requests, including unauthenticated ones. When evaluating any middleware — especially in AI/ML observability tooling that's newer and may have less security review history — check:
- Does it perform any file I/O?
- Does it use request data to construct paths?
- Is it receiving data before authentication middleware runs?
3. Keep AI/ML Dependencies Pinned and Monitored
The LangChain ecosystem (langchain, langsmith, langgraph) moves extremely fast. New versions ship frequently, and security patches are often bundled with feature releases. Use tools like:
- Trivy — flagged this exact vulnerability via rule
GHSA-f4xh-w4cj-qxq8 - pip-audit — scans installed packages against PyPI advisory database
- Dependabot / Renovate — automated PRs for dependency updates
- GitHub Advisory Database — subscribe to advisories for your dependencies
4. Apply Least-Privilege Filesystem Permissions
Even if a path traversal bug exists, its blast radius is limited if the application process can't read sensitive files. Run your application as a non-root user with read access only to directories it legitimately needs.
5. OWASP & CWE References
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
- CWE-73: External Control of File Name or Path
- OWASP Path Traversal: https://owasp.org/www-community/attacks/Path_Traversal
Key Takeaways
TracingMiddlewarein LangSmith ≤ 0.8.15 processed request data without sufficient path sanitization, creating a direct path from attacker input to server filesystem reads.- AI observability libraries deserve the same security scrutiny as any other web middleware — they sit in the request path and handle raw, potentially hostile input.
- The
poetry.lockfile is your ground truth for what's actually installed; pinning to 0.8.15 while a patched 0.8.18 exists left this vulnerability open unnecessarily. - Trivy's GHSA-f4xh-w4cj-qxq8 rule can detect this specific vulnerable version range in your dependency lock files — integrate it into CI to catch regressions.
- Upgrading to 0.8.18 is the complete fix — no application-level code changes are required because the vulnerability is entirely within the SDK's middleware.
How Orbis AppSec Detected This
- Source: Attacker-controlled HTTP request data (headers, body, or route parameters) processed by
TracingMiddlewarein the LangSmith SDK - Sink: File-read operations within
TracingMiddlewarethat consumed path values derived from request input without adequate sanitization - Missing control: No path canonicalization or base-directory enforcement before the file-open call, allowing
../traversal sequences to escape the intended directory scope - CWE: CWE-22 — Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
- Fix: Upgraded
langsmithfrom0.8.15to0.8.18inpoetry.lockandpyproject.toml, replacing the vulnerableTracingMiddlewareimplementation with the patched version from the upstream SDK
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
GHSA-f4xh-w4cj-qxq8 is a sharp reminder that the observability and tracing layers we bolt onto AI applications aren't exempt from classic web security vulnerabilities. An arbitrary file read in TracingMiddleware could expose secrets, configuration files, or sensitive data to any attacker who can send a crafted HTTP request — a low bar in any internet-facing deployment.
The fix is straightforward: upgrade LangSmith to 0.8.18. But the broader lesson is architectural — middleware that processes unauthenticated request data and performs file I/O is a high-risk combination that deserves careful review, regardless of the vendor or framework. Build dependency scanning into your CI pipeline, subscribe to advisories for your AI/ML dependencies, and apply filesystem least-privilege so that even if a traversal bug slips through, its impact is contained.
References
- CWE-22: Path Traversal
- CWE-73: External Control of File Name or Path
- OWASP Path Traversal Attack
- OWASP Input Validation Cheat Sheet
- GitHub Security Advisory GHSA-f4xh-w4cj-qxq8
- Python pathlib documentation — safe path resolution
- Semgrep rules for path traversal
- fix: upgrade langsmith to 0.8.18 (GHSA-f4xh-w4cj-qxq8)