{"id":"CVE-2025-67644","aliases":["GHSA-9rwj-6rc7-p77c","PYSEC-2026-1530"],"url":"https://o3.security/vulnerability/CVE-2025-67644","summary":"LangGraph SQLite Checkpoint is vulnerable to SQL Injection via metadata filter key in 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","published":"2025-12-10T23:37:36.182Z","modified":"2026-08-12T03:51:43.233428716Z","cvss":{"score":7.3,"severity":"HIGH","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N"},"epss":{"score":0.02306,"percentile":0.82516,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"langgraph-checkpoint-sqlite","fixedVersion":"3.0.1"}],"fix":{"url":"https://github.com/langchain-ai/langgraph/commit/297242913f8ad2143ee3e2f72e67db0911d48e2a","label":"langchain-ai/langgraph@2972429"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2025/67xxx/CVE-2025-67644.json"},{"type":"ADVISORY","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":"FIX","url":"https://github.com/langchain-ai/langgraph/commit/297242913f8ad2143ee3e2f72e67db0911d48e2a"},{"type":"PACKAGE","url":"https://github.com/langchain-ai/langgraph"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:43.233428716Z"}}