Skip to content

workspace key prefixing uses a hand-rolled key walker and leaves five *STORE destinations unprefixed #668

Description

@TinDang97

The gap

src/workspace/mod.rs rewrites a command's key positions to prefix them with the workspace tag. It does this with a hand-rolled key walker — four hardcoded name lists (ALL_KEYS_COMMANDS, TWO_KEY_COMMANDS, STORE_NUMKEYS_COMMANDS, STORE_ALL_COMMANDS) plus a handful of if cmd.eq_ignore_ascii_case(...) special cases — and everything that matches none of them falls through to a single-key default:

// --- Default: single-key command — prefix args[0] only ---
let mut out = args.to_vec();
out[0] = prefix_frame(&args[0], ws_id);
out

For a command that writes a key the walker does not know about, that destination is written unprefixed — i.e. outside the workspace.

Not covered by any of the four lists today:

Command Destination position
GEOSEARCHSTORE args[0] (prefixed by accident — but args[1], the SOURCE, is not)
ZRANGESTORE args[0] (same accident; source unprefixed)
PFMERGE args[0] (same accident; sources unprefixed)
SORT ... STORE dst keyword-positional, never prefixed
GEORADIUS*/... STORE|STOREDIST dst keyword-positional, never prefixed
BITOP dest src... not in any list
COPY, LMOVE etc. in TWO_KEY_COMMANDS, fine

GEORADIUS ... STORE joined this list with moon#645, which is what surfaced the gap — the file carried a comment claiming it handled that case:

// --- GEORADIUS/GEORADIUSBYMEMBER STORE variants: args[0] is key, STORE/STOREDIST keys ---
// For simplicity, prefix args[0] only (standard single-key behavior).
// The STORE dest key is a separate concern handled later if needed.

moon#645 replaced that comment with an accurate one, but did not fix the walker — appending one more name to a list that is already wrong for five other commands would make the drift worse, not better.

Why this is one bug, not six

This is the fifth independent key-extraction implementation in the tree (acl::keyspec::command_key_positions, tracking::invalidation::written_of, command::metadata's first_key/last_key, COMMAND GETKEYS, and this). moon#582 already consolidated the first two behind command_key_positions, and cross_shard_multikey_rejection (moon#592) and the ACL layer both call it — so the correct layouts for every command above already exist in the tree and are already tested.

Proposed fix

Replace the four lists with acl::keyspec::command_key_positions, prefixing exactly the positions it reports and leaving the rest of the argv alone. KeyPositions::AtPlusComputed (SORT ... BY w_*) needs a decision: the computed weight keys cannot be enumerated, so either they are left unprefixed (leaking reads across the workspace boundary) or SORT ... BY is refused inside a workspace.

Severity

Depends on whether workspaces are a shipped multi-tenancy guarantee or a dev-only feature. If the former this is a tenant-isolation break, not a cosmetic gap: a SORT k STORE dst inside workspace A writes a global dst that workspace B can read and overwrite.

Found while implementing moon#645; not introduced by it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions