diff --git a/apps/desktop/src/lib/ai/__tests__/aiRedisConsoleRouting.spec.ts b/apps/desktop/src/lib/ai/__tests__/aiRedisConsoleRouting.spec.ts index 2c3a3f3888..74f84fb9db 100644 --- a/apps/desktop/src/lib/ai/__tests__/aiRedisConsoleRouting.spec.ts +++ b/apps/desktop/src/lib/ai/__tests__/aiRedisConsoleRouting.spec.ts @@ -25,7 +25,7 @@ describe("AI Redis console routing", () => { it("uses the console safety path and rejects unavailable command input", () => { expect(classifyRedisCommandSafety("CONFIG SET requirepass secret")).toBe("blocked"); - expect(classifyRedisCommandSafety("FLUSHDB")).toBe("confirm"); + expect(classifyRedisCommandSafety("FLUSHDB")).toBe("blocked"); expect(classifyRedisCommandSafety("SET issue:846 fixed")).toBe("write"); expect(classifyRedisCommandSafety("INFO server")).toBe("allowed"); diff --git a/apps/desktop/src/lib/redis/redisCommandSafety.ts b/apps/desktop/src/lib/redis/redisCommandSafety.ts index 67fc907632..e059db6543 100644 --- a/apps/desktop/src/lib/redis/redisCommandSafety.ts +++ b/apps/desktop/src/lib/redis/redisCommandSafety.ts @@ -2,7 +2,7 @@ import { isRedisMonitorCommand } from "./redisMonitor"; export type RedisCommandSafety = "allowed" | "write" | "confirm" | "blocked"; -const BLOCKED_COMMANDS = new Set(["KEYS", "FLUSHALL", "SHUTDOWN", "CONFIG", "SAVE", "BGSAVE", "SLAVEOF", "REPLICAOF", "MIGRATE", "MODULE", "SCRIPT", "EVAL", "EVALSHA"]); +const BLOCKED_COMMANDS = new Set(["KEYS", "FLUSHALL", "FLUSHDB", "SHUTDOWN", "CONFIG", "SAVE", "BGSAVE", "SLAVEOF", "REPLICAOF", "MIGRATE", "MODULE", "SCRIPT", "EVAL", "EVALSHA"]); // Explicit allowlist matching the backend's read-command classification. // Unknown commands fall through to "blocked" (fail-closed). @@ -199,7 +199,6 @@ const CONFIRM_COMMANDS = new Set([ "ZUNIONSTORE", "PFMERGE", "GEOSEARCHSTORE", - "FLUSHDB", ]); const WRITE_COMMANDS = new Set([ diff --git a/apps/desktop/src/lib/redis/redisCommandTable.ts b/apps/desktop/src/lib/redis/redisCommandTable.ts index 6b405cea7a..e71d657b2b 100644 --- a/apps/desktop/src/lib/redis/redisCommandTable.ts +++ b/apps/desktop/src/lib/redis/redisCommandTable.ts @@ -377,7 +377,7 @@ const RAW_COMMANDS: Record = { DBSIZE: [1, "server"], FAILOVER: [-1, "server", "confirm"], FLUSHALL: [-1, "server", "blocked"], - FLUSHDB: [-1, "server", "confirm"], + FLUSHDB: [-1, "server", "blocked"], INFO: [-1, "server"], LASTSAVE: [1, "server"], "MEMORY USAGE": [-3, "server"], diff --git a/crates/dbx-driver-redis/src/lib.rs b/crates/dbx-driver-redis/src/lib.rs index 306c073808..f30ba6cc68 100644 --- a/crates/dbx-driver-redis/src/lib.rs +++ b/crates/dbx-driver-redis/src/lib.rs @@ -2134,7 +2134,7 @@ pub fn classify_command(command: &str) -> RedisCommandSafety { | "ZREM" | "ZPOPMAX" | "ZPOPMIN" | "ZMPOP" | "BZMPOP" | "BZPOPMAX" | "BZPOPMIN" | "ZREMRANGEBYLEX" | "ZREMRANGEBYRANK" | "ZREMRANGEBYSCORE" | "XDEL" | "XTRIM" | "MOVE" | "SORT" | "SDIFFSTORE" | "SINTERSTORE" | "SUNIONSTORE" | "ZDIFFSTORE" | "ZINTERSTORE" | "ZRANGESTORE" | "ZUNIONSTORE" | "PFMERGE" - | "GEOSEARCHSTORE" | "FLUSHDB" => RedisCommandSafety::Confirm, + | "GEOSEARCHSTORE" => RedisCommandSafety::Confirm, "APPEND" | "BITFIELD" | "BITOP" | "COPY" | "DECR" | "DECRBY" | "GEOADD" | "GEORADIUS" | "GEORADIUSBYMEMBER" | "GETEX" | "GETSET" | "INCR" | "INCRBY" | "INCRBYFLOAT" | "SET" | "SETEX" | "PSETEX" | "SETNX" | "SETRANGE" | "MSET" | "MSETNX" | "PERSIST" | "HSET" | "HMSET" | "HINCRBY" | "HINCRBYFLOAT" | "HSETNX" @@ -7686,7 +7686,7 @@ mod tests { assert_eq!(classify_command("GETEX"), RedisCommandSafety::Write); assert_eq!(classify_command("XREADGROUP"), RedisCommandSafety::Write); assert_eq!(classify_command("del"), RedisCommandSafety::Confirm); - assert_eq!(classify_command("flushdb"), RedisCommandSafety::Confirm); + assert_eq!(classify_command("flushdb"), RedisCommandSafety::Blocked); assert_eq!(classify_command("JSON.DEL"), RedisCommandSafety::Confirm); assert_eq!(classify_command("JSON.FORGET"), RedisCommandSafety::Confirm); assert_eq!(classify_command("JSON.CLEAR"), RedisCommandSafety::Confirm); diff --git a/packages/app-tests/redisCommandSafety.test.ts b/packages/app-tests/redisCommandSafety.test.ts index 0b4e8b776d..faca53c3c2 100644 --- a/packages/app-tests/redisCommandSafety.test.ts +++ b/packages/app-tests/redisCommandSafety.test.ts @@ -7,7 +7,7 @@ test("classifies normal Redis writes separately from destructive commands", () = assert.equal(classifyRedisCommandSafety("HSET hash field value"), "write"); assert.equal(classifyRedisCommandSafety("LPUSH queue value"), "write"); assert.equal(classifyRedisCommandSafety("DEL session:1"), "confirm"); - assert.equal(classifyRedisCommandSafety("FLUSHDB"), "confirm"); + assert.equal(classifyRedisCommandSafety("FLUSHDB"), "blocked"); assert.equal(classifyRedisCommandSafety("KEYS *"), "blocked"); assert.equal(classifyRedisCommandSafety("GET session:1"), "allowed"); }); diff --git a/packages/app-tests/redisCommandTable.test.ts b/packages/app-tests/redisCommandTable.test.ts index fe4db571b9..121f46263b 100644 --- a/packages/app-tests/redisCommandTable.test.ts +++ b/packages/app-tests/redisCommandTable.test.ts @@ -83,5 +83,5 @@ test("normal writes do not require confirmation but destructive commands do", () assert.equal(resolveRedisCommandSpec(["HSET"])?.safety, "write"); assert.equal(resolveRedisCommandSpec(["LPUSH"])?.safety, "write"); assert.equal(resolveRedisCommandSpec(["DEL"])?.safety, "confirm"); - assert.equal(resolveRedisCommandSpec(["FLUSHDB"])?.safety, "confirm"); + assert.equal(resolveRedisCommandSpec(["FLUSHDB"])?.safety, "blocked"); }); diff --git a/packages/app-tests/redisSyntaxDiagnostics.test.ts b/packages/app-tests/redisSyntaxDiagnostics.test.ts index 6db9107f3d..9194700c7e 100644 --- a/packages/app-tests/redisSyntaxDiagnostics.test.ts +++ b/packages/app-tests/redisSyntaxDiagnostics.test.ts @@ -71,8 +71,8 @@ test("highlights destructive commands as warning", () => { assert.match(diags[0].message, /Dangerous command 'DEL'/); }); -test("highlights flushall as blocked error but flushdb as confirm warning", () => { - // Aligned with the execution safety classification: FLUSHALL=blocked, FLUSHDB=confirm. +test("highlights flushall and flushdb as blocked errors", () => { + // Aligned with the execution safety classification: FLUSHALL/FLUSHDB=blocked. const all = buildRedisSyntaxDiagnostics("FLUSHALL"); assert.equal(all.length, 1); assert.equal(all[0].severity, "error"); @@ -80,8 +80,8 @@ test("highlights flushall as blocked error but flushdb as confirm warning", () = const db = buildRedisSyntaxDiagnostics("FLUSHDB"); assert.equal(db.length, 1); - assert.equal(db[0].severity, "warning"); - assert.match(db[0].message, /Dangerous command 'FLUSHDB'/); + assert.equal(db[0].severity, "error"); + assert.match(db[0].message, /Blocked command 'FLUSHDB'/); }); test("subcommands resolve via MAIN SUB key", () => {