diff --git a/src/scripting.c b/src/scripting.c index dbbd50eaf..a0f6c31f4 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -603,6 +603,15 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { goto cleanup; } + /* This check is for EVAL_RO, EVALSHA_RO. We want to allow only read only commands */ + if ((server.lua_caller->cmd->proc == evalRoCommand || + server.lua_caller->cmd->proc == evalShaRoCommand) && + (cmd->flags & CMD_WRITE)) + { + luaPushError(lua, "Write commands are not allowed from read-only scripts"); + goto cleanup; + } + /* Check the ACLs. */ int acl_errpos; int acl_retval = ACLCheckAllPerm(c,&acl_errpos); @@ -1696,6 +1705,10 @@ void evalCommand(client *c) { evalGenericCommandWithDebugging(c,0); } +void evalRoCommand(client *c) { + evalCommand(c); +} + void evalShaCommand(client *c) { if (sdslen(c->argv[1]->ptr) != 40) { /* We know that a match is not possible if the provided SHA is @@ -1713,6 +1726,10 @@ void evalShaCommand(client *c) { } } +void evalShaRoCommand(client *c) { + evalShaCommand(c); +} + void scriptCommand(client *c) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { const char *help[] = { diff --git a/src/server.c b/src/server.c index 95202f9fa..46151619d 100644 --- a/src/server.c +++ b/src/server.c @@ -910,10 +910,18 @@ struct redisCommand redisCommandTable[] = { "no-script may-replicate @scripting", 0,evalGetKeys,0,0,0,0,0,0}, + {"eval_ro",evalRoCommand,-3, + "no-script @scripting", + 0,evalGetKeys,0,0,0,0,0,0}, + {"evalsha",evalShaCommand,-3, "no-script may-replicate @scripting", 0,evalGetKeys,0,0,0,0,0,0}, + {"evalsha_ro",evalShaRoCommand,-3, + "no-script @scripting", + 0,evalGetKeys,0,0,0,0,0,0}, + {"slowlog",slowlogCommand,-2, "admin random ok-loading ok-stale", 0,NULL,0,0,0,0,0,0}, diff --git a/src/server.h b/src/server.h index 127f24929..146c57c19 100644 --- a/src/server.h +++ b/src/server.h @@ -2644,7 +2644,9 @@ void memoryCommand(client *c); void clientCommand(client *c); void helloCommand(client *c); void evalCommand(client *c); +void evalRoCommand(client *c); void evalShaCommand(client *c); +void evalShaRoCommand(client *c); void scriptCommand(client *c); void timeCommand(client *c); void bitopCommand(client *c); diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index 0efe34cad..d5c876f3d 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -320,6 +320,17 @@ start_server {tags {"scripting"}} { r eval {return 'hello' --trailing comment} 0 } {hello} + test {EVAL_RO - Successful case} { + r set foo bar + assert_equal bar [r eval_ro {return redis.call('get', KEYS[1]);} 1 foo] + } + + test {EVAL_RO - Cannot run write commands} { + r set foo bar + catch {r eval_ro {redis.call('del', KEYS[1]);} 1 foo} e + set e + } {*Write commands are not allowed from read-only scripts*} + test {SCRIPTING FLUSH - is able to clear the scripts cache?} { r set mykey myval set v [r evalsha fd758d1589d044dd850a6f05d52f2eefd27f033f 1 mykey]