fix(plugin-redis): list databases when the server refuses CONFIG or INFO, and the Redis defects found with it - #3038
Merged
Merged
Conversation
…block and report a block lost to a reconnect
…s own row instead of the session's
…NKNOWN and no expiry
… zero in Redis Cluster
…y like the editor bar
… return a session a read left elsewhere
…before it is sent
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Automations to automatically generate PRs for you. |
This was referenced Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3036.
The reported bug
RedisPluginDriver.databaseCountreadCONFIG GET databasesthroughRedisCommandChannel.run, which throws on any error reply. So the function's own "16 if it does not answer" fallback only ever caught a successful reply of the wrong shape. ElastiCache and Azure Cache for Redis removeCONFIG(ERR unknown command), Memorystore and ACL users withoutconfig|getdeny it (NOPERM), and every one of them got the error in the window instead of a database list.INFO keyspacein the same two functions failed the same way for ACL users outside@dangerous.The issue suggested
try?, which would also have turned a dropped socket,-BUSY,-NOAUTHand a+QUEUEDfrom an openMULTIinto a healthy-looking list of 16. Instead:RedisMetadataReadnames the one rule: a read the app makes on its own is declined when the error class isERRorNOPERM(first word, compared whole). Everything else still throws, and every decline is logged as a notice.RedisDatabaseCountresolves the count once for both the sidebar and the database list: the server's own count when it gives one, otherwise the largest of 16, the highest databaseINFO keyspacenames plus one, and the session's database plus one. Azure allows 64 databases and Memorystore 100, both withCONFIGrefused, so a flat 16 would hide a populateddb20.INFO keyspaceleaves key counts unknown (NULL) instead of failing the list.RedisServerInfo.modealso reads Valkey 8'sserver_mode. Without it the new fallback would list 16 empty databases for a Standalone connection pointed at a Valkey Sentinel port, where today it at least errors.SELECTthe server refuses (a single-database service answeringERR DB index is out of range) now shows the server's error on the tab the click retargeted, instead of an empty grid.Everything else found while investigating, all fixed here at your request
fix(plugin-redis): keep the app's own commands out of a user's MULTI block…CONFIG/INFO, the key browser'sSCAN/TYPE, the key tree and the health monitor'sPINGall joined aMULTIblock the user left open: an allowed one paddedEXECwith extra replies, a refused one aborted the block withEXECABORT. A reconnect replayed into a new session that no longer held the block orWATCH. A pureRedisSessionFootprintnow tracks the block andWATCHfrom the server's replies; every send states a scope (.session,.outsideBlock,.cleanSession), app reads are held back from a block, and a lost block orWATCHis reported to the user's next command instead of replayed.fix(plugin-redis): read, count and export each Redis database from its own row…SELECTfailed showed the session's keys under its own name after a refresh.KEYBROWSEtakesDB <n>, a table's own browse and export queries carry it, and a read on another database visits it and returns.fix(plugin-redis): write Redis grid edits to their row's database…SELECT n…SELECT home, queued inside the save'sMULTI). The session tracks where it is and where it belongs, so a read abandoned part way (a cancelled stream) cannot leave the next command on the database it was visiting. ASELECTtyped inside a block now answersQUEUEDand is recorded, soEXEC's replies stay paired with their commands.fix(plugin-redis): show a refused TYPE or TTL as unknown…UNKNOWN, TTL-1(which means "never expires") and an empty collection. Those cells are now NULL, and a Value edit is skipped when the type is unknown.fix(plugin-redis): report a shard's refusal instead of counting it as zero in Redis ClusterDELreported success and a sidebar count came up short. A shard's error now wins for every policy butone_succeeded, andSCRIPT EXISTSfolds element-wise.fix(editor): stop offering Explain on Redis…DEBUG OBJECT, which Redis 7+ refuses by default and ElastiCache and Azure remove, and the Query menu enabled Explain where the editor bar did not. OnecanExplainrule now drives both. Only Redis used the driver-built fallback that is removed.fix(sidebar): show a failed Redis key tree load instead of No itemsSCAN, an open block, a dropped socket) read as "No items". It now shows the error row, with Refresh on the Keys section.fix(connection-form): let Redis Database Index reach every database…and…refuse to save a stepper value outside its rangefix(ios): report a refused or queued Redis SCAN…SCANinto an empty key list.Verification
--rename-command CONFIG ''with--databases 32and a key in db20: fails withCONFIG: ERR unknown command→ 21 databases with counts. ACL+@all -config→ 16 with counts. ACL+@all -@dangerous(refusedCONFIGandINFO) → 16 with unknown counts.MULTI; SETthen sidebar reads and the health probe:EXECreturned 5 replies → 1. A user without+ping:EXECABORT→ applied. Connection killed mid-block: a silent replay onto the new session → "the open MULTI block was lost and nothing in it ran".ERR DB index is out of range. Deleting a db3 row deleted db0's key of the same name → db3's.~app:*user:UNKNOWN | -1→ NULL. Two-master cluster: partialDELreported 1 → the shard'sNOPERM.AllPlugins). Lint is clean on every line this branch wrote.code-reviewat high effort and bysecurity-review. The review's findings are fixed in the branch: grid writes following the session and a cancelled stream able to leave the session on a visited database (the grid-write commit), an out-of-range stepper value being saved (the range commit), and, from a second pass over those fixes, the health monitor's ungatedPINGsending the session home in the middle of a visit (each command now moves to its own database right before it is sent). The security review found nothing at its exploitability bar.Not covered
RedisConnectionModeUITests) is written but could not run locally: macOS held the runner at an "XCTest is trying to Enable UI Automation" password prompt.CONFIGand serve db0 only, so they now list 16 databases and every one but db0 shows the server's error when clicked. That replaces an empty sidebar. No read-only command reports the count, and probing withSELECTwould queue into an openMULTI.SCANnow gets a connect error instead of an empty key list, because iOS has no "connected, but the key list failed" state.