summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeyang Zhou <zhy20000919@hotmail.com>2024-02-06 00:27:03 +0800
committerGitHub <noreply@github.com>2024-02-06 00:27:03 +0800
commite53ced0b8dc458e772ae08fdf152efa889a2250a (patch)
tree6c180ca2739b6b5194bf7f305cf3dd306b3c9a16
parentd13094c8215e7e0285e7bf4bcb23ac97a126c198 (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.ts51
-rw-r--r--ext/kv/lib.rs41
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 })