Skip to content

fix: add security hardening to Redis plugin command execution#41570

Open
RaikaSurendra wants to merge 3 commits intoappsmithorg:releasefrom
RaikaSurendra:fix/redis-command-security-hardening
Open

fix: add security hardening to Redis plugin command execution#41570
RaikaSurendra wants to merge 3 commits intoappsmithorg:releasefrom
RaikaSurendra:fix/redis-command-security-hardening

Conversation

@RaikaSurendra
Copy link
Contributor

@RaikaSurendra RaikaSurendra commented Feb 20, 2026

Summary

  • Adds a blocklist of 24 dangerous Redis commands to the Redis plugin, preventing authenticated users from executing server administration, data destruction, scripting/RCE, replication, and other high-risk commands through Appsmith
  • Blocked categories: server admin (SHUTDOWN, DEBUG, MONITOR, SAVE, BGSAVE, BGREWRITEAOF), config manipulation (CONFIG), replication/data exfiltration (SLAVEOF, REPLICAOF), module loading/RCE (MODULE), data destruction (FLUSHALL, FLUSHDB), scripting/RCE (EVAL, EVALSHA, SCRIPT), ACL manipulation (ACL), cluster/migration (CLUSTER, MIGRATE), client manipulation (CLIENT), DoS/deserialization risks (KEYS, RESTORE, SWAPDB, SYNC, PSYNC, SENTINEL)
  • All standard data-manipulation commands (GET, SET, HGET, LPUSH, SCAN, TTL, INFO, SUBSCRIBE, PUBLISH, etc.) remain fully functional

Changes

  • RedisPlugin.java: Added BLOCKED_COMMANDS set and validation check before jedis.sendCommand() — blocked commands return a clear error message without reaching the Redis server
  • RedisErrorMessages.java: Added BLOCKED_COMMAND_ERROR_MSG constant with security-section banner (matching MongoDB plugin pattern)
  • RedisPluginTest.java: Fixed testSelectedDatabase and testDefaultDatabase tests (previously used blocked CLIENT INFO command, replaced with SET/GET and PING respectively); added itShouldRejectBlockedCommand and itShouldRejectBlockedCommandWithArgs tests

Test plan

  • Verify PING, GET, SET, MGET, MSET, DEL, HGET, LPUSH, SCAN, TTL, INFO, DBSIZE, SELECT, SUBSCRIBE, PUBLISH all work normally
  • Verify FLUSHALL, SHUTDOWN, CONFIG GET requirepass, EVAL "return 1" 0, SLAVEOF, MODULE LOAD all return the blocked command error
  • Verify existing Redis queries in production apps continue to function
  • Run full Redis plugin test suite: mvn test -pl appsmith-plugins/redisPlugin

Summary by CodeRabbit

  • Bug Fixes

    • Block specific unsafe Redis commands (case-insensitive) and return clear, descriptive error messages that reference the command attempted.
    • Improve validation to reject disallowed commands earlier in execution.
  • Tests

    • Added/updated end-to-end tests to verify blocked-command behavior (with and without arguments), case-insensitivity, and cross-database command execution.

Add a blocklist of dangerous Redis commands to prevent authenticated
users from executing server administration, data destruction, scripting,
replication, and other high-risk commands through the Redis plugin.

Blocked command categories:
- Server admin: SHUTDOWN, DEBUG, MONITOR, SAVE, BGSAVE, BGREWRITEAOF
- Config manipulation: CONFIG
- Replication/exfiltration: SLAVEOF, REPLICAOF
- Module loading (RCE): MODULE
- Data destruction: FLUSHALL, FLUSHDB
- Scripting/RCE: EVAL, EVALSHA, SCRIPT
- ACL manipulation: ACL
- Cluster/migration: CLUSTER, MIGRATE
- Client manipulation: CLIENT
- DoS/deserialization: KEYS, RESTORE, SWAPDB, SYNC, PSYNC, SENTINEL

Updated existing tests that relied on blocked CLIENT command, and added
new tests verifying blocked commands return appropriate error responses.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 20, 2026

No actionable comments were generated in the recent review. 🎉


Walkthrough

Adds an early security filter in the Redis plugin that extracts the command name, blocks a defined set of unsafe Redis commands (case-insensitive), returns a specific blocked-command error, tightens typing for command payloads, and updates tests to verify rejection cases and SELECT db behavior.

Changes

Cohort / File(s) Summary
Plugin core & errors
app/server/appsmith-plugins/redisPlugin/src/main/java/com/external/plugins/RedisPlugin.java, app/server/appsmith-plugins/redisPlugin/src/main/java/com/external/plugins/exceptions/RedisErrorMessages.java
Adds BLOCKED_COMMANDS set and new BLOCKED_COMMAND_ERROR_MSG; extracts commandName early, blocks disallowed commands (case-insensitive) before resolving Protocol.Command, updates invalid-command messaging to use commandName, and tightens cmdAndArgs typing to Map<String, Object>.
Tests
app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java
Replaces/renames default-database test with blocked-command tests: verifies blocking of FLUSHALL, CONFIG GET requirepass, and case-insensitive commands; adds SELECT DB7 end-to-end test and updates assertions to check error title/body and command mention.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Plugin as RedisPlugin
    participant Errors as RedisErrorMessages
    participant Redis as RedisServer

    Client->>Plugin: execute(command + args)
    Plugin->>Plugin: parse payload -> cmdAndArgs (Map<String,Object>)\nextract commandName (normalize case)
    alt commandName in BLOCKED_COMMANDS
      Plugin->>Errors: format(BLOCKED_COMMAND_ERROR_MSG, commandName)
      Plugin-->>Client: respond(PLUGIN_EXECUTE_ARGUMENT_ERROR, formattedError)
    else
      Plugin->>Redis: resolve Protocol.Command using commandName\nforward command + args
      Redis-->>Plugin: result / error
      Plugin-->>Client: respond(result/error)
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🔍 A name extracted, a rule applied,
Unsafe commands politely denied,
Typing tightened, tests attest,
DB seven checked, errors dressed,
The plugin guards Redis with pride.

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description is detailed and complete, covering the summary, changes, and test plan; however, it does not reference a linked issue or use the required template structure (missing 'Fixes #Issue' and 'Automation' sections). Link the issue reference (e.g., 'Fixes #41570') and include the required template sections (Automation and Communication checkboxes) for consistency with repository standards.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main security hardening change—adding a blocklist to the Redis plugin—which aligns with the core objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java (1)

377-415: Assert the error message content, not just the error type.

Both tests only verify the error title (PLUGIN_EXECUTE_ARGUMENT_ERROR). Since the same error type is returned for empty body, parse failures, and invalid commands, a regression could swap the rejection reason without failing the test. The error message does include the command name, so assert that it's present:

Suggested assertion additions
 // In itShouldRejectBlockedCommand:
                     assertFalse(result.getIsExecutionSuccess());
                     assertEquals(AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR.getTitle(), result.getTitle());
+                    assertTrue(result.getBody().toString().contains("FLUSHALL"));
 
 // In itShouldRejectBlockedCommandWithArgs:
                     assertFalse(result.getIsExecutionSuccess());
                     assertEquals(AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR.getTitle(), result.getTitle());
+                    assertTrue(result.getBody().toString().contains("CONFIG"));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java`
around lines 377 - 415, The tests itShouldRejectBlockedCommand and
itShouldRejectBlockedCommandWithArgs currently only assert the error title; add
an assertion that the returned error message includes the offending command text
so regressions changing rejection reason will fail. After the existing title
assertions, assert that the error payload (e.g. result.getBody() or
result.getMessage(), whichever holds the plugin error text) is not null and
contains "FLUSHALL" in it for itShouldRejectBlockedCommand and contains "CONFIG
GET requirepass" for itShouldRejectBlockedCommandWithArgs; keep the existing
title checks intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/server/appsmith-plugins/redisPlugin/src/main/java/com/external/plugins/RedisPlugin.java`:
- Around line 65-91: Update the BLOCKED_COMMANDS set in RedisPlugin
(BLOCKED_COMMANDS) to include "FUNCTION" as a blocked command (add the literal
"FUNCTION" into the Set.of(...) alongside the other commands) and update the
surrounding comment to note this is a forward-looking block for Redis 7+ due to
persistent FUNCTION LOAD risks (CVE-style sandbox escape). Do not add "EVAL_RO"
or "EVALSHA_RO" to the blocklist; instead add a brief comment that read-only
eval variants should be controlled via ACL roles when/if support for Redis 7+
and a newer Jedis client is added.

---

Nitpick comments:
In
`@app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java`:
- Around line 377-415: The tests itShouldRejectBlockedCommand and
itShouldRejectBlockedCommandWithArgs currently only assert the error title; add
an assertion that the returned error message includes the offending command text
so regressions changing rejection reason will fail. After the existing title
assertions, assert that the error payload (e.g. result.getBody() or
result.getMessage(), whichever holds the plugin error text) is not null and
contains "FLUSHALL" in it for itShouldRejectBlockedCommand and contains "CONFIG
GET requirepass" for itShouldRejectBlockedCommandWithArgs; keep the existing
title checks intact.

- Add FUNCTION to blocked commands set (Redis 7+ forward-looking:
  persistent server-side code loading with CVE-2025-49844 sandbox
  escape risk). Add comment noting EVAL_RO/EVALSHA_RO should be
  controlled via Redis ACL roles rather than blanket-blocked.
- Strengthen blocked command test assertions to verify the error
  message contains the offending command name, not just the error
  type. Prevents regressions that swap rejection reasons silently.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/server/appsmith-plugins/redisPlugin/src/main/java/com/external/plugins/RedisPlugin.java (1)

125-125: Raw Map type — pre-existing but worth a future cleanup.

getCommandAndArgs returns a raw Map. Since you're touching the call site, consider typing it as Map<String, Object> to match the declaration at line 242 and avoid unchecked casts.

Suggested fix
-                        Map cmdAndArgs = getCommandAndArgs(query.trim());
+                        Map<String, Object> cmdAndArgs = getCommandAndArgs(query.trim());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-plugins/redisPlugin/src/main/java/com/external/plugins/RedisPlugin.java`
at line 125, The call site uses a raw Map for the result of getCommandAndArgs;
change the declaration of cmdAndArgs to a parameterized type (Map<String,
Object> cmdAndArgs = getCommandAndArgs(query.trim())) to match the method's
intended return and the existing typed declaration elsewhere, eliminating the
raw type and any unchecked cast warnings; update any subsequent uses of
cmdAndArgs if necessary to compile with the generic type.
app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java (1)

377-399: Solid negative tests for both no-arg and with-arg blocked commands.

Both tests verify the error title and that the body contains the offending command name — good for regression detection.

Minor suggestion: consider adding a case-sensitivity test (e.g., "flushall" lowercase) to explicitly prove the uppercase normalization in getCommandAndArgs works as expected. Not critical since existing code uppercases, but it would document the contract.

Also applies to: 401-423

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java`
around lines 377 - 399, Add a new negative test to assert case-insensitive
blocking by calling pluginExecutor.execute with a lowercase (or mixed-case)
blocked command body (e.g., actionConfiguration.setBody("flushall")) — similar
assertions to itShouldRejectBlockedCommand: assert execution failed, title
equals AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR.getTitle(), body is not
null and contains the offending command name; this documents and verifies the
uppercase normalization done in getCommandAndArgs and protects against
regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@app/server/appsmith-plugins/redisPlugin/src/main/java/com/external/plugins/RedisPlugin.java`:
- Line 125: The call site uses a raw Map for the result of getCommandAndArgs;
change the declaration of cmdAndArgs to a parameterized type (Map<String,
Object> cmdAndArgs = getCommandAndArgs(query.trim())) to match the method's
intended return and the existing typed declaration elsewhere, eliminating the
raw type and any unchecked cast warnings; update any subsequent uses of
cmdAndArgs if necessary to compile with the generic type.

In
`@app/server/appsmith-plugins/redisPlugin/src/test/java/com/external/plugins/RedisPluginTest.java`:
- Around line 377-399: Add a new negative test to assert case-insensitive
blocking by calling pluginExecutor.execute with a lowercase (or mixed-case)
blocked command body (e.g., actionConfiguration.setBody("flushall")) — similar
assertions to itShouldRejectBlockedCommand: assert execution failed, title
equals AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR.getTitle(), body is not
null and contains the offending command name; this documents and verifies the
uppercase normalization done in getCommandAndArgs and protects against
regressions.

- Parameterize raw Map return type in getCommandAndArgs() and its call
  site to Map<String, Object>, eliminating unchecked cast warnings.
- Add itShouldRejectBlockedCommandCaseInsensitive test to document and
  verify that lowercase command input is normalized to uppercase before
  blocklist lookup.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant