
Research
/Security News
Critical Vulnerability in NestJS Devtools: Localhost RCE via Sandbox Escape
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
node-red-contrib-redis-variable
Advanced tools
A comprehensive Node-RED node for Redis operations with universal payload-based configuration, automatic JSON handling, SSL/TLS support, and advanced pattern matching with pagination
A comprehensive Node-RED node for Redis operations with flexible connection management and modern features.
Developed by Andrii Lototskyi
msg.payload
for parametersnpm install node-red-contrib-redis-variable
Or install directly through the Node-RED palette manager.
The Redis configuration node supports flexible connection parameters:
All connection parameters support multiple input types:
JSON object with additional ioredis connection options:
{
"connectTimeout": 10000,
"lazyConnect": true,
"keepAlive": 30000,
"family": 4,
"retryDelayOnFailover": 100
}
The module provides comprehensive SSL/TLS support for secure Redis connections:
Basic SSL (server verification only):
Enable SSL/TLS: ✓
Verify Certificate: ✓
Client Certificate: (empty)
Private Key: (empty)
CA Certificate: (empty)
Mutual TLS (client + server authentication):
Enable SSL/TLS: ✓
Verify Certificate: ✓
Client Certificate: Your client certificate
Private Key: Your private key
CA Certificate: Custom CA if needed
Self-signed certificates:
Enable SSL/TLS: ✓
Verify Certificate: ✗ (disable for self-signed)
CA Certificate: Your self-signed CA
Environment-based SSL configuration:
Enable SSL/TLS: ✓
Client Certificate Type: Environment Variable → TLS_CLIENT_CERT
Private Key Type: Environment Variable → TLS_PRIVATE_KEY
CA Certificate Type: Environment Variable → TLS_CA_CERT
Host: Environment Variable → REDIS_HOST
Port: Environment Variable → REDIS_PORT
Password: Environment Variable → REDIS_PASSWORD
Host: Global Context → redis_config.host
Port: Global Context → redis_config.port
Password: Flow Context → redis_password
Setting up Global Context Variables:
In Node-RED Admin Panel:
redis_config.host
= your-redis-host
redis_config.port
= 6379
redis_config.password
= your-redis-password
Via Function Node:
// Set global context variables
flow.set("redis_config", {
host: "your-redis-host",
port: 6379,
password: "your-redis-password"
});
Via HTTP API:
curl -X POST http://localhost:1880/context/global/redis_config \
-H "Content-Type: application/json" \
-d '{"host":"your-redis-host","port":6379,"password":"your-redis-password"}'
Troubleshooting Global Context:
Testing Global Context Setup:
Set up test variables:
// In a Function node
flow.set("redis_config", {
host: "localhost",
port: 6379,
password: "your-password"
});
Test connection:
// In another Function node
msg.payload = "test_key";
return msg;
Check logs:
NODE_RED_DEBUG=1 node-red
Universal Payload Interface: All Redis operations use a unified msg.payload
interface. Parameters can be passed as simple strings (for single keys) or as objects with specific properties. This provides flexibility while maintaining simplicity.
// Simple key format
msg.payload = "user:123";
// Returns: { payload: "stored_value" }
// Object format
msg.payload = {
key: "user:123"
};
// Returns: { payload: "stored_value" }
// Simple SET
msg.payload = {
key: "user:123",
value: "John Doe"
};
// Returns: { payload: { success: true, result: "OK", ttl: null } }
// SET with TTL
msg.payload = {
key: "session:abc123",
value: { userId: 42, role: "admin" },
ttl: 3600
};
// Returns: { payload: { success: true, result: "OK", ttl: 3600 } }
// Delete single key
msg.payload = {
key: "mykey"
};
// Or simple format
msg.payload = "mykey";
// Returns: { payload: { success: true, deleted: 1, keys: ["mykey"] } }
// Delete multiple keys
msg.payload = {
keys: ["key1", "key2", "key3"]
};
// Returns: { payload: { success: true, deleted: 3, keys: ["key1", "key2", "key3"] } }
// Check single key
msg.payload = "mykey";
// Or object format
msg.payload = {
key: "mykey"
};
// Returns: { payload: { exists: true, count: 1, keys: ["mykey"] } }
// Check multiple keys
msg.payload = {
keys: ["key1", "key2", "key3"]
};
// Returns: { payload: { exists: true, count: 2, keys: ["key1", "key2", "key3"] } }
// Simple pattern
msg.payload = "user:*";
// Returns: { payload: { pattern: "user:*", keys: ["user:123", "user:456"], count: 2, scanned: true } }
// Pattern with custom count
msg.payload = {
pattern: "session:*",
count: 50
};
// Returns: { payload: { pattern: "session:*", keys: ["session:abc123", "session:def456"], count: 2, limit: 50, scanned: true } }
// Pattern with pagination (cursor)
msg.payload = {
pattern: "user:*",
count: 30,
cursor: "12345"
};
// Returns: { payload: { pattern: "user:*", keys: ["user:31", "user:32"], count: 2, limit: 30, cursor: "67890", startCursor: "12345", scanned: true, truncated: false } }
// Pattern with skip (skip first N keys)
msg.payload = {
pattern: "session:*",
count: 30,
skip: 100
};
// Returns: { payload: { pattern: "session:*", keys: ["session:101", "session:102"], count: 2, limit: 30, cursor: "67890", startCursor: 0, scanned: true, truncated: false } }
// Complex patterns
msg.payload = "cache:page:*"; // All cache pages
msg.payload = "temp:*:data"; // Temporary data keys
msg.payload = "user:*:profile"; // User profiles
Advanced MATCH Features:
cursor
parameter for efficient pagination through large datasetsskip
parameter to skip the first N matching keysResponse Format:
{
"pattern": "user:*",
"keys": ["user:1", "user:2", "user:3"],
"count": 3, // Number of keys returned
"limit": 50, // Requested limit
"cursor": "67890", // Next cursor for pagination
"startCursor": "0", // Starting cursor
"scanned": true, // Operation completed
"truncated": false // true if results were limited by count
}
Pagination Example:
// First request
msg.payload = {
pattern: "session:*",
count: 30
};
// Response contains cursor for next page
// Use that cursor in next request
msg.payload = {
pattern: "session:*",
count: 30,
cursor: "67890" // from previous response
};
// Continue until cursor becomes "0" (end of results)
msg.payload = "mykey";
// Returns: { payload: { key: "mykey", ttl: 3600, status: "expires in 3600 seconds" } }
msg.payload = {
key: "mykey",
ttl: 3600
};
// Returns: { payload: { success: true, key: "mykey", ttl: 3600, message: "Expiration set" } }
msg.payload = "mykey";
// Returns: { payload: { success: true, key: "mykey", message: "Expiration removed" } }
// Simple increment
msg.payload = "counter";
// Returns: { payload: { key: "counter", value: 1 } }
// Increment by amount
msg.payload = {
key: "score",
amount: 10
};
// Returns: { payload: { key: "score", value: 110, increment: 10 } }
msg.payload = {
key: "mylist",
value: "item1"
};
// Returns: { payload: { success: true, key: "mylist", length: 1, operation: "lpush" } }
msg.payload = "mylist";
// Returns: { payload: "item1" }
msg.payload = "mylist";
// Returns: { payload: { key: "mylist", length: 5 } }
msg.payload = {
key: "mylist",
start: 0,
stop: -1
};
// Returns: { payload: { key: "mylist", range: {start: 0, stop: -1}, values: ["item1", "item2", "item3"], count: 3 } }
Configure timeout in node settings. These operations run continuously and emit messages when items are available.
// Single field
msg.payload = {
key: "myhash",
field: "name",
value: "John"
};
// Returns: { payload: { success: true, key: "myhash", field: "name", created: true } }
// Multiple fields
msg.payload = {
key: "myhash",
fields: {
name: "John",
age: 30,
city: "New York"
}
};
// Returns: { payload: { success: true, key: "myhash", fields: ["name", "age", "city"], created: 3 } }
msg.payload = {
key: "myhash",
field: "name"
};
// Returns: { payload: "John" }
msg.payload = "myhash";
// Returns: { payload: { name: "John", age: "30", city: "New York" } }
// Delete single field
msg.payload = {
key: "myhash",
field: "age"
};
// Returns: { payload: { success: true, key: "myhash", deleted: 1, fields: ["age"] } }
// Delete multiple fields
msg.payload = {
key: "myhash",
fields: ["age", "city"]
};
// Returns: { payload: { success: true, key: "myhash", deleted: 2, fields: ["age", "city"] } }
msg.payload = {
channel: "mychannel",
message: "Hello World"
};
// Returns: { payload: { success: true, channel: "mychannel", subscribers: 2, message: "Hello World" } }
Configure channel in node settings. Messages are automatically emitted:
// Received message format:
{
topic: "mychannel",
payload: "Hello World"
}
Configure pattern in node settings (e.g., "news.*"):
// Received message format:
{
pattern: "news.*",
topic: "news.sports",
payload: "Sports update"
}
// Configure Lua script in node editor:
// return redis.call('GET', KEYS[1])
msg.payload = ["mykey"]; // Array of keys and arguments
// Returns: { payload: "script_result" }
Stores Redis client in flow or global context for direct access:
// Access stored instance
const redis = flow.get("redis_client");
const result = await redis.get("mykey");
The module automatically detects and handles JSON data without any configuration:
msg.payload = {
key: "user:123",
value: {
name: "John Doe",
age: 30,
preferences: {
theme: "dark",
language: "en"
}
}
};
// Automatically stored as: '{"name":"John Doe","age":30,"preferences":{"theme":"dark","language":"en"}}'
msg.payload = "user:123";
// Returns: {
// "name": "John Doe",
// "age": 30,
// "preferences": {
// "theme": "dark",
// "language": "en"
// }
// }
// Store simple string
msg.payload = {
key: "message",
value: "Hello World"
};
// Returns: "Hello World"
// Store number
msg.payload = {
key: "count",
value: 42
};
// Returns: "42"
// Store object
msg.payload = {
key: "config",
value: {
debug: true,
timeout: 5000
}
};
// Returns: {debug: true, timeout: 5000}
For blocking operations (BLPOP, BRPOP, Lua scripts), enable "Force new connection" to prevent blocking other operations.
All operations include comprehensive error handling:
// Error response format:
{
payload: {
error: "Connection failed: ECONNREFUSED"
}
}
// Store user session
msg.payload = {
key: "session:abc123",
value: {
userId: 456,
loginTime: new Date().toISOString(),
permissions: ["read", "write"]
},
ttl: 3600 // 1 hour expiration
};
// Producer - Add task to queue
msg.payload = {
key: "task_queue",
value: {
id: "task_001",
type: "email",
data: { to: "user@example.com", subject: "Welcome" }
}
};
// Consumer (using BLPOP)
// Configure BLPOP operation in node settings
// Automatically receives tasks as they're added
// Cache API response for 1 hour
msg.payload = {
key: "api_cache:users",
value: apiResponse,
ttl: 3600 // 1 hour
};
// Publisher
msg.payload = {
channel: "notifications:user:123",
message: {
type: "message",
from: "user:456",
content: "Hello there!"
}
};
// Subscriber automatically receives notifications
// Find all temporary keys
msg.payload = "temp:*";
// Returns: { pattern: "temp:*", keys: ["temp:cache1", "temp:cache2", "temp:session123"], count: 3, scanned: true }
// Find expired session keys
msg.payload = {
pattern: "session:*:expired",
count: 50
};
// Returns: { pattern: "session:*:expired", keys: ["session:abc:expired", "session:def:expired"], count: 2, scanned: true }
// Clean up old cache entries
msg.payload = "cache:old:*";
// Use returned keys with DEL operation for cleanup
// Simple format
msg.payload = "user:123";
// Object format
msg.payload = {
key: "user:123"
};
// Returns: "John Doe" (or stored value)
// Simple SET
msg.payload = {
key: "user:123",
value: "John Doe"
};
// SET with TTL (expires in 1 hour)
msg.payload = {
key: "session:abc123",
value: {userId: 42, role: "admin"},
ttl: 3600
};
// Returns: { success: true, result: "OK", ttl: 3600 }
// Delete single key
msg.payload = {
key: "temp:data"
};
// Delete multiple keys
msg.payload = {
keys: ["cache:page1", "cache:page2", "temp:data"]
};
// Returns: { success: true, deleted: 3, keys: [...] }
// Find all user keys
msg.payload = "user:*";
// Returns: { pattern: "user:*", keys: ["user:123", "user:456", "user:789"], count: 3, scanned: true }
// Find session keys with custom scan count
msg.payload = {
pattern: "session:*",
count: 25
};
// Returns: { pattern: "session:*", keys: ["session:abc123", "session:def456"], count: 2, scanned: true }
// Find cache keys
msg.payload = "cache:*";
// Returns: { pattern: "cache:*", keys: ["cache:page1", "cache:page2", "cache:api"], count: 3, scanned: true }
msg.payload = "session:abc123";
// Returns: { key: "session:abc123", ttl: 2847, status: "expires in 2847 seconds" }
msg.payload = {
key: "temp:data",
ttl: 1800
};
// Returns: { success: true, key: "temp:data", ttl: 1800, message: "Expiration set" }
msg.payload = "permanent:key";
// Returns: { success: true, key: "permanent:key", message: "Expiration removed" }
// Simple increment
msg.payload = "page:views";
// Returns: { key: "page:views", value: 1547 }
// Increment by amount
msg.payload = {
key: "score:player1",
amount: 100
};
// Returns: { key: "score:player1", value: 2350, increment: 100 }
msg.payload = {
key: "queue:tasks",
value: {
task: "process_order",
id: 12345
}
};
// Returns: { success: true, key: "queue:tasks", length: 8, operation: "lpush" }
msg.payload = {
key: "queue:tasks",
start: 0,
stop: 4
};
// Returns: { key: "queue:tasks", range: {start: 0, stop: 4}, values: [...], count: 5 }
msg.payload = "queue:tasks";
// Returns: {"task": "process_order", "id": 12345} (first item)
// Single field
msg.payload = {
key: "user:123",
field: "email",
value: "john.doe@example.com"
};
// Returns: { success: true, key: "user:123", field: "email", created: true }
// Multiple fields
msg.payload = {
key: "user:123",
fields: {
name: "John Doe",
age: 30,
city: "New York",
active: true
}
};
// Returns: { success: true, key: "user:123", fields: ["name", "age", "city", "active"], created: 4 }
// Get single field
msg.payload = {
key: "user:123",
field: "email"
};
// Returns: "john.doe@example.com"
// Get all fields
msg.payload = "user:123";
// Returns: { name: "John Doe", age: "30", city: "New York", email: "john.doe@example.com", active: "true" }
msg.payload = {
channel: "notifications",
message: {
type: "alert",
text: "System maintenance in 5 minutes",
timestamp: "2024-01-15T10:30:00Z"
}
};
// Returns: { success: true, channel: "notifications", subscribers: 3, message: "..." }
If you encounter the error "Protocol error, got "\u0015" as reply type byte"
, this indicates an SSL/TLS configuration problem:
Solution: Disable certificate verification in the SSL/TLS configuration:
Common scenarios where this is needed:
Security Note: Disabling certificate verification reduces security. Only use this in trusted environments or when you're certain about the server's identity.
Enable Node-RED debug mode to see detailed connection and operation logs:
DEBUG=redis* node-red
Context Debugging: Enable context debugging by setting the environment variable:
NODE_RED_DEBUG=1 node-red
Check Node-RED logs for messages like:
Context lookup - Type: global, Path: redis_config.host, Result: your-redis-host
Redis connection config - Host: your-redis-host, Port: 6379, Database: 0, Username: not set, Password: set
Common Context Issues:
redis_config.host
)Contributions are welcome! Please read the contributing guidelines and submit pull requests to the GitHub repository.
MIT License - see LICENSE file for details.
cursor
, startCursor
, limit
, and truncated
fieldsFind keys by pattern using Redis SCAN:
// Find all keys starting with "user:"
msg.payload = {
operation: "match",
pattern: "user:*"
};
// Find keys with specific pattern and custom scan count
msg.payload = {
operation: "match",
pattern: "session:*:active",
count: 50 // Number of keys to scan per iteration
};
// Simple pattern matching
msg.payload = "temp:*"; // Find all keys starting with "temp:"
Response format:
{
pattern: "user:*",
keys: ["user:1", "user:2", "user:admin"],
count: 3,
scanned: true
}
Pattern examples:
user:*
- All keys starting with "user:"*:active
- All keys ending with ":active"session:*:data
- Keys with "session:" prefix and ":data" suffixtemp_*
- Keys starting with "temp_"FAQs
A comprehensive Node-RED node for Redis operations with universal payload-based configuration, automatic JSON handling, SSL/TLS support, and advanced pattern matching with pagination
The npm package node-red-contrib-redis-variable receives a total of 13 weekly downloads. As such, node-red-contrib-redis-variable popularity was classified as not popular.
We found that node-red-contrib-redis-variable demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
/Security News
A flawed sandbox in @nestjs/devtools-integration lets attackers run code on your machine via CSRF, leading to full Remote Code Execution (RCE).
Product
Customize license detection with Socket’s new license overlays: gain control, reduce noise, and handle edge cases with precision.
Product
Socket now supports Rust and Cargo, offering package search for all users and experimental SBOM generation for enterprise projects.