CVE
CVE-2026-115419, CVE-2026-115883, CVE-2026-115886
CWE
CWE-488, CWE-706, CWE-862, CWE-346, CWE-918
Affected Surface
- PyPI `strands-agents < 1.43.0` and npm `@strands-agents/sdk < 1.5.0` running multi-caller Agent2Agent (`A2AServer`) endpoints (`CVE-2026-115419` / `GHSA-4v6f-7vjh-6x4m`)
- PyPI `langgraph-checkpoint-postgres < 3.1.3` and `langgraph-checkpoint-sqlite < 3.1.2` (`langgraph-checkpoint < 4.3.0`) when user- or tenant-influenced strings reach `get`, `search`, `delete`, `list_namespaces`, or `batch` (`CVE-2026-115883` / `GHSA-6hmw-9432-67qq`)
- PyPI `fastmcp` and `fastmcp-slim` `>= 3.0.0, < 3.4.10` and `>= 4.0.0, < 4.1.2` using component `auth=` checks with transforms (`GHSA-7xgj-8392-5q4r`) or `ClientCredentialsOAuthProvider` / `PrivateKeyJWTOAuthProvider` (`GHSA-vqqp-x5qm-5qp4`)
- PyPI `mlflow >= 3.12.0rc0, < 3.17.0` tracking servers with `trulens` and `trulens-providers-litellm` installed (`CVE-2026-115886` / `GHSA-2wqp-q3c6-ccj5`)
TL;DR
Between 22 and 23 October 2026, four major AI agent and evaluation frameworks (Amazon Strands Agents SDK, LangGraph, Prefect FastMCP, and MLflow) disclosed five vulnerabilities affecting multi-tenant session isolation, MCP tool authorization, OAuth client-credential binding, and tracking-server egress:
- Amazon Strands Agents SDK (
CVE-2026-115419/GHSA-4v6f-7vjh-6x4m, CVSS 8.1 High):A2AServerandStrandsA2AExecutoraccepted a singleAgentinstance and reused its in-memory conversation state and streaming state across every caller’s Agent2Agent (A2A)context_id. Any caller could read another tenant’s conversation history or inject persistent prompt instructions into subsequent sessions. - LangGraph Checkpoint Postgres & SQLite (
CVE-2026-115883/GHSA-6hmw-9432-67qq, CVSS 6.0 Medium):PostgresStoreandSqliteStoreserialize a namespace tuple("tenant", "bob")by joining labels with a period (".".join(namespace)->"tenant.bob").BaseStoreonly rejected.inside labels onput()/aput(), leavingget(),search(),delete(),list_namespaces(), andbatch()/abatch()(includingPutOpitems insidebatch()) unchecked. Passing("tenant.bob",)read, overwrote, or deleted("tenant", "bob"). - Prefect FastMCP (
GHSA-7xgj-8392-5q4r, CVSS 6.8 Medium, andGHSA-vqqp-x5qm-5qp4, CVSS 5.9 Medium): First, when aToolTransformor customTransformadded tags or modified metadata that anAuthCheck(such asrestrict_tag("admin", scopes=["admin"])) relied on,list_tools()hid the tool, but directcall_tool()/get_resource()lookups checked the pre-transform component or returned the transformed component without running_component_auth_allows(). Second,ClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProvidernever exposed or forwarded theissuer=parameter to the underlying MCP SDK, so a compromised or user-supplied MCP server could advertise its own OAuth token endpoint and capture the client’sclient_idandclient_secret. - MLflow (
CVE-2026-115886/GHSA-2wqp-q3c6-ccj5, CVSS 6.5 Medium): The unauthenticatedPOST /ajax-api/3.0/mlflow/scorer/invokeendpoint deserializedthird_party_scorer_dataviaScorer.model_validate_json(), reflectively instantiatingmlflow.genai.scorers.trulensclasses and forwarding rawkwargs(api_base,base_url,api_key,completion_kwargs) straight intoLiteLLM(), allowing unauthenticated SSRF with caller-controlled headers.
What to do now:
- Upgrade
strands-agentsto>= 1.43.0(@strands-agents/sdkto>= 1.5.0) and migrateA2AServer(agent=...)calls toA2AServer(agent_factory=lambda context_id: ...). - Upgrade
langgraph-checkpoint-postgresto>= 3.1.3,langgraph-checkpoint-sqliteto>= 3.1.2, andlanggraph-checkpointto>= 4.3.0. - Upgrade
fastmcp/fastmcp-slimto>= 4.1.2(or>= 3.4.10on 3.x), and explicitly passissuer="https://..."toClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProvider. - Upgrade
mlflowto>= 3.17.0.
Affected packages and projects
| Ecosystem | Package | CVE / GHSA | Severity | Affected versions | Fixed versions | Action required beyond upgrade? |
|---|---|---|---|---|---|---|
| PyPI / npm | strands-agents, @strands-agents/sdk | CVE-2026-115419 (GHSA-4v6f-7vjh-6x4m) | CVSS 8.1 High | strands-agents < 1.43.0; @strands-agents/sdk < 1.5.0 | 1.43.0 (PyPI), 1.5.0 (npm) | Yes (recommended): pass agent_factory= (agentFactory) to A2AServer so each context_id gets its own Agent and session_manager |
| PyPI | langgraph-checkpoint-postgres, langgraph-checkpoint-sqlite, langgraph-checkpoint | CVE-2026-115883 (GHSA-6hmw-9432-67qq) | CVSS 6.0 Medium | langgraph-checkpoint-postgres < 3.1.3; langgraph-checkpoint-sqlite < 3.1.2; langgraph-checkpoint < 4.3.0 | 3.1.3, 3.1.2, 4.3.0 | Custom external stores that join namespaces with . should call validate_op_namespace(op) |
| PyPI | fastmcp, fastmcp-slim | GHSA-7xgj-8392-5q4r | CVSS 6.8 Medium | >= 3.0.0, < 3.4.10; >= 4.0.0, < 4.1.2 | 3.4.10, 4.1.2 | No |
| PyPI | fastmcp, fastmcp-slim | GHSA-vqqp-x5qm-5qp4 | CVSS 5.9 Medium | >= 4.0.0a1, < 4.1.2 | 4.1.2 (requires mcp >= 2.2.0) | Yes: pass issuer="https://..." to ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider, and rotate secrets exposed to untrusted MCP URLs |
| PyPI | mlflow | CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) | CVSS 6.5 Medium | >= 3.12.0rc0, < 3.17.0 | 3.17.0 | No (previously stored scorer rows with destination kwargs are also blocked on read) |
Technical analysis
1. Strands Agents SDK (CVE-2026-115419): shared Agent state across A2A context_ids
In Amazon’s Strands Agents SDK (strands-agents on PyPI and @strands-agents/sdk on npm), A2AServer adapts a Strands Agent to the Agent2Agent (A2A) protocol. Before Python 1.43.0 (commit 398343faad) and TypeScript 1.5.0 (commit f8ba9376fe), A2AServer accepted a single agent: SAAgent instance and passed that same object into StrandsA2AExecutor.
Because a Strands Agent holds its conversation messages (agent.messages), system prompt state, and session manager on the instance itself (and StrandsA2AExecutor also stored _current_artifact_id and _is_first_chunk on self), every incoming A2A request mutated the exact same conversation history regardless of the request’s context_id:
- Cross-tenant conversation disclosure: Caller B sending a request with a fresh
context_idcaused the model to run with Caller A’s prior turns still inagent.messages. - Cross-session prompt injection: Any caller could plant persistent instructions in the shared
agent.messageslist that steered tool calls and responses for subsequent callers.
The fix introduces agent_factory: Callable[[str], Agent] (agentFactory: (contextId: string) => InvokableAgent in TypeScript) alongside an LRU context map (max_contexts = 1000) where each context_id owns a dedicated _ContextEntry(agent, lock) and per-request _StreamState:
# strands-py/src/strands/multiagent/a2a/server.py (patched in 1.43.0)
from strands import Agent
from strands.multiagent.a2a import A2AServer
# DeprecationWarning if a single `agent=` is passed; use agent_factory for multi-caller isolation:
server = A2AServer(
agent_factory=lambda context_id: Agent(
model=my_model,
tools=[...],
# Wire any session_manager per context_id here
),
max_contexts=1000,
)
If legacy code still passes a single agent= instance, 1.43.0 / 1.5.0 emits a DeprecationWarning, serializes all requests behind a single lock, and swaps take_snapshot() / load_snapshot() state onto and off the shared instance per context_id (provided no shared session_manager is attached). For real concurrency and multi-tenant isolation, switch to agent_factory.
2. LangGraph (CVE-2026-115883): dot-joined namespace collision in PostgresStore and SqliteStore
In LangGraph’s persistence layer, namespaces are passed as tuples of strings such as ("org_123", "user_456"). Both PostgresStore (langgraph-checkpoint-postgres) and SqliteStore (langgraph-checkpoint-sqlite) store that tuple in SQL as a single dot-joined prefix column (".".join(namespace) -> "org_123.user_456").
To prevent ("org_123.user_456",) from colliding with ("org_123", "user_456"), BaseStore._validate_namespace() forbade periods (.) in namespace labels. However, before commit bfcfea554e, BaseStore only called _validate_namespace() inside put() and aput():
store.get(namespace, key),store.search(namespace_prefix),store.delete(namespace, key), andstore.list_namespaces(prefix=..., suffix=...)(plusaget,asearch,adelete,alist_namespaces) never validated the labels.store.batch(ops)andstore.abatch(ops)validated nothing at all, not evenPutOp(namespace, key, value)items passed directly insidebatch().
Consequently, if an application built a namespace tuple from a caller-supplied string tenant_id (for example namespace = (f"tenant_{user_input}",) or (user_input,)), passing "tenant_a.secret_ns" allowed the caller to:
# Against langgraph-checkpoint-postgres < 3.1.3 or langgraph-checkpoint-sqlite < 3.1.2:
store.get(("tenant_a.secret_ns",), "api_keys") # reads ("tenant_a", "secret_ns")
store.delete(("tenant_a.secret_ns",), "api_keys") # deletes ("tenant_a", "secret_ns")
store.batch([PutOp(("tenant_a.secret_ns",), "k", {})]) # overwrites ("tenant_a", "secret_ns")
Commit bfcfea554e adds _validate_namespace_labels() to get, search, delete, list_namespaces, and their async counterparts in langgraph-checkpoint 4.3.0 (before queueing into AsyncBatchedBaseStore._aqueue, so one caller’s invalid label cannot fail concurrent callers sharing the batch), and calls validate_op_namespace(op) inside _group_ops() in both PostgresStore (3.1.3) and SqliteStore (3.1.2):
--- a/libs/checkpoint-postgres/langgraph/store/postgres/base.py
+++ b/libs/checkpoint-postgres/langgraph/store/postgres/base.py
@@ -1386,6 +1387,7 @@ def _group_ops(ops: Iterable[Op]) -> tuple[dict[type, list[tuple[int, Op]]], int
grouped_ops: dict[type, list[tuple[int, Op]]] = defaultdict(list)
tot = 0
for idx, op in enumerate(ops):
+ validate_op_namespace(op)
grouped_ops[type(op)].append((idx, op))
tot += 1
return grouped_ops, tot
3. FastMCP (GHSA-7xgj-8392-5q4r and GHSA-vqqp-x5qm-5qp4): post-transform auth bypass and un-bound M2M OAuth client secrets
Prefect’s fastmcp 4.1.2 and 3.4.10 (commit 85df42fbf2) fix two distinct security flaws:
A. Direct component calls skipped post-transform AuthCheck (GHSA-7xgj-8392-5q4r)
FastMCP allows servers to attach component-level authorization rules (such as auth=restrict_tag("admin", scopes=["admin"])) and also attach transforms (ToolTransform, Transform) that rename components or add tags like tags={"admin"} at the provider, server, or session layer.
When a client called list_tools(), FastMCP ran all transforms first and then filtered the transformed list through _tool_auth_allows(tool), hiding the "admin"-tagged tool from unprivileged callers. However, on the direct lookup path (server.call_tool("tagged_tool"), get_resource(), get_prompt()), FastMCP checked authorization on the untransformed component or returned the transformed component from call_next without re-evaluating _component_auth_allows() after the transform pipeline finished. Furthermore, if a concrete resource (secret://data) was denied by its AuthCheck and a wildcard resource template (secret://{name}) existed on the same server or via a ProxyProvider, falling through on denial allowed the template to serve the protected URI anyway.
The fix in 85df42fbf2 re-runs _component_auth_allows() on the final transformed Tool, Resource, ResourceTemplate, and Prompt at every lookup layer, and ensures a denied concrete resource shadows any matching resource template at that URI.
B. M2M OAuth wrappers omitted issuer binding (GHSA-vqqp-x5qm-5qp4)
In FastMCP 4.0.0a1 through 4.1.1, ClientCredentialsOAuthProvider and PrivateKeyJWTOAuthProvider (fastmcp/client/auth/client_credentials.py) wrapped the upstream mcp Python SDK’s machine-to-machine OAuth providers, which discover the MCP server’s authorization server via RFC 9728 Protected Resource Metadata.
Although the underlying mcp SDK supported an issuer= parameter to verify that the authorization server advertised by the MCP server matched the issuer that actually owns the client’s client_id and client_secret, FastMCP’s wrappers omitted issuer from __init__() and _bind(). If a FastMCP client connected to a user-supplied or compromised MCP server URL, that server could advertise https://attacker[.]example/oauth/token in its metadata and receive the client’s client_id and client_secret in the token-exchange POST.
FastMCP 4.1.2 bumps its dependency floor to mcp>=2.2.0, adds issuer: str | None = None to both providers, isolates cached tokens by (client_id, scopes, issuer), and emits an MCPDeprecationWarning when issuer is omitted:
from fastmcp import Client
from fastmcp.client.auth import ClientCredentialsOAuthProvider
# Upgrading to fastmcp >= 4.1.2 is not enough by itself; pass issuer= explicitly:
auth = ClientCredentialsOAuthProvider(
client_id="my-client-id",
client_secret="my-client-secret",
scopes=["read", "write"],
issuer="https://auth.example.com",
)
4. MLflow (CVE-2026-115886): unauthenticated POST /ajax-api/3.0/mlflow/scorer/invoke SSRF via third_party_scorer_data.kwargs
In MLflow 3.12.0rc0 through 3.16.x, the GenAI evaluation routes POST /ajax-api/3.0/mlflow/scorer/invoke, POST /ajax-api/3.0/mlflow/scorers/register, and POST /ajax-api/3.0/mlflow/genai/evaluate accept a JSON serialized_scorer string. Notably, /ajax-api/3.0/mlflow/scorer/invoke is registered without an authentication decorator on default MLflow tracking servers and runs Scorer.model_validate_json(serialized_scorer) synchronously.
Inside mlflow/genai/scorers/base.py, Scorer.model_validate_json() checked module_path against THIRD_PARTY_SCORER_ALLOWED_MODULES (ragas, deepeval, trulens, phoenix), imported the module, looked up getattr(module, class_name), and called scorer_class(**init_kwargs) with unconstrained data["kwargs"]. When trulens and trulens-providers-litellm were installed in the server environment, selecting module: "mlflow.genai.scorers.trulens" with a non-native model URI such as "openai:/gpt-4o" routed **kwargs directly into trulens.providers.litellm.LiteLLM(model_engine=..., **kwargs), which passed api_base, base_url, api_key, or completion_kwargs into litellm.completion().
Commit bac30b4148 (released in MLflow 3.17.0) adds _validate_serialized_scorer_payload() and _validate_third_party_scorer_data() in mlflow/utils/validation.py, recursively walking every nested third_party_scorer_data dictionary and list of 2-item pairs (used by completion_kwargs) and rejecting any key in GATEWAY_DESTINATION_KEYS (api_base, base_url, api_key, custom_llm_provider, litellm_params, etc.) as well as call_source decorator scorers:
--- a/mlflow/utils/validation.py
+++ b/mlflow/utils/validation.py
@@ -1119,6 +1119,25 @@
+def _find_destination_keys(value: Any) -> set[str]:
+ if isinstance(value, dict):
+ found = GATEWAY_DESTINATION_KEYS & set(value)
+ for nested in value.values():
+ found |= _find_destination_keys(nested)
+ return found
+ if isinstance(value, list):
+ found: set[str] = set()
+ for nested in value:
+ if isinstance(nested, list) and len(nested) == 2 and isinstance(nested[0], str):
+ found |= GATEWAY_DESTINATION_KEYS & {nested[0]}
+ found |= _find_destination_keys(nested)
+ return found
+ return set()
Detection and remediation
1. Audit environments, lockfiles, and vulnerable call patterns
Run the following commands across your Python and TypeScript repositories:
# 1. Check installed Python package versions
pip show strands-agents langgraph-checkpoint langgraph-checkpoint-postgres langgraph-checkpoint-sqlite fastmcp fastmcp-slim mlflow 2>/dev/null || true
# 2. Check npm lockfiles for @strands-agents/sdk < 1.5.0
npm ls @strands-agents/sdk 2>/dev/null || true
# 3. Audit code for single-agent A2AServer(...) construction and FastMCP M2M OAuth without issuer=
rg -n 'A2AServer\s*\(|StrandsA2AExecutor\s*\(|A2AExecutor\s*\(' --glob '*.{py,ts,js}' . 2>/dev/null
rg -n '(ClientCredentialsOAuthProvider|PrivateKeyJWTOAuthProvider)\s*\(' --glob '*.py' . 2>/dev/null
2. Upgrade packages and apply required code changes
- Strands Agents SDK (
CVE-2026-115419):- Upgrade Python
strands-agentsto>= 1.43.0and TypeScript@strands-agents/sdkto>= 1.5.0. - Refactor every multi-caller
A2AServer(agent)/StrandsA2AExecutor(agent)call to passagent_factory(agentFactoryin TypeScript) so eachcontext_idreceives a freshAgentinstance and a context-scopedsession_manager.
- Upgrade Python
- LangGraph (
CVE-2026-115883):- Upgrade
langgraph-checkpoint-postgresto>= 3.1.3,langgraph-checkpoint-sqliteto>= 3.1.2, andlanggraph-checkpointto>= 4.3.0(or upgrade self-hosted LangSmith Agent Server to>= 0.15.5/>= 0.16.2).
- Upgrade
- FastMCP (
GHSA-7xgj-8392-5q4randGHSA-vqqp-x5qm-5qp4):- Upgrade
fastmcp/fastmcp-slimto>= 4.1.2(or>= 3.4.10on the 3.x branch). - Add
issuer="https://<your-expected-auth-server>"to everyClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProviderinstantiation, and rotate anyclient_secretthat was used with untrusted or user-configured MCP server URLs.
- Upgrade
- MLflow (
CVE-2026-115886):- Upgrade
mlflowto>= 3.17.0, and audit tracking-storeregistered_scorersrows and HTTP logs for requests to/ajax-api/3.0/mlflow/scorer/invokecontainingthird_party_scorer_data.
- Upgrade
Timeline
- 2026-10-22 (20:04 UTC): AWS publishes bulletin
2026-146-AWSandCVE-2026-115419(GHSA-4v6f-7vjh-6x4m) alongsidestrands-agents1.43.0and@strands-agents/sdk1.5.0. - 2026-10-23 (21:12 UTC): LangChain publishes
CVE-2026-115883(GHSA-6hmw-9432-67qq) and releaseslanggraph-checkpoint4.3.0,langgraph-checkpoint-postgres3.1.3, andlanggraph-checkpoint-sqlite3.1.2. - 2026-10-23 (21:17 UTC): MLflow publishes
CVE-2026-115886(GHSA-2wqp-q3c6-ccj5), fixed inmlflow3.17.0. - 2026-10-23 (21:31 to 21:32 UTC): Prefect publishes
GHSA-vqqp-x5qm-5qp4andGHSA-7xgj-8392-5q4rforfastmcpandfastmcp-slim, fixed in3.4.10and4.1.2.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- AWS Security Bulletin 2026-146-AWS & GHSA-4v6f-7vjh-6x4m (CVE-2026-115419: Strands Agents SDK A2A session leak)
- strands-agents/harness-sdk commit 398343faad (Python A2A agent_factory isolation)
- LangGraph advisory GHSA-6hmw-9432-67qq (CVE-2026-115883: dot-joined namespace traversal)
- langchain-ai/langgraph commit bfcfea554e (validate namespace labels before store dispatch)
- FastMCP advisory GHSA-7xgj-8392-5q4r (post-transform component authorization skipped on direct calls)
- FastMCP advisory GHSA-vqqp-x5qm-5qp4 (M2M OAuth providers without issuer binding)
- PrefectHQ/fastmcp commit 85df42fbf2 (enforce component authorization and bind OAuth credentials to an issuer)
- MLflow advisory GHSA-2wqp-q3c6-ccj5 (CVE-2026-115886: third-party scorer kwargs SSRF)
- mlflow/mlflow commit bac30b4148 (reject egress-steering kwargs in third-party scorer payloads)