From d06aa8b8f843aff9eb31462bb9b1211db0294d4b Mon Sep 17 00:00:00 2001 From: Michael Herzner Date: Thu, 11 Apr 2024 22:32:33 +0200 Subject: [PATCH 1/3] test(crypto): improve test coverage --- crypto/_fnv/mod_test.ts | 29 +++++++++++++++++++++++++ crypto/crypto_test.ts | 37 +++++++++++++++++++++++++++++++- crypto/timing_safe_equal_test.ts | 20 +++++++++++++++++ crypto/unstable_keystack_test.ts | 13 ++++++++++- 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 crypto/_fnv/mod_test.ts diff --git a/crypto/_fnv/mod_test.ts b/crypto/_fnv/mod_test.ts new file mode 100644 index 000000000000..12dee47c1c2d --- /dev/null +++ b/crypto/_fnv/mod_test.ts @@ -0,0 +1,29 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +import { assertEquals, assertThrows } from "../../assert/mod.ts"; +import { fnv } from "./mod.ts"; + +Deno.test("fnv()", function () { + const encoder = new TextEncoder(); + const input = encoder.encode("a"); + const fixtures = [ + ["FNV32", new ArrayBuffer(4)], + ["FNV32A", new ArrayBuffer(4)], + ["FNV64", new ArrayBuffer(8)], + ["FNV64A", new ArrayBuffer(8)], + ] as const; + for (const [name, expected] of fixtures) { + assertEquals(fnv(name, input), expected); + } +}); + +Deno.test("fnv() throws when no data is provided", function () { + assertThrows(() => fnv("FNV32"), TypeError, "no data provided for hashing"); +}); + +Deno.test("fnv() throws when an unsupported digest is provided", function () { + assertThrows( + () => fnv("FNV128", new Uint8Array()), + TypeError, + "unsupported fnv digest: FNV128", + ); +}); diff --git a/crypto/crypto_test.ts b/crypto/crypto_test.ts index d09fb73e4e62..388489b75623 100644 --- a/crypto/crypto_test.ts +++ b/crypto/crypto_test.ts @@ -1,5 +1,11 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals, assertInstanceOf, fail } from "../assert/mod.ts"; +import { + assert, + assertEquals, + assertInstanceOf, + assertRejects, + fail, +} from "../assert/mod.ts"; import { crypto as stdCrypto, DIGEST_ALGORITHM_NAMES, @@ -260,6 +266,35 @@ Deno.test("digest() keeps memory usage reasonable with many calls", async () => ); }); +Deno.test("digest() throws on invalid input", async () => { + const inputString = "taking the hobbits to isengard"; + const inputBytes = new TextEncoder().encode(inputString); + + await assertRejects( + () => stdCrypto.subtle.digest("BLAKE2B", {} as Iterable), + TypeError, + "data must be a BufferSource or [Async]Iterable", + ); + + await assertRejects( + () => + stdCrypto.subtle.digest( + "BLAKE2B", + (async function* () { + yield undefined; + })() as AsyncIterable, + ), + TypeError, + "data contained chunk of the wrong type", + ); + + await assertRejects( + () => stdCrypto.subtle.digest("BLAK" as DigestAlgorithmName, inputBytes), + DOMException, + "Unrecognized algorithm name", + ); +}); + // Simple periodic data, but the periods shouldn't line up with any block // or chunk sizes. const aboutAMeg = repeat( diff --git a/crypto/timing_safe_equal_test.ts b/crypto/timing_safe_equal_test.ts index 4530b51b64f5..b7e4dbb3351d 100644 --- a/crypto/timing_safe_equal_test.ts +++ b/crypto/timing_safe_equal_test.ts @@ -71,6 +71,26 @@ Deno.test({ }, }); +Deno.test({ + name: + "timingSafeEqual() handles Uint8Array with different byte lengths (a > b)", + fn() { + const a = new Uint8Array([212, 213]); + const b = new Uint8Array([212]); + assert(!timingSafeEqual(a, b)); + }, +}); + +Deno.test({ + name: + "timingSafeEqual() handles Uint8Array with different byte lengths (a < b)", + fn() { + const a = new Uint8Array([212]); + const b = new Uint8Array([212, 213]); + assert(!timingSafeEqual(a, b)); + }, +}); + Deno.test({ name: "timingSafeEqual() handles Uint8Array with different buffer sizes (a > b)", diff --git a/crypto/unstable_keystack_test.ts b/crypto/unstable_keystack_test.ts index b5dd87438195..18967ed69e5d 100644 --- a/crypto/unstable_keystack_test.ts +++ b/crypto/unstable_keystack_test.ts @@ -1,9 +1,20 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert, assertEquals } from "../assert/mod.ts"; +import { assert, assertEquals, assertThrows } from "../assert/mod.ts"; import { KeyStack } from "./unstable_keystack.ts"; +Deno.test({ + name: "KeyStack() throws on empty keys", + fn() { + assertThrows( + () => new KeyStack([]), + TypeError, + "keys must contain at least one value", + ); + }, +}); + Deno.test({ name: "keyStack.sign() handles single key", async fn() { From 3c0fbb89ccc65d8c50e3dcb4d39b4956ee41efdc Mon Sep 17 00:00:00 2001 From: Michael Herzner Date: Mon, 22 Apr 2024 09:17:06 +0200 Subject: [PATCH 2/3] chore: implement review remarks --- crypto/crypto_test.ts | 40 ++++++++++++++++++++++++++++---- crypto/unstable_keystack_test.ts | 14 ++++++++++- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/crypto/crypto_test.ts b/crypto/crypto_test.ts index 388489b75623..90b66118997e 100644 --- a/crypto/crypto_test.ts +++ b/crypto/crypto_test.ts @@ -4,6 +4,7 @@ import { assertEquals, assertInstanceOf, assertRejects, + assertThrows, fail, } from "../assert/mod.ts"; import { @@ -271,14 +272,14 @@ Deno.test("digest() throws on invalid input", async () => { const inputBytes = new TextEncoder().encode(inputString); await assertRejects( - () => stdCrypto.subtle.digest("BLAKE2B", {} as Iterable), + async () => await stdCrypto.subtle.digest("BLAKE2B", {} as Iterable), TypeError, "data must be a BufferSource or [Async]Iterable", ); await assertRejects( - () => - stdCrypto.subtle.digest( + async () => + await stdCrypto.subtle.digest( "BLAKE2B", (async function* () { yield undefined; @@ -289,12 +290,43 @@ Deno.test("digest() throws on invalid input", async () => { ); await assertRejects( - () => stdCrypto.subtle.digest("BLAK" as DigestAlgorithmName, inputBytes), + async () => + await stdCrypto.subtle.digest("BLAK" as DigestAlgorithmName, inputBytes), DOMException, "Unrecognized algorithm name", ); }); +Deno.test("digestSync() throws on invalid input", () => { + const inputString = "taking the hobbits to isengard"; + const inputBytes = new TextEncoder().encode(inputString); + + assertThrows( + () => stdCrypto.subtle.digestSync("BLAKE2B", {} as Iterable), + TypeError, + "data must be a BufferSource or Iterable", + ); + + assertThrows( + () => + stdCrypto.subtle.digestSync( + "BLAKE2B", + (function* () { + yield undefined; + })() as Iterable, + ), + TypeError, + "data contained chunk of the wrong type", + ); + + assertThrows( + () => + stdCrypto.subtle.digestSync("BLAK" as DigestAlgorithmName, inputBytes), + TypeError, + "unsupported algorithm", + ); +}); + // Simple periodic data, but the periods shouldn't line up with any block // or chunk sizes. const aboutAMeg = repeat( diff --git a/crypto/unstable_keystack_test.ts b/crypto/unstable_keystack_test.ts index 18967ed69e5d..e662dd032046 100644 --- a/crypto/unstable_keystack_test.ts +++ b/crypto/unstable_keystack_test.ts @@ -234,7 +234,7 @@ Deno.test({ }); Deno.test({ - name: "KeyStack() handles inspection", + name: "KeyStack() handles inspection in Deno", fn() { assertEquals( Deno.inspect(new KeyStack(["abcdef"])), @@ -242,3 +242,15 @@ Deno.test({ ); }, }); + +Deno.test({ + name: "KeyStack() handles inspection in Node", + async fn() { + const { inspect } = await import("node:util"); + + assertEquals( + inspect(new KeyStack(["abcdef"])), + `KeyStack { length: 1 }`, + ); + }, +}); From f8e0df648e0efa814f99803887c18766b660f4d2 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Mon, 22 Apr 2024 19:16:12 +0900 Subject: [PATCH 3/3] fix types incompatible with @types/node --- testing/time.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/time.ts b/testing/time.ts index 371807344d8e..9f97957b5929 100644 --- a/testing/time.ts +++ b/testing/time.ts @@ -84,7 +84,7 @@ function fakeSetTimeout( return setTimer(callback, delay, args, false); } -function fakeClearTimeout(id?: number) { +function fakeClearTimeout(id?: unknown) { if (!time) throw new TimeError("no fake time"); if (typeof id === "number" && dueNodes.has(id)) { dueNodes.delete(id); @@ -102,7 +102,7 @@ function fakeSetInterval( return setTimer(callback, delay, args, true); } -function fakeClearInterval(id?: number) { +function fakeClearInterval(id?: unknown) { if (!time) throw new TimeError("no fake time"); if (typeof id === "number" && dueNodes.has(id)) { dueNodes.delete(id);