Home - Coinspect Security
A cyan identity marker falls from a flowing code strand at a cross-language handoff as the original request reaches another agent's Store records

LangGraph Flaw Lets Agents Compromise Another Agent's Store Data

Juan Pablo Martinez Kuhn
Director of Security Services
Agentic Systems, Frontier, LangChain

LangGraph is a framework for building AI agents. Developers can use it to build agents that draw on saved information across conversations. Coinspect identified a flaw in LangGraph that lets a logged-in user, or an agent using that user’s access, read, search, overwrite, or delete another agent’s saved data.

Our proof of concept exploited the flaw in langgraph-api==0.10.0. The public advisory assigns it a High severity rating (CVSS v4.0: 8.6) and tracks it as CVE-2026-118668 and GHSA-4hm6-w6qq-w73v.

This is our first case study in our research series on sandbox escapes, authorization, isolation, and secure defaults in agentic systems.

Why LangChain Matters

LangChain is an open-source framework for building LLM applications and agents, with integrations for models, tools, and data sources. Its ecosystem includes LangGraph, which supports stateful agent workflows with persistence and human-in-the-loop execution. LangChain reports that its open-source frameworks—LangChain, LangGraph, and Deep Agents—have surpassed one billion cumulative downloads and are used by more than one million practitioners (LangChain).

That reach makes the security of shared agent infrastructure important.

Public examples from LangChain show LangGraph in customer support and recruiting. Its Klarna case study describes an assistant that handles payments, refunds, and escalations; the case study reports 2.5 million conversations and an 80% reduction in average query-resolution time. LangChain also describes LinkedIn’s AI recruiter, which uses LangGraph for candidate sourcing, matching, and messaging. These are examples of LangGraph use, not deployments we tested; we did not assess whether either uses the affected Store configuration.

LangGraph Store and User Isolation

LangGraph’s Store keeps data that applications need across threads, such as long-term memories, preferences, and documents. Each item is addressed by a namespace—a label for a data area—and a key that identifies the item. Namespaces can organize data, but they do not isolate users by themselves. In a multi-user deployment, the server must connect each operation to the authenticated user’s identity.

The Store isolation guide describes two common approaches: reject a request that names another user’s namespace, or rewrite the namespace to include the authenticated identity. The second approach lets application code use logical names such as memories while the authorization layer maps them to a user’s own Store area.

That rewrite is part of the authorization decision. The Store must perform the operation against the resulting namespace.

The documented rewrite pattern is equivalent to this JavaScript handler:

auth.on("store", ({ value, user }) => {
  const namespace = Array.isArray(value.namespace)
    ? value.namespace
    : [];

  if (namespace.length === 0 || namespace[0] !== user.identity) {
    value.namespace = [user.identity, ...namespace];
  }

  return true;
});

For Alice, the request victim.private becomes alice.victim.private. The operation should then read or change only that Alice-scoped namespace.

The Authorization Rewrite Did Not Reach the Store

The issue occurs at a handoff between JavaScript authorization and the Python API runtime. JavaScript rewrites requests to the logged-in user’s data area, but the Python bridge copies back only metadata and drops the rewrite. The Store therefore acts on the original, unscoped namespace, reaching another user’s data.

A lost namespace rewrite lets an agent using Alice's authenticated access target another agent's Store data through read, search, overwrite, and delete operations

Figure 1. The bridge drops the namespace rewrite, so an agent using one user’s authenticated access can read, search, overwrite, or delete another agent’s Store data.

The Python route used the authorization handler’s changes: it passed the request to the handler, then used the namespace and key from that same request in the Store call.

handler_payload = {
    "namespace": namespace,
    "key": key,
    "refresh_ttl": refresh_ttl,
}

await handle_event("get", handler_payload)

result = await store.aget(
    handler_payload["namespace"],
    handler_payload["key"],
    refresh_ttl=handler_payload["refresh_ttl"],
)

With JavaScript custom auth, the request crossed from Python to a separate JavaScript process and back. The JavaScript handler returned the updated request, but the Python bridge copied back only metadata:

if (
    isinstance(value, dict)
    and (updated_value := response.get("value"))
    and isinstance(value.get("metadata"), dict)
    and (metadata := updated_value.get("metadata"))
):
    value["metadata"].update(metadata)

Because the bridge did not copy back the changed namespace, the Store call kept using the original one. The bridge also dropped other fields that determine which records an operation can reach, including key, filter, limit, offset, suffix, and max_depth.

What We Demonstrated

Our primary proof of concept used langgraph dev with langgraph-api==0.10.0, the JavaScript sidecar process shipped with the API, the Python-to-JavaScript authorization bridge, and the npm authorize() implementation. We seeded a synthetic item at victim.private/ssn, then sent an authenticated request as Alice:

GET /store/items?namespace=victim.private&key=ssn

HTTP result:
  namespace = ["victim", "private"]
  key       = "ssn"
  value     = "VICTIM-SSN-CANARY-STOCK-HTTP-0xDEADBEEF"

The initial HTTP PUT created the synthetic record for this proof. A separate control harness inserted the item directly into the backing Store, so the result did not depend on the HTTP write setup.

We then ran the same namespace-scoping policy through Python custom auth as a control. With JavaScript custom auth, Alice’s request returned the canary. With equivalent Python custom auth, the route used alice.victim.private and returned no item. A direct check of the sidecar response confirmed that it had returned the rewritten namespace:

{
  "value": {
    "namespace": ["alice", "victim", "private"],
    "key": "ssn"
  },
  "filters": null
}

Together, these checks establish three separate facts: the JavaScript handler rewrote the namespace, the sidecar returned that rewrite, and the vulnerable Store route nevertheless used the original namespace.

The proof of concept targeted langgraph-api==0.10.0, @langchain/langgraph-api==1.3.1, and @langchain/langgraph-sdk==1.9.22.

Impact and Affected Configurations

The vulnerable behavior affected deployments that used all of the following:

  • an affected langgraph-api release;
  • JavaScript or TypeScript custom authentication through the Python runtime’s JavaScript sidecar;
  • a Store containing data for multiple users; and
  • an authorization handler that relied on rewriting namespaces to separate those users’ data.

An attacker needs an ordinary authenticated account and the ability to make Store requests. No administrator privileges, user interaction, race condition, or bypass of another mitigation is required. The flaw can permit cross-user reads, overwrites, searches, and deletions.

Source analysis traced the dropped authorization changes through the Store operation handlers. Our two-user regression suite confirmed that the fix carries rewritten namespaces through get, put, delete, search, and namespace listing.

langgraph-api versions >=0.1.2,<0.15.0 are affected; >=0.15.0 is patched. Python custom-auth handlers are unaffected, as are JavaScript or TypeScript handlers that reject unauthorized namespaces instead of rewriting them.

The issue maps to CWE-639 (Authorization Bypass Through User-Controlled Key) and CWE-863 (Incorrect Authorization).

Fix and Operator Guidance

The fix is available in langgraph-api==0.15.0. It carries authorization-handler changes across the JavaScript-to-Python boundary, applies them to the route payload, and validates the resulting namespace before Store access. Invalid or unsafe changes fail closed.

Upgrade to langgraph-api>=0.15.0. If an upgrade is not immediately possible, the advisory’s workaround is to reject requests for unauthorized namespaces rather than relying on namespace rewriting. Apply that check to every Store action the deployment exposes.

For regression coverage, use two authenticated users and inspect the namespace that actually reaches the Store backend. Checking only that the handler ran or returned a rewritten value would have missed this failure.

Building agentic systems, sandboxed environments, or sandboxing frameworks?

Find gaps between what your agents can do and what their sandbox allows.

Contact Coinspect

Disclosure Timeline

  • June 15, 2026: We completed source review and a route/bridge control proof against langgraph-api==0.10.0.
  • June 16, 2026: We completed the HTTP reproduction with langgraph dev and the shipped JavaScript sidecar, submitted the private report, and notified LangChain’s security address.
  • July 30, 2026: LangChain acknowledged the report. We reran the HTTP proof and Python-auth control.
  • September 28, 2026: LangChain published GHSA-4hm6-w6qq-w73v and credited coinspect-audits as reporter.
  • October 1, 2026: We reviewed the released langgraph-api==0.15.0 package and verified the fix across the Store operation families.
  • October 28, 2026: CVE-2026-118668 was published.

References