VYPR
High severity7.3GHSA Advisory· Published Oct 26, 2025· Updated Apr 15, 2026

CVE-2025-8709

CVE-2025-8709

Description

A SQL injection vulnerability exists in the langchain-ai/langchain repository, specifically in the LangGraph's SQLite store implementation. The affected version is langgraph-checkpoint-sqlite 2.0.10. The vulnerability arises from improper handling of filter operators ($eq, $ne, $gt, $lt, $gte, $lte) where direct string concatenation is used without proper parameterization. This allows attackers to inject arbitrary SQL, leading to unauthorized access to all documents, data exfiltration of sensitive fields such as passwords and API keys, and a complete bypass of application-level security filters.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
langgraph-checkpoint-sqlitePyPI
< 2.0.112.0.11

Affected products

1

Patches

1
bc9d45b47610

fix(checkpoint-sqlite): add validation to filter keys in sql store (#5666)

https://github.com/langchain-ai/langgraphEugene YurtsevJul 25, 2025via ghsa
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

6

News mentions

0

No linked articles in our index yet.