{"id":"PYSEC-2026-1530","summary":"LangGraph's SQLite is vulnerable to SQL injection via metadata filter key in SQLite checkpointer list method","details":"# Context\n\nA SQL injection vulnerability exists in LangGraph's SQLite checkpoint implementation that allows attackers to manipulate SQL queries through metadata filter keys. This affects applications that accept **untrusted metadata filter keys** (not just filter values) in checkpoint search operations.\n\n# Impact\n\nAttackers who control metadata filter keys can execute arbitrary sql queries against the database.\n\n# Root Cause\n\nThe `_metadata_predicate()` function constructs SQL queries by interpolating filter keys directly into f-strings without validation:\n\n```python\n# VULNERABLE CODE (before fix)\nfor query_key, query_value in metadata_filter.items():\n    operator, param_value = _where_value(query_value)\n    predicates.append(\n        f\"json_extract(CAST(metadata AS TEXT), '$.{query_key}') {operator}\"\n    )\n    param_values.append(param_value)\n```\n\nWhile filter **values** are parameterized, filter **keys** are not validated, allowing SQL injection.\n\n# Attack Example\n\n**Before Fix:**\n```python\nfrom langgraph.checkpoint.sqlite import SqliteSaver\n\nsaver = SqliteSaver.from_conn_string(\"checkpoints.db\")\n\n# Attacker controls the filter keys\nmalicious_filter = {\"x') OR '1'='1\": \"dummy\"}\n\n# Returns ALL checkpoints, bypassing filtering\nresults = list(saver.list(None, filter=malicious_filter))\n```\n\n**Resulting SQL:**\n```sql\nWHERE json_extract(CAST(metadata AS TEXT), '$.x') OR '1'='1') = ?\n-- Injected condition makes WHERE clause always true\n```\n\n## Who Is Affected?\n\n### LangSmith Deployment Customers: NOT Impacted\n\n**LangSmith deployment customers are NOT affected by this vulnerability.** LangSmith deployments do not allow configuring custom checkpointers, so the vulnerable code path cannot be reached.\n\n### High Risk: Custom Server Deployments\n\nYou are affected if your application:\n- Runs a custom server with SqliteSaver checkpointer\n- Exposes an endpoint for fetching checkpoint history (e.g., via `get_state_history()`)\n- Accepts metadata filter keys from untrusted sources\n\n**Example vulnerable code:**\n```python\n# Custom server endpoint - User controls filter key names - DANGEROUS\n@app.post(\"/api/history\")\ndef get_history(request):\n    filter_field = request.json.get(\"filter_field\")  # Untrusted input\n    filter_value = request.json.get(\"filter_value\")\n\n    # VULNERABLE: Attacker can bypass access controls\n    history = list(graph.get_state_history(\n        config,\n        filter={filter_field: filter_value}\n    ))\n    return history\n```\n\n**Note on privilege escalation:** If an endpoint allows end users to specify arbitrary filter keys, those users likely already have legitimate access to query the checkpoint database. In such cases, this vulnerability may not constitute a privilege escalation, as users who can control filter keys would typically already be expected to have database access. However, the SQL injection still allows bypassing intended filtering logic and metadata-based access controls that the application may rely on for data isolation.\n\n### Additional Security Hardening (Defense in Depth)\n\nThis release also includes hardening improvements:\n\n**1. Checkpoint Limit Parameter**: used f-string interpolation into parameterized query.  Not considered a vulnerability as it requires users to accept untrusted input and not validate it against the actual API signature. \n\n**2. Store Filter Value Parameterization**: Refactored all filter value handling from manual quote escaping to parameterized queries\n\n## Remediation\n\n### Immediate Actions\n\n1. **Update to the patched version** of `langgraph-checkpoint-sqlite`\n2. **Audit your code** for locations where filter keys come from untrusted sources","aliases":["CVE-2025-67644","GHSA-9rwj-6rc7-p77c"],"modified":"2026-07-07T17:46:36.795807386Z","published":"2026-07-07T16:03:12.694748Z","references":[{"type":"WEB","url":"https://github.com/langchain-ai/langgraph/security/advisories/GHSA-9rwj-6rc7-p77c"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-67644"},{"type":"WEB","url":"https://github.com/langchain-ai/langgraph/commit/297242913f8ad2143ee3e2f72e67db0911d48e2a"},{"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-9rwj-6rc7-p77c"}],"affected":[{"package":{"name":"langgraph-checkpoint-sqlite","ecosystem":"PyPI","purl":"pkg:pypi/langgraph-checkpoint-sqlite"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.0.1"}]}],"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.11","2.0.2","2.0.3","2.0.4","2.0.5","2.0.6","2.0.7","2.0.8","2.0.9","3.0.0"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/langgraph-checkpoint-sqlite/PYSEC-2026-1530.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N"}]}