High severity7.3OSV Advisory· Published Oct 29, 2025· Updated Apr 15, 2026
CVE-2025-64104
CVE-2025-64104
Description
LangGraph SQLite Checkpoint is an implementation of LangGraph CheckpointSaver that uses SQLite DB (both sync and async, via aiosqlite). Prior to 2.0.11, 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. This vulnerability is fixed in 2.0.11.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
langgraph-checkpoint-sqlitePyPI | < 2.0.11 | 2.0.11 |
Affected products
1- Range: 0.1.10, 0.1.11, 0.1.12, …
Patches
1bc9d45b47610fix(checkpoint-sqlite): add validation to filter keys in sql store (#5666)
2 files changed · +44 −0
libs/checkpoint-sqlite/langgraph/store/sqlite/base.py+24 −0 modified@@ -3,6 +3,7 @@ import concurrent.futures import datetime import logging +import re import sqlite3 import threading from collections import defaultdict @@ -107,6 +108,23 @@ def _decode_ns_text(namespace: str) -> tuple[str, ...]: return tuple(namespace.split(".")) +def _validate_filter_key(key: str) -> None: + """Validate that a filter key is safe for use in SQL queries. + + Args: + key: The filter key to validate + + Raises: + ValueError: If the key contains invalid characters that could enable SQL injection + """ + # Allow alphanumeric characters, underscores, dots, and hyphens + # This covers typical JSON property names while preventing SQL injection + if not re.match(r"^[a-zA-Z0-9_.-]+$", key): + raise ValueError( + f"Invalid filter key: '{key}'. Filter keys must contain only alphanumeric characters, underscores, dots, and hyphens." + ) + + def _json_loads(content: bytes | str | orjson.Fragment) -> Any: if isinstance(content, orjson.Fragment): if hasattr(content, "buf"): @@ -372,6 +390,8 @@ def _prepare_batch_search_queries( filter_conditions = [] if op.filter: for key, value in op.filter.items(): + _validate_filter_key(key) + if isinstance(value, dict): for op_name, val in value.items(): condition, filter_params_ = self._get_filter_condition( @@ -622,6 +642,8 @@ def _get_batch_list_namespaces_queries( def _get_filter_condition(self, key: str, op: str, value: Any) -> tuple[str, list]: """Helper to generate filter conditions.""" + _validate_filter_key(key) + # We need to properly format values for SQLite JSON extraction comparison if op == "$eq": if isinstance(value, str): @@ -858,6 +880,8 @@ def _get_batch_GET_ops_queries( def _get_filter_condition(self, key: str, op: str, value: Any) -> tuple[str, list]: """Helper to generate filter conditions.""" + _validate_filter_key(key) + # We need to properly format values for SQLite JSON extraction comparison if op == "$eq": if isinstance(value, str):
libs/checkpoint-sqlite/tests/test_store.py+20 −0 modified@@ -1047,3 +1047,23 @@ def test_search_items( for ns in test_namespaces: key = f"item_{ns[-1]}" store.delete(ns, key) + + +def test_sql_injection_vulnerability(store: SqliteStore) -> None: + """Test that SQL injection via malicious filter keys is prevented.""" + # 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"}) + assert len(normal) == 1 + assert normal[0].value["access"] == "public" + + # SQL injection attempt via malicious key should raise ValueError + malicious_key = "access') = 'public' OR '1'='1' OR json_extract(value, '$." + + with pytest.raises(ValueError, match="Invalid filter key"): + store.search(("docs",), filter={malicious_key: "dummy"})
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.