diff options
author | Heyang Zhou <zhy20000919@hotmail.com> | 2024-02-06 00:27:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-06 00:27:03 +0800 |
commit | e53ced0b8dc458e772ae08fdf152efa889a2250a (patch) | |
tree | 6c180ca2739b6b5194bf7f305cf3dd306b3c9a16 | |
parent | d13094c8215e7e0285e7bf4bcb23ac97a126c198 (diff) |
fix(unstable): validate kv list selector (#22265)
Check that in a `KvListSelector`, `start` and `end` are actually within
the keyspace bounds defined by `prefix`, if both are present.
-rw-r--r-- | cli/tests/unit/kv_test.ts | 51 | ||||
-rw-r--r-- | ext/kv/lib.rs | 41 |
2 files changed, 81 insertions, 11 deletions
diff --git a/cli/tests/unit/kv_test.ts b/cli/tests/unit/kv_test.ts index 19081fd12..c98611e2d 100644 --- a/cli/tests/unit/kv_test.ts +++ b/cli/tests/unit/kv_test.ts @@ -702,6 +702,24 @@ dbTest("list prefix with start empty", async (db) => { assertEquals(entries.length, 0); }); +dbTest("list prefix with start equal to prefix", async (db) => { + await setupData(db); + await assertRejects( + async () => await collect(db.list({ prefix: ["a"], start: ["a"] })), + TypeError, + "start key is not in the keyspace defined by prefix", + ); +}); + +dbTest("list prefix with start out of bounds", async (db) => { + await setupData(db); + await assertRejects( + async () => await collect(db.list({ prefix: ["b"], start: ["a"] })), + TypeError, + "start key is not in the keyspace defined by prefix", + ); +}); + dbTest("list prefix with end", async (db) => { const versionstamp = await setupData(db); const entries = await collect(db.list({ prefix: ["a"], end: ["a", "c"] })); @@ -717,6 +735,24 @@ dbTest("list prefix with end empty", async (db) => { assertEquals(entries.length, 0); }); +dbTest("list prefix with end equal to prefix", async (db) => { + await setupData(db); + await assertRejects( + async () => await collect(db.list({ prefix: ["a"], end: ["a"] })), + TypeError, + "end key is not in the keyspace defined by prefix", + ); +}); + +dbTest("list prefix with end out of bounds", async (db) => { + await setupData(db); + await assertRejects( + async () => await collect(db.list({ prefix: ["a"], end: ["b"] })), + TypeError, + "end key is not in the keyspace defined by prefix", + ); +}); + dbTest("list prefix with empty prefix", async (db) => { const res = await db.set(["a"], 1); const entries = await collect(db.list({ prefix: [] })); @@ -1020,6 +1056,21 @@ dbTest("list range with manual cursor reverse", async (db) => { ]); }); +dbTest("list range with start greater than end", async (db) => { + await setupData(db); + await assertRejects( + async () => await collect(db.list({ start: ["b"], end: ["a"] })), + TypeError, + "start key is greater than end key", + ); +}); + +dbTest("list range with start equal to end", async (db) => { + await setupData(db); + const entries = await collect(db.list({ start: ["a"], end: ["a"] })); + assertEquals(entries.length, 0); +}); + dbTest("list invalid selector", async (db) => { await setupData(db); diff --git a/ext/kv/lib.rs b/ext/kv/lib.rs index 1286a7323..3a868d463 100644 --- a/ext/kv/lib.rs +++ b/ext/kv/lib.rs @@ -605,17 +605,36 @@ impl RawSelector { start: None, end: None, }), - (Some(prefix), Some(start), None) => Ok(Self::Prefixed { - prefix, - start: Some(start), - end: None, - }), - (Some(prefix), None, Some(end)) => Ok(Self::Prefixed { - prefix, - start: None, - end: Some(end), - }), - (None, Some(start), Some(end)) => Ok(Self::Range { start, end }), + (Some(prefix), Some(start), None) => { + if !start.starts_with(&prefix) || start.len() == prefix.len() { + return Err(type_error( + "start key is not in the keyspace defined by prefix", + )); + } + Ok(Self::Prefixed { + prefix, + start: Some(start), + end: None, + }) + } + (Some(prefix), None, Some(end)) => { + if !end.starts_with(&prefix) || end.len() == prefix.len() { + return Err(type_error( + "end key is not in the keyspace defined by prefix", + )); + } + Ok(Self::Prefixed { + prefix, + start: None, + end: Some(end), + }) + } + (None, Some(start), Some(end)) => { + if start > end { + return Err(type_error("start key is greater than end key")); + } + Ok(Self::Range { start, end }) + } (None, Some(start), None) => { let end = start.iter().copied().chain(Some(0)).collect(); Ok(Self::Range { start, end }) |