Skip to content

Commit e85e3f7

Browse files
authored
[SF-11323] disallow sending command getkeys (#7)
* [SF-11323] disallow sending command getkeys
1 parent 6ccf553 commit e85e3f7

File tree

4 files changed

+771
-706
lines changed

4 files changed

+771
-706
lines changed

redis/connection.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,14 @@ def pack_command(self, *args):
878878
elif b" " in args[0]:
879879
args = tuple(args[0].split()) + args[1:]
880880

881+
# `COMMAND GETKEYS` can crash redis server entirely under certain conditions.
882+
# So we have decided to make sure that `COMMAND GETKEYS` is never sent to the
883+
# server. If you need to send `COMMAND GETKEYS` to the server, please reach out
884+
# to Doogie and Zach to discuss the use case.
885+
# ref: https://github.com/redis/redis/pull/12380
886+
if len(args) > 1 and args[0].lower() == b'command' and args[1].lower().startswith(b'getkeys'):
887+
raise Exception(f'Redis command "{args[0].decode()} {args[1].decode()}" is not supported')
888+
881889
buff = SYM_EMPTY.join((SYM_STAR, str(len(args)).encode(), SYM_CRLF))
882890

883891
buffer_cutoff = self._buffer_cutoff

tests/test_cluster.py

Lines changed: 124 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,8 +1420,9 @@ def test_time(self, r):
14201420

14211421
@skip_if_server_version_lt("4.0.0")
14221422
def test_memory_usage(self, r):
1423-
r.set("foo", "bar")
1424-
assert isinstance(r.memory_usage("foo"), int)
1423+
with pytest.raises(Exception):
1424+
r.set("foo", "bar")
1425+
assert isinstance(r.memory_usage("foo"), int)
14251426

14261427
@skip_if_server_version_lt("4.0.0")
14271428
@skip_if_redis_enterprise()
@@ -1732,80 +1733,87 @@ def test_cluster_sunionstore(self, r):
17321733

17331734
@skip_if_server_version_lt("6.2.0")
17341735
def test_cluster_zdiff(self, r):
1735-
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 3})
1736-
r.zadd("{foo}b", {"a1": 1, "a2": 2})
1737-
assert r.zdiff(["{foo}a", "{foo}b"]) == [b"a3"]
1738-
assert r.zdiff(["{foo}a", "{foo}b"], withscores=True) == [b"a3", b"3"]
1736+
with pytest.raises(Exception):
1737+
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 3})
1738+
r.zadd("{foo}b", {"a1": 1, "a2": 2})
1739+
assert r.zdiff(["{foo}a", "{foo}b"]) == [b"a3"]
1740+
assert r.zdiff(["{foo}a", "{foo}b"], withscores=True) == [b"a3", b"3"]
17391741

17401742
@skip_if_server_version_lt("6.2.0")
17411743
def test_cluster_zdiffstore(self, r):
1742-
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 3})
1743-
r.zadd("{foo}b", {"a1": 1, "a2": 2})
1744-
assert r.zdiffstore("{foo}out", ["{foo}a", "{foo}b"])
1745-
assert r.zrange("{foo}out", 0, -1) == [b"a3"]
1746-
assert r.zrange("{foo}out", 0, -1, withscores=True) == [(b"a3", 3.0)]
1744+
with pytest.raises(Exception):
1745+
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 3})
1746+
r.zadd("{foo}b", {"a1": 1, "a2": 2})
1747+
assert r.zdiffstore("{foo}out", ["{foo}a", "{foo}b"])
1748+
assert r.zrange("{foo}out", 0, -1) == [b"a3"]
1749+
assert r.zrange("{foo}out", 0, -1, withscores=True) == [(b"a3", 3.0)]
17471750

17481751
@skip_if_server_version_lt("6.2.0")
17491752
def test_cluster_zinter(self, r):
1750-
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 1})
1751-
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1752-
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1753-
assert r.zinter(["{foo}a", "{foo}b", "{foo}c"]) == [b"a3", b"a1"]
1754-
# invalid aggregation
1755-
with pytest.raises(DataError):
1756-
r.zinter(["{foo}a", "{foo}b", "{foo}c"], aggregate="foo", withscores=True)
1757-
# aggregate with SUM
1758-
assert r.zinter(["{foo}a", "{foo}b", "{foo}c"], withscores=True) == [
1759-
(b"a3", 8),
1760-
(b"a1", 9),
1761-
]
1762-
# aggregate with MAX
1763-
assert r.zinter(
1764-
["{foo}a", "{foo}b", "{foo}c"], aggregate="MAX", withscores=True
1765-
) == [(b"a3", 5), (b"a1", 6)]
1766-
# aggregate with MIN
1767-
assert r.zinter(
1768-
["{foo}a", "{foo}b", "{foo}c"], aggregate="MIN", withscores=True
1769-
) == [(b"a1", 1), (b"a3", 1)]
1770-
# with weights
1771-
assert r.zinter({"{foo}a": 1, "{foo}b": 2, "{foo}c": 3}, withscores=True) == [
1772-
(b"a3", 20),
1773-
(b"a1", 23),
1774-
]
1753+
with pytest.raises(Exception):
1754+
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 1})
1755+
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1756+
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1757+
assert r.zinter(["{foo}a", "{foo}b", "{foo}c"]) == [b"a3", b"a1"]
1758+
# invalid aggregation
1759+
with pytest.raises(DataError):
1760+
r.zinter(["{foo}a", "{foo}b", "{foo}c"], aggregate="foo", withscores=True)
1761+
# aggregate with SUM
1762+
assert r.zinter(["{foo}a", "{foo}b", "{foo}c"], withscores=True) == [
1763+
(b"a3", 8),
1764+
(b"a1", 9),
1765+
]
1766+
# aggregate with MAX
1767+
assert r.zinter(
1768+
["{foo}a", "{foo}b", "{foo}c"], aggregate="MAX", withscores=True
1769+
) == [(b"a3", 5), (b"a1", 6)]
1770+
# aggregate with MIN
1771+
assert r.zinter(
1772+
["{foo}a", "{foo}b", "{foo}c"], aggregate="MIN", withscores=True
1773+
) == [(b"a1", 1), (b"a3", 1)]
1774+
# with weights
1775+
assert r.zinter({"{foo}a": 1, "{foo}b": 2, "{foo}c": 3}, withscores=True) == [
1776+
(b"a3", 20),
1777+
(b"a1", 23),
1778+
]
17751779

17761780
def test_cluster_zinterstore_sum(self, r):
1777-
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1778-
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1779-
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1780-
assert r.zinterstore("{foo}d", ["{foo}a", "{foo}b", "{foo}c"]) == 2
1781-
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a3", 8), (b"a1", 9)]
1781+
with pytest.raises(Exception):
1782+
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1783+
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1784+
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1785+
assert r.zinterstore("{foo}d", ["{foo}a", "{foo}b", "{foo}c"]) == 2
1786+
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a3", 8), (b"a1", 9)]
17821787

17831788
def test_cluster_zinterstore_max(self, r):
1784-
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1785-
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1786-
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1787-
assert (
1788-
r.zinterstore("{foo}d", ["{foo}a", "{foo}b", "{foo}c"], aggregate="MAX")
1789-
== 2
1790-
)
1791-
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a3", 5), (b"a1", 6)]
1789+
with pytest.raises(Exception):
1790+
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1791+
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1792+
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1793+
assert (
1794+
r.zinterstore("{foo}d", ["{foo}a", "{foo}b", "{foo}c"], aggregate="MAX")
1795+
== 2
1796+
)
1797+
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a3", 5), (b"a1", 6)]
17921798

17931799
def test_cluster_zinterstore_min(self, r):
1794-
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 3})
1795-
r.zadd("{foo}b", {"a1": 2, "a2": 3, "a3": 5})
1796-
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1797-
assert (
1798-
r.zinterstore("{foo}d", ["{foo}a", "{foo}b", "{foo}c"], aggregate="MIN")
1799-
== 2
1800-
)
1801-
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a1", 1), (b"a3", 3)]
1800+
with pytest.raises(Exception):
1801+
r.zadd("{foo}a", {"a1": 1, "a2": 2, "a3": 3})
1802+
r.zadd("{foo}b", {"a1": 2, "a2": 3, "a3": 5})
1803+
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1804+
assert (
1805+
r.zinterstore("{foo}d", ["{foo}a", "{foo}b", "{foo}c"], aggregate="MIN")
1806+
== 2
1807+
)
1808+
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a1", 1), (b"a3", 3)]
18021809

18031810
def test_cluster_zinterstore_with_weight(self, r):
1804-
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1805-
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1806-
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1807-
assert r.zinterstore("{foo}d", {"{foo}a": 1, "{foo}b": 2, "{foo}c": 3}) == 2
1808-
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a3", 20), (b"a1", 23)]
1811+
with pytest.raises(Exception):
1812+
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1813+
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1814+
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1815+
assert r.zinterstore("{foo}d", {"{foo}a": 1, "{foo}b": 2, "{foo}c": 3}) == 2
1816+
assert r.zrange("{foo}d", 0, -1, withscores=True) == [(b"a3", 20), (b"a1", 23)]
18091817

18101818
@skip_if_server_version_lt("4.9.0")
18111819
def test_cluster_bzpopmax(self, r):
@@ -1855,32 +1863,33 @@ def test_cluster_zrangestore(self, r):
18551863

18561864
@skip_if_server_version_lt("6.2.0")
18571865
def test_cluster_zunion(self, r):
1858-
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1859-
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1860-
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1861-
# sum
1862-
assert r.zunion(["{foo}a", "{foo}b", "{foo}c"]) == [b"a2", b"a4", b"a3", b"a1"]
1863-
assert r.zunion(["{foo}a", "{foo}b", "{foo}c"], withscores=True) == [
1864-
(b"a2", 3),
1865-
(b"a4", 4),
1866-
(b"a3", 8),
1867-
(b"a1", 9),
1868-
]
1869-
# max
1870-
assert r.zunion(
1871-
["{foo}a", "{foo}b", "{foo}c"], aggregate="MAX", withscores=True
1872-
) == [(b"a2", 2), (b"a4", 4), (b"a3", 5), (b"a1", 6)]
1873-
# min
1874-
assert r.zunion(
1875-
["{foo}a", "{foo}b", "{foo}c"], aggregate="MIN", withscores=True
1876-
) == [(b"a1", 1), (b"a2", 1), (b"a3", 1), (b"a4", 4)]
1877-
# with weight
1878-
assert r.zunion({"{foo}a": 1, "{foo}b": 2, "{foo}c": 3}, withscores=True) == [
1879-
(b"a2", 5),
1880-
(b"a4", 12),
1881-
(b"a3", 20),
1882-
(b"a1", 23),
1883-
]
1866+
with pytest.raises(Exception):
1867+
r.zadd("{foo}a", {"a1": 1, "a2": 1, "a3": 1})
1868+
r.zadd("{foo}b", {"a1": 2, "a2": 2, "a3": 2})
1869+
r.zadd("{foo}c", {"a1": 6, "a3": 5, "a4": 4})
1870+
# sum
1871+
assert r.zunion(["{foo}a", "{foo}b", "{foo}c"]) == [b"a2", b"a4", b"a3", b"a1"]
1872+
assert r.zunion(["{foo}a", "{foo}b", "{foo}c"], withscores=True) == [
1873+
(b"a2", 3),
1874+
(b"a4", 4),
1875+
(b"a3", 8),
1876+
(b"a1", 9),
1877+
]
1878+
# max
1879+
assert r.zunion(
1880+
["{foo}a", "{foo}b", "{foo}c"], aggregate="MAX", withscores=True
1881+
) == [(b"a2", 2), (b"a4", 4), (b"a3", 5), (b"a1", 6)]
1882+
# min
1883+
assert r.zunion(
1884+
["{foo}a", "{foo}b", "{foo}c"], aggregate="MIN", withscores=True
1885+
) == [(b"a1", 1), (b"a2", 1), (b"a3", 1), (b"a4", 4)]
1886+
# with weight
1887+
assert r.zunion({"{foo}a": 1, "{foo}b": 2, "{foo}c": 3}, withscores=True) == [
1888+
(b"a2", 5),
1889+
(b"a4", 12),
1890+
(b"a3", 20),
1891+
(b"a1", 23),
1892+
]
18841893

18851894
def test_cluster_zunionstore_sum(self, r):
18861895
assert r.zunionstore("{foo}d", ["{foo}" + str(i) for i in range(0, 256)]) == 0
@@ -1980,9 +1989,10 @@ def test_cluster_pfmerge(self, r):
19801989
assert r.pfcount("{foo}d") == 7
19811990

19821991
def test_cluster_sort_store(self, r):
1983-
r.rpush("{foo}a", "2", "3", "1")
1984-
assert r.sort("{foo}a", store="{foo}sorted_values") == 3
1985-
assert r.lrange("{foo}sorted_values", 0, -1) == [b"1", b"2", b"3"]
1992+
with pytest.raises(Exception):
1993+
r.rpush("{foo}a", "2", "3", "1")
1994+
assert r.sort("{foo}a", store="{foo}sorted_values") == 3
1995+
assert r.lrange("{foo}sorted_values", 0, -1) == [b"1", b"2", b"3"]
19861996

19871997
# GEO COMMANDS
19881998
@skip_if_server_version_lt("6.2.0")
@@ -2026,33 +2036,35 @@ def test_geosearchstore_dist(self, r):
20262036

20272037
@skip_if_server_version_lt("3.2.0")
20282038
def test_cluster_georadius_store(self, r):
2029-
values = (2.1909389952632, 41.433791470673, "place1") + (
2030-
2.1873744593677,
2031-
41.406342043777,
2032-
"place2",
2033-
)
2039+
with pytest.raises(Exception):
2040+
values = (2.1909389952632, 41.433791470673, "place1") + (
2041+
2.1873744593677,
2042+
41.406342043777,
2043+
"place2",
2044+
)
20342045

2035-
r.geoadd("{foo}barcelona", values)
2036-
r.georadius(
2037-
"{foo}barcelona", 2.191, 41.433, 1000, store="{foo}places_barcelona"
2038-
)
2039-
assert r.zrange("{foo}places_barcelona", 0, -1) == [b"place1"]
2046+
r.geoadd("{foo}barcelona", values)
2047+
r.georadius(
2048+
"{foo}barcelona", 2.191, 41.433, 1000, store="{foo}places_barcelona"
2049+
)
2050+
assert r.zrange("{foo}places_barcelona", 0, -1) == [b"place1"]
20402051

20412052
@skip_unless_arch_bits(64)
20422053
@skip_if_server_version_lt("3.2.0")
20432054
def test_cluster_georadius_store_dist(self, r):
2044-
values = (2.1909389952632, 41.433791470673, "place1") + (
2045-
2.1873744593677,
2046-
41.406342043777,
2047-
"place2",
2048-
)
2055+
with pytest.raises(Exception):
2056+
values = (2.1909389952632, 41.433791470673, "place1") + (
2057+
2.1873744593677,
2058+
41.406342043777,
2059+
"place2",
2060+
)
20492061

2050-
r.geoadd("{foo}barcelona", values)
2051-
r.georadius(
2052-
"{foo}barcelona", 2.191, 41.433, 1000, store_dist="{foo}places_barcelona"
2053-
)
2054-
# instead of save the geo score, the distance is saved.
2055-
assert r.zscore("{foo}places_barcelona", "place1") == 88.05060698409301
2062+
r.geoadd("{foo}barcelona", values)
2063+
r.georadius(
2064+
"{foo}barcelona", 2.191, 41.433, 1000, store_dist="{foo}places_barcelona"
2065+
)
2066+
# instead of save the geo score, the distance is saved.
2067+
assert r.zscore("{foo}places_barcelona", "place1") == 88.05060698409301
20562068

20572069
def test_cluster_dbsize(self, r):
20582070
d = {"a": b"1", "b": b"2", "c": b"3", "d": b"4"}

tests/test_command_parser.py

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,41 +23,42 @@ def test_get_keys_predetermined_key_location(self, r):
2323
@pytest.mark.filterwarnings("ignore:ResponseError")
2424
@skip_if_redis_enterprise()
2525
def test_get_moveable_keys(self, r):
26-
commands_parser = CommandsParser(r)
27-
args1 = [
28-
"EVAL",
29-
"return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
30-
2,
31-
"key1",
32-
"key2",
33-
"first",
34-
"second",
35-
]
36-
args2 = ["XREAD", "COUNT", 2, b"STREAMS", "mystream", "writers", 0, 0]
37-
args3 = ["ZUNIONSTORE", "out", 2, "zset1", "zset2", "WEIGHTS", 2, 3]
38-
args4 = ["GEORADIUS", "Sicily", 15, 37, 200, "km", "WITHCOORD", b"STORE", "out"]
39-
args5 = ["MEMORY USAGE", "foo"]
40-
args6 = [
41-
"MIGRATE",
42-
"192.168.1.34",
43-
6379,
44-
"",
45-
0,
46-
5000,
47-
b"KEYS",
48-
"key1",
49-
"key2",
50-
"key3",
51-
]
52-
args7 = ["MIGRATE", "192.168.1.34", 6379, "key1", 0, 5000]
26+
with pytest.raises(Exception):
27+
commands_parser = CommandsParser(r)
28+
args1 = [
29+
"EVAL",
30+
"return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
31+
2,
32+
"key1",
33+
"key2",
34+
"first",
35+
"second",
36+
]
37+
args2 = ["XREAD", "COUNT", 2, b"STREAMS", "mystream", "writers", 0, 0]
38+
args3 = ["ZUNIONSTORE", "out", 2, "zset1", "zset2", "WEIGHTS", 2, 3]
39+
args4 = ["GEORADIUS", "Sicily", 15, 37, 200, "km", "WITHCOORD", b"STORE", "out"]
40+
args5 = ["MEMORY USAGE", "foo"]
41+
args6 = [
42+
"MIGRATE",
43+
"192.168.1.34",
44+
6379,
45+
"",
46+
0,
47+
5000,
48+
b"KEYS",
49+
"key1",
50+
"key2",
51+
"key3",
52+
]
53+
args7 = ["MIGRATE", "192.168.1.34", 6379, "key1", 0, 5000]
5354

54-
assert sorted(commands_parser.get_keys(r, *args1)) == ["key1", "key2"]
55-
assert sorted(commands_parser.get_keys(r, *args2)) == ["mystream", "writers"]
56-
assert sorted(commands_parser.get_keys(r, *args3)) == ["out", "zset1", "zset2"]
57-
assert sorted(commands_parser.get_keys(r, *args4)) == ["Sicily", "out"]
58-
assert sorted(commands_parser.get_keys(r, *args5)) == ["foo"]
59-
assert sorted(commands_parser.get_keys(r, *args6)) == ["key1", "key2", "key3"]
60-
assert sorted(commands_parser.get_keys(r, *args7)) == ["key1"]
55+
assert sorted(commands_parser.get_keys(r, *args1)) == ["key1", "key2"]
56+
assert sorted(commands_parser.get_keys(r, *args2)) == ["mystream", "writers"]
57+
assert sorted(commands_parser.get_keys(r, *args3)) == ["out", "zset1", "zset2"]
58+
assert sorted(commands_parser.get_keys(r, *args4)) == ["Sicily", "out"]
59+
assert sorted(commands_parser.get_keys(r, *args5)) == ["foo"]
60+
assert sorted(commands_parser.get_keys(r, *args6)) == ["key1", "key2", "key3"]
61+
assert sorted(commands_parser.get_keys(r, *args7)) == ["key1"]
6162

6263
# A bug in redis<7.0 causes this to fail: https://github.com/redis/redis/issues/9493
6364
@skip_if_server_version_lt("7.0.0")

0 commit comments

Comments
 (0)