From bf6288e909a60bad955a41da8478cacacdfdd872 Mon Sep 17 00:00:00 2001 From: Imamuzzaki Abu Salam Date: Wed, 18 Oct 2023 16:33:38 +0700 Subject: [PATCH] fix(hgetall): handle unsafe integer in deserialization (#664) * fix(hgetall): handle unsafe integer in deserialization * test: add test for randomUnsafeIntegerString() * fix(test-utils): Fix randomUnsafeIntegerString implementation * Format changes with fmt --------- Co-authored-by: ogzhanolguncu --- deno.lock | 34 ++++++++++++++++++++++++++++++++++ pkg/commands/hgetall.test.ts | 27 +++++++++++++++++++++++++-- pkg/commands/hgetall.ts | 9 ++++++++- pkg/test-utils.test.ts | 22 ++++++++++++++++++++++ pkg/test-utils.ts | 7 +++++++ 5 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 pkg/test-utils.test.ts diff --git a/deno.lock b/deno.lock index 11130084..e4023815 100644 --- a/deno.lock +++ b/deno.lock @@ -1,6 +1,7 @@ { "version": "3", "redirects": { + "https://deno.land/std/testing/asserts.ts": "https://deno.land/std@0.204.0/testing/asserts.ts", "https://deno.land/x/base64/base64url.ts": "https://deno.land/x/base64@v0.2.1/base64url.ts" }, "remote": { @@ -43,6 +44,39 @@ "https://deno.land/std@0.201.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", "https://deno.land/std@0.201.0/fmt/colors.ts": "87544aa2bc91087bb37f9c077970c85bfb041b48e4c37356129d7b450a415b6f", "https://deno.land/std@0.201.0/testing/asserts.ts": "b4e4b1359393aeff09e853e27901a982c685cb630df30426ed75496961931946", + "https://deno.land/std@0.204.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.204.0/assert/_diff.ts": "58e1461cc61d8eb1eacbf2a010932bf6a05b79344b02ca38095f9b805795dc48", + "https://deno.land/std@0.204.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.204.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.204.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.204.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.204.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.204.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.204.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", + "https://deno.land/std@0.204.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.204.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.204.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.204.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.204.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.204.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.204.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.204.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.204.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.204.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.204.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.204.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.204.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.204.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.204.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.204.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.204.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.204.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.204.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.204.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.204.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.204.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.204.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", + "https://deno.land/std@0.204.0/testing/asserts.ts": "b4e4b1359393aeff09e853e27901a982c685cb630df30426ed75496961931946", "https://deno.land/x/base64@v0.2.1/base.ts": "47dc8d68f07dc91524bdd6db36eccbe59cf4d935b5fc09f27357a3944bb3ff7b", "https://deno.land/x/base64@v0.2.1/base64url.ts": "18bbf879b31f1f32cca8adaa2b6885ae325c2cec6a66c5817b684ca12c46ad5e", "https://deno.land/x/sha1@v1.0.3/deps.ts": "2e1af51a48c8507017fdb057b950366601b177fb7e73d5de54c1b3e0e115d72e", diff --git a/pkg/commands/hgetall.test.ts b/pkg/commands/hgetall.test.ts index cf91609c..6af3f64c 100644 --- a/pkg/commands/hgetall.test.ts +++ b/pkg/commands/hgetall.test.ts @@ -1,8 +1,13 @@ -import { keygen, newHttpClient, randomID } from "../test-utils.ts"; import { assertEquals } from "https://deno.land/std@0.177.0/testing/asserts.ts"; import { afterAll } from "https://deno.land/std@0.177.0/testing/bdd.ts"; -import { HSetCommand } from "./hset.ts"; +import { + keygen, + newHttpClient, + randomID, + randomUnsafeIntegerString, +} from "../test-utils.ts"; import { HGetAllCommand } from "./hgetall.ts"; +import { HSetCommand } from "./hset.ts"; const client = newHttpClient(); @@ -29,3 +34,21 @@ Deno.test("when hash does not exist", async (t) => { assertEquals(res, null); }); }); +Deno.test("properly return bigint precisely", async () => { + const key = newKey(); + const field3 = randomID(); + const field2 = randomID(); + const field1 = randomID(); + const value1 = false; + const value2 = randomID(); + const value3 = randomUnsafeIntegerString(); + await new HSetCommand([ + key, + { [field1]: value1, [field2]: value2, [field3]: value3 }, + ]).exec(client); + + const res = await new HGetAllCommand([key]).exec(client); + + const obj = { [field1]: value1, [field2]: value2, [field3]: value3 }; + assertEquals(res, obj); +}); diff --git a/pkg/commands/hgetall.ts b/pkg/commands/hgetall.ts index e889a8e9..9248a051 100644 --- a/pkg/commands/hgetall.ts +++ b/pkg/commands/hgetall.ts @@ -11,7 +11,14 @@ function deserialize>( const key = result.shift()!; const value = result.shift()!; try { - obj[key] = JSON.parse(value); + // handle unsafe integer + const valueIsNumberAndNotSafeInteger = !isNaN(Number(value)) && + !Number.isSafeInteger(value); + if (valueIsNumberAndNotSafeInteger) { + obj[key] = value; + } else { + obj[key] = JSON.parse(value); + } } catch { obj[key] = value; } diff --git a/pkg/test-utils.test.ts b/pkg/test-utils.test.ts new file mode 100644 index 00000000..feb58bbc --- /dev/null +++ b/pkg/test-utils.test.ts @@ -0,0 +1,22 @@ +import { + assertEquals, + assertFalse, +} from "https://deno.land/std@0.177.0/testing/asserts.ts"; +import { randomUnsafeIntegerString } from "./test-utils.ts"; + +Deno.test("randomUnsafeIntegerString() should return a string", () => { + const result = randomUnsafeIntegerString(); + assertEquals(typeof result, "string"); +}); +Deno.test("randomUnsafeIntegerString() should return different values", () => { + const result1 = randomUnsafeIntegerString(); + const result2 = randomUnsafeIntegerString(); + assertEquals(result1 !== result2, true); +}); +Deno.test( + "randomUnsafeIntegerString() should return a string with unsafe integer", + () => { + const result = randomUnsafeIntegerString(); + assertFalse(Number.isSafeInteger(Number(result))); + }, +); diff --git a/pkg/test-utils.ts b/pkg/test-utils.ts index b6bc5b05..75fc9df2 100644 --- a/pkg/test-utils.ts +++ b/pkg/test-utils.ts @@ -14,6 +14,13 @@ export function randomID(): string { } return btoa(s.join("")); } +export const randomUnsafeIntegerString = (): string => { + const buffer = new Uint8Array(8); + crypto.getRandomValues(buffer); + const dataView = new DataView(buffer.buffer); + const unsafeInteger = dataView.getBigInt64(0, true); // true for little-endian + return unsafeInteger.toString(); +}; export const newHttpClient = () => { const url = Deno.env.get("UPSTASH_REDIS_REST_URL"); if (!url) {