From 421f4d85778ec20f98bd7ba7b735c78683fd0b65 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 22 Sep 2026 01:39:29 +0200 Subject: [PATCH] =?UTF-8?q?redis:=20a=20consumer's=20ACL=20loses=20the=20d?= =?UTF-8?q?angerous=20category=20=E2=80=94=20a=20key=20pattern=20does=20no?= =?UTF-8?q?t=20confine=20FLUSHALL=20(novox/hq=20issue=20080)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/redis/client.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/redis/client.ts b/modules/redis/client.ts index d4eb35d..7ca6744 100644 --- a/modules/redis/client.ts +++ b/modules/redis/client.ts @@ -98,9 +98,14 @@ export class RedisClient { * clears any prior rules so the call is idempotent, then the user is enabled with the given * password, confined to keys matching `:*`, and allowed the ordinary command set. The * consumer connects as this user and can touch nothing outside its prefix. + * + * Minus the dangerous category: a key pattern confines commands that name keys, and FLUSHALL, + * FLUSHDB, CONFIG, SHUTDOWN and the rest of `@dangerous` name none — with `+@all` alone a + * consumer scoped to its own keys could still wipe the server (novox/hq issue 080). KEYS goes + * with them; SCAN stays, and is what a consumer should use anyway. */ async createAclUser(username: string, password: string, keyspacePrefix: string): Promise { - await this.command("ACL", "SETUSER", username, "reset", "on", `>${password}`, `~${keyspacePrefix}:*`, "+@all"); + await this.command("ACL", "SETUSER", username, "reset", "on", `>${password}`, `~${keyspacePrefix}:*`, "+@all", "-@dangerous"); } async deleteAclUser(username: string): Promise {