From 0bbfd6fb3dcae70c3cd727ef1cb45296f83c9680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 5 Jul 2024 03:38:12 +0100 Subject: [PATCH 1/8] feat: Deprecate --lock-write flag (#24436) This commit deprecates `--lock-write` flag by removing it from the help output and printing a warning message when it's used. Users should use `--frozen=false` instead which was added in https://github.com/denoland/deno/pull/24355. Towards https://github.com/denoland/deno/issues/24167. --- cli/args/flags.rs | 4 ++++ cli/args/lockfile.rs | 4 ++++ tests/testdata/lockfile/no_dts/main.cache.out | 1 + 3 files changed, 9 insertions(+) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 56fb4f09d2..4d0465caab 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -546,6 +546,7 @@ pub struct Flags { pub inspect_wait: Option, pub inspect: Option, pub location: Option, + // TODO(bartlomieju): deprecated, to be removed in Deno 2. pub lock_write: bool, pub lock: Option, pub log_level: Option, @@ -3623,12 +3624,14 @@ If value is not provided, defaults to \"deno.lock\" in the current working direc .value_hint(ValueHint::FilePath) } +// TODO(bartlomieju): deprecated, to be removed in Deno 2. fn lock_write_arg() -> Arg { Arg::new("lock-write") .action(ArgAction::SetTrue) .long("lock-write") .help("Force overwriting the lock file.") .conflicts_with("no-lock") + .hide(true) } fn no_lock_arg() -> Arg { @@ -4747,6 +4750,7 @@ fn check_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { fn lock_args_parse(flags: &mut Flags, matches: &mut ArgMatches) { lock_arg_parse(flags, matches); no_lock_arg_parse(flags, matches); + // TODO(bartlomieju): deprecated, to be removed in Deno 2. if matches.get_flag("lock-write") { flags.lock_write = true; } diff --git a/cli/args/lockfile.rs b/cli/args/lockfile.rs index d18c871aad..3421addae8 100644 --- a/cli/args/lockfile.rs +++ b/cli/args/lockfile.rs @@ -130,6 +130,10 @@ impl CliLockfile { }; let lockfile = if flags.lock_write { + log::warn!( + "{} \"--lock-write\" flag is deprecated and will be removed in Deno 2.", + crate::colors::yellow("Warning") + ); CliLockfile::new( Lockfile::new_empty(filename, true), flags.frozen_lockfile, diff --git a/tests/testdata/lockfile/no_dts/main.cache.out b/tests/testdata/lockfile/no_dts/main.cache.out index ee8ad33ab7..84e728d662 100644 --- a/tests/testdata/lockfile/no_dts/main.cache.out +++ b/tests/testdata/lockfile/no_dts/main.cache.out @@ -1,2 +1,3 @@ +Warning "--lock-write" flag is deprecated and will be removed in Deno 2. Download http://localhost:4545/lockfile/no_dts/mod.js Download http://localhost:4545/lockfile/no_dts/mod.d.ts From b290fd01f3f5d32f9d010fc719ced0240759c049 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 4 Jul 2024 20:19:42 -0700 Subject: [PATCH 2/8] fix(ext/node): http chunked writes hangs (#24428) Fixes https://github.com/denoland/deno/issues/24239 --- ext/node/polyfills/http.ts | 20 +++++++--------- tests/unit_node/http_test.ts | 44 ++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index 40155d998c..dde7c4e410 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -1349,7 +1349,6 @@ export class ServerResponse extends NodeWritable { // used by `npm:on-finished` finished = false; headersSent = false; - #firstChunk: Chunk | null = null; #resolve: (value: Response | PromiseLike) => void; // deno-lint-ignore no-explicit-any #socketOverride: any | null = null; @@ -1386,28 +1385,25 @@ export class ServerResponse extends NodeWritable { autoDestroy: true, defaultEncoding: "utf-8", emitClose: true, + // FIXME: writes don't work when a socket is assigned and then + // detached. write: (chunk, encoding, cb) => { + // Writes chunks are directly written to the socket if + // one is assigned via assignSocket() if (this.#socketOverride && this.#socketOverride.writable) { this.#socketOverride.write(chunk, encoding); return cb(); } if (!this.headersSent) { - if (this.#firstChunk === null) { - this.#firstChunk = chunk; - return cb(); - } else { - ServerResponse.#enqueue(controller, this.#firstChunk); - this.#firstChunk = null; - this.respond(false); - } + ServerResponse.#enqueue(controller, chunk); + this.respond(false); + return cb(); } ServerResponse.#enqueue(controller, chunk); return cb(); }, final: (cb) => { - if (this.#firstChunk) { - this.respond(true, this.#firstChunk); - } else if (!this.headersSent) { + if (!this.headersSent) { this.respond(true); } controller.close(); diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index 0935aeac03..af88d5f9c8 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -1045,11 +1045,15 @@ Deno.test("[node/http] ServerResponse assignSocket and detachSocket", () => { writtenData = undefined; writtenEncoding = undefined; + // TODO(@littledivy): This test never really worked + // because there was no data being sent and it passed. + // // @ts-ignore it's a socket mock - res.detachSocket(socket); - res.write("Hello World!", "utf8"); - assertEquals(writtenData, undefined); - assertEquals(writtenEncoding, undefined); + // res.detachSocket(socket); + // res.write("Hello World!", "utf8"); + // + // assertEquals(writtenData, undefined); + // assertEquals(writtenEncoding, undefined); }); Deno.test("[node/http] ServerResponse getHeaders", () => { @@ -1252,6 +1256,38 @@ Deno.test("[node/http] http.request() post streaming body works", async () => { assertEquals(server.listening, false); }); +// https://github.com/denoland/deno/issues/24239 +Deno.test("[node/http] ServerResponse write transfer-encoding chunked", async () => { + const { promise, resolve } = Promise.withResolvers(); + const server = http.createServer((_req, res) => { + res.setHeader("Content-Type", "text/event-stream"); + res.setHeader("Cache-Control", "no-cache"); + res.setHeader("Connection", "keep-alive"); + res.setHeader("Transfer-Encoding", "chunked"); + res.setHeader("Access-Control-Allow-Origin", "*"); + + res.writeHead(200, { + "Other-Header": "value", + }); + res.write(""); + }); + + server.listen(async () => { + const { port } = server.address() as { port: number }; + const res = await fetch(`http://localhost:${port}`); + assertEquals(res.status, 200); + assertEquals(res.headers.get("content-type"), "text/event-stream"); + assertEquals(res.headers.get("Other-Header"), "value"); + await res.body!.cancel(); + + server.close(() => { + resolve(); + }); + }); + + await promise; +}); + Deno.test("[node/http] Server.address() can be null", () => { const server = http.createServer((_req, res) => res.end("it works")); assertEquals(server.address(), null); From 08e5606c3400d3a993c0ce6748901c56fc3db35b Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Fri, 5 Jul 2024 10:10:22 +0200 Subject: [PATCH 3/8] fix(ext/node): rewrite digest handling (#24392) Previously we had many different code paths all handling digests in different places, all with wildly different digest support. This commit rewrites this to use a single digest handling mechanism for all digest operations. It adds various aliases for digest algorithms, like node does. For example `sha1WithRSAEncryption` is an alias for `sha1`. It also adds support for `md5-sha1` digests in various places. --- ext/crypto/Cargo.toml | 2 +- ext/node/Cargo.toml | 6 +- ext/node/ops/crypto/digest.rs | 346 ++++++++--------- ext/node/ops/crypto/md5_sha1.rs | 99 +++++ ext/node/ops/crypto/mod.rs | 163 ++++---- ext/node/polyfills/internal/crypto/hkdf.ts | 7 +- ext/node/polyfills/internal/crypto/pbkdf2.ts | 76 +++- ext/node/polyfills/internal/crypto/sig.ts | 9 - ext/node/polyfills/internal/crypto/types.ts | 2 +- tests/integration/node_unit_tests.rs | 7 +- tests/node_compat/config.jsonc | 2 + tests/node_compat/runner/TODO.md | 1 - .../test/parallel/test-crypto-pbkdf2.js | 251 ++++++++++++ tests/unit_node/crypto/crypto_hkdf_test.ts | 36 ++ .../crypto_pbkdf2_test.ts} | 32 ++ .../crypto_scrypt_test.ts} | 0 tests/unit_node/crypto/crypto_sign_test.ts | 41 ++ tests/unit_node/crypto/generate_fixture.mjs | 69 ++++ .../testdata/crypto_digest_fixtures.json | 366 ++++++++++++++++++ 19 files changed, 1220 insertions(+), 295 deletions(-) create mode 100644 ext/node/ops/crypto/md5_sha1.rs create mode 100644 tests/node_compat/test/parallel/test-crypto-pbkdf2.js create mode 100644 tests/unit_node/crypto/crypto_hkdf_test.ts rename tests/unit_node/{internal/pbkdf2_test.ts => crypto/crypto_pbkdf2_test.ts} (95%) rename tests/unit_node/{internal/scrypt_test.ts => crypto/crypto_scrypt_test.ts} (100%) create mode 100644 tests/unit_node/crypto/generate_fixture.mjs create mode 100644 tests/unit_node/testdata/crypto_digest_fixtures.json diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index e1fc162750..418394584e 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -35,7 +35,7 @@ ring = { workspace = true, features = ["std"] } rsa.workspace = true serde.workspace = true serde_bytes.workspace = true -sha1 = { version = "0.10.6", features = ["oid"] } +sha1.workspace = true sha2.workspace = true signature.workspace = true spki.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index b1d40756bd..b9228dfdd3 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -49,7 +49,7 @@ k256 = "0.13.1" lazy-regex.workspace = true libc.workspace = true libz-sys.workspace = true -md-5 = "0.10.5" +md-5 = { version = "0.10.5", features = ["oid"] } md4 = "0.10.2" num-bigint.workspace = true num-bigint-dig = "0.8.2" @@ -66,14 +66,14 @@ rand.workspace = true regex.workspace = true reqwest.workspace = true ring.workspace = true -ripemd = "0.1.3" +ripemd = { version = "0.1.3", features = ["oid"] } rsa.workspace = true scrypt = "0.11.0" sec1 = "0.7" serde = "1.0.149" sha1.workspace = true sha2.workspace = true -sha3 = "0.10.8" +sha3 = { version = "0.10.8", features = ["oid"] } signature.workspace = true simd-json = "0.13.4" sm3 = "0.4.2" diff --git a/ext/node/ops/crypto/digest.rs b/ext/node/ops/crypto/digest.rs index e3a91e338f..0a21a395a3 100644 --- a/ext/node/ops/crypto/digest.rs +++ b/ext/node/ops/crypto/digest.rs @@ -56,30 +56,110 @@ impl Hasher { } } +macro_rules! match_fixed_digest { + ($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => { + match $algorithm_name { + "blake2b512" => { + type $type = ::blake2::Blake2b512; + $body + } + "blake2s256" => { + type $type = ::blake2::Blake2s256; + $body + } + _ => match_fixed_digest_with_eager_block_buffer!($algorithm_name, fn <$type>() $body, _ => $other) + } + }; +} +pub(crate) use match_fixed_digest; + +macro_rules! match_fixed_digest_with_eager_block_buffer { + ($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => { + match $algorithm_name { + "rsa-sm3" | "sm3" | "sm3withrsaencryption" => { + type $type = ::sm3::Sm3; + $body + } + "md5-sha1" => { + type $type = crate::ops::crypto::md5_sha1::Md5Sha1; + $body + } + _ => match_fixed_digest_with_oid!($algorithm_name, fn <$type>() $body, _ => $other) + } + }; +} +pub(crate) use match_fixed_digest_with_eager_block_buffer; + +macro_rules! match_fixed_digest_with_oid { + ($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => { + match $algorithm_name { + "rsa-md5" | "md5" | "md5withrsaencryption" | "ssl3-md5" => { + type $type = ::md5::Md5; + $body + } + "rsa-ripemd160" | "ripemd" | "ripemd160" | "ripemd160withrsa" + | "rmd160" => { + type $type = ::ripemd::Ripemd160; + $body + } + "rsa-sha1" + | "rsa-sha1-2" + | "sha1" + | "sha1-2" + | "sha1withrsaencryption" + | "ssl3-sha1" => { + type $type = ::sha1::Sha1; + $body + } + "rsa-sha224" | "sha224" | "sha224withrsaencryption" => { + type $type = ::sha2::Sha224; + $body + } + "rsa-sha256" | "sha256" | "sha256withrsaencryption" => { + type $type = ::sha2::Sha256; + $body + } + "rsa-sha384" | "sha384" | "sha384withrsaencryption" => { + type $type = ::sha2::Sha384; + $body + } + "rsa-sha512" | "sha512" | "sha512withrsaencryption" => { + type $type = ::sha2::Sha512; + $body + } + "rsa-sha512/224" | "sha512-224" | "sha512-224withrsaencryption" => { + type $type = ::sha2::Sha512_224; + $body + } + "rsa-sha512/256" | "sha512-256" | "sha512-256withrsaencryption" => { + type $type = ::sha2::Sha512_256; + $body + } + "rsa-sha3-224" | "id-rsassa-pkcs1-v1_5-with-sha3-224" | "sha3-224" => { + type $type = ::sha3::Sha3_224; + $body + } + "rsa-sha3-256" | "id-rsassa-pkcs1-v1_5-with-sha3-256" | "sha3-256" => { + type $type = ::sha3::Sha3_256; + $body + } + "rsa-sha3-384" | "id-rsassa-pkcs1-v1_5-with-sha3-384" | "sha3-384" => { + type $type = ::sha3::Sha3_384; + $body + } + "rsa-sha3-512" | "id-rsassa-pkcs1-v1_5-with-sha3-512" | "sha3-512" => { + type $type = ::sha3::Sha3_512; + $body + } + _ => $other, + } + }; +} + +pub(crate) use match_fixed_digest_with_oid; + pub enum Hash { - Blake2b512(Box), - Blake2s256(Box), - - Md4(Box), - Md5(Box), - - Ripemd160(Box), - - Sha1(Box), - - Sha224(Box), - Sha256(Box), - Sha384(Box), - Sha512(Box), - Sha512_224(Box), - Sha512_256(Box), - - Sha3_224(Box), - Sha3_256(Box), - Sha3_384(Box), - Sha3_512(Box), - - Sm3(Box), + FixedSize(Box), Shake128(Box, /* output_length: */ Option), Shake256(Box, /* output_length: */ Option), @@ -98,105 +178,32 @@ impl Hash { _ => {} } - let algorithm = match algorithm_name { - "blake2b512" => Blake2b512(Default::default()), - "blake2s256" => Blake2s256(Default::default()), - - "md4" => Md4(Default::default()), - "md5" => Md5(Default::default()), - - "ripemd160" => Ripemd160(Default::default()), - - "sha1" => Sha1(Default::default()), - "sha224" => Sha224(Default::default()), - "sha256" => Sha256(Default::default()), - "sha384" => Sha384(Default::default()), - "sha512" => Sha512(Default::default()), - "sha512-224" => Sha512_224(Default::default()), - "sha512-256" => Sha512_256(Default::default()), - - "sha3-224" => Sha3_224(Default::default()), - "sha3-256" => Sha3_256(Default::default()), - "sha3-384" => Sha3_384(Default::default()), - "sha3-512" => Sha3_512(Default::default()), - - "sm3" => Sm3(Default::default()), - + let algorithm = match_fixed_digest!( + algorithm_name, + fn () { + let digest: D = Digest::new(); + if let Some(length) = output_length { + if length != digest.output_size() { + return Err(generic_error( + "Output length mismatch for non-extendable algorithm", + )); + } + } + FixedSize(Box::new(digest)) + }, _ => { return Err(generic_error(format!( "Digest method not supported: {algorithm_name}" ))) } - }; - if let Some(length) = output_length { - if length != algorithm.output_length() { - return Err(generic_error( - "Output length mismatch for non-extendable algorithm", - )); - } - } + ); + Ok(algorithm) } - pub fn output_length(&self) -> usize { - match self { - Blake2b512(context) => context.output_size(), - Blake2s256(context) => context.output_size(), - - Md4(context) => context.output_size(), - Md5(context) => context.output_size(), - - Ripemd160(context) => context.output_size(), - - Sha1(context) => context.output_size(), - Sha224(context) => context.output_size(), - Sha256(context) => context.output_size(), - Sha384(context) => context.output_size(), - Sha512(context) => context.output_size(), - Sha512_224(context) => context.output_size(), - Sha512_256(context) => context.output_size(), - - Sha3_224(context) => context.output_size(), - Sha3_256(context) => context.output_size(), - Sha3_384(context) => context.output_size(), - Sha3_512(context) => context.output_size(), - - Sm3(context) => context.output_size(), - - Shake128(_, _) => unreachable!( - "output_length() should not be called on extendable algorithms" - ), - Shake256(_, _) => unreachable!( - "output_length() should not be called on extendable algorithms" - ), - } - } - pub fn update(&mut self, data: &[u8]) { match self { - Blake2b512(context) => Digest::update(&mut **context, data), - Blake2s256(context) => Digest::update(&mut **context, data), - - Md4(context) => Digest::update(&mut **context, data), - Md5(context) => Digest::update(&mut **context, data), - - Ripemd160(context) => Digest::update(&mut **context, data), - - Sha1(context) => Digest::update(&mut **context, data), - Sha224(context) => Digest::update(&mut **context, data), - Sha256(context) => Digest::update(&mut **context, data), - Sha384(context) => Digest::update(&mut **context, data), - Sha512(context) => Digest::update(&mut **context, data), - Sha512_224(context) => Digest::update(&mut **context, data), - Sha512_256(context) => Digest::update(&mut **context, data), - - Sha3_224(context) => Digest::update(&mut **context, data), - Sha3_256(context) => Digest::update(&mut **context, data), - Sha3_384(context) => Digest::update(&mut **context, data), - Sha3_512(context) => Digest::update(&mut **context, data), - - Sm3(context) => Digest::update(&mut **context, data), - + FixedSize(context) => DynDigest::update(&mut **context, data), Shake128(context, _) => Update::update(&mut **context, data), Shake256(context, _) => Update::update(&mut **context, data), }; @@ -204,28 +211,7 @@ impl Hash { pub fn digest_and_drop(self) -> Box<[u8]> { match self { - Blake2b512(context) => context.finalize(), - Blake2s256(context) => context.finalize(), - - Md4(context) => context.finalize(), - Md5(context) => context.finalize(), - - Ripemd160(context) => context.finalize(), - - Sha1(context) => context.finalize(), - Sha224(context) => context.finalize(), - Sha256(context) => context.finalize(), - Sha384(context) => context.finalize(), - Sha512(context) => context.finalize(), - Sha512_224(context) => context.finalize(), - Sha512_256(context) => context.finalize(), - - Sha3_224(context) => context.finalize(), - Sha3_256(context) => context.finalize(), - Sha3_384(context) => context.finalize(), - Sha3_512(context) => context.finalize(), - - Sm3(context) => context.finalize(), + FixedSize(context) => context.finalize(), // The default output lengths align with Node.js Shake128(context, output_length) => { @@ -242,69 +228,77 @@ impl Hash { output_length: Option, ) -> Result { let hash = match self { - Shake128(context, _) => { - return Ok(Shake128(context.clone(), output_length)) - } - Shake256(context, _) => { - return Ok(Shake256(context.clone(), output_length)) + FixedSize(context) => { + if let Some(length) = output_length { + if length != context.output_size() { + return Err(generic_error( + "Output length mismatch for non-extendable algorithm", + )); + } + } + FixedSize(context.box_clone()) } - Blake2b512(context) => Blake2b512(context.clone()), - Blake2s256(context) => Blake2s256(context.clone()), - - Md4(context) => Md4(context.clone()), - Md5(context) => Md5(context.clone()), - - Ripemd160(context) => Ripemd160(context.clone()), - - Sha1(context) => Sha1(context.clone()), - Sha224(context) => Sha224(context.clone()), - Sha256(context) => Sha256(context.clone()), - Sha384(context) => Sha384(context.clone()), - Sha512(context) => Sha512(context.clone()), - Sha512_224(context) => Sha512_224(context.clone()), - Sha512_256(context) => Sha512_256(context.clone()), - - Sha3_224(context) => Sha3_224(context.clone()), - Sha3_256(context) => Sha3_256(context.clone()), - Sha3_384(context) => Sha3_384(context.clone()), - Sha3_512(context) => Sha3_512(context.clone()), - - Sm3(context) => Sm3(context.clone()), + Shake128(context, _) => Shake128(context.clone(), output_length), + Shake256(context, _) => Shake256(context.clone(), output_length), }; - - if let Some(length) = output_length { - if length != hash.output_length() { - return Err(generic_error( - "Output length mismatch for non-extendable algorithm", - )); - } - } - Ok(hash) } pub fn get_hashes() -> Vec<&'static str> { vec![ - "blake2s256", + "RSA-MD5", + "RSA-RIPEMD160", + "RSA-SHA1", + "RSA-SHA1-2", + "RSA-SHA224", + "RSA-SHA256", + "RSA-SHA3-224", + "RSA-SHA3-256", + "RSA-SHA3-384", + "RSA-SHA3-512", + "RSA-SHA384", + "RSA-SHA512", + "RSA-SHA512/224", + "RSA-SHA512/256", + "RSA-SM3", "blake2b512", - "md4", + "blake2s256", + "id-rsassa-pkcs1-v1_5-with-sha3-224", + "id-rsassa-pkcs1-v1_5-with-sha3-256", + "id-rsassa-pkcs1-v1_5-with-sha3-384", + "id-rsassa-pkcs1-v1_5-with-sha3-512", "md5", + "md5-sha1", + "md5WithRSAEncryption", + "ripemd", "ripemd160", + "ripemd160WithRSA", + "rmd160", "sha1", + "sha1WithRSAEncryption", "sha224", + "sha224WithRSAEncryption", "sha256", - "sha384", - "sha512", - "sha512-224", - "sha512-256", + "sha256WithRSAEncryption", "sha3-224", "sha3-256", "sha3-384", "sha3-512", + "sha384", + "sha384WithRSAEncryption", + "sha512", + "sha512-224", + "sha512-224WithRSAEncryption", + "sha512-256", + "sha512-256WithRSAEncryption", + "sha512WithRSAEncryption", "shake128", "shake256", "sm3", + "sm3WithRSAEncryption", + "ssl3-md5", + "ssl3-sha1", ] } } diff --git a/ext/node/ops/crypto/md5_sha1.rs b/ext/node/ops/crypto/md5_sha1.rs new file mode 100644 index 0000000000..9164b0a1cb --- /dev/null +++ b/ext/node/ops/crypto/md5_sha1.rs @@ -0,0 +1,99 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use core::fmt; +use digest::core_api::AlgorithmName; +use digest::core_api::BlockSizeUser; +use digest::core_api::Buffer; +use digest::core_api::BufferKindUser; +use digest::core_api::CoreWrapper; +use digest::core_api::FixedOutputCore; +use digest::core_api::OutputSizeUser; +use digest::core_api::UpdateCore; +use digest::HashMarker; +use digest::Output; +use digest::Reset; + +pub type Md5Sha1 = CoreWrapper; + +pub struct Md5Sha1Core { + md5: md5::Md5Core, + sha1: sha1::Sha1Core, +} + +impl HashMarker for Md5Sha1Core {} + +impl BlockSizeUser for Md5Sha1Core { + type BlockSize = sec1::consts::U64; +} + +impl BufferKindUser for Md5Sha1Core { + type BufferKind = digest::block_buffer::Eager; +} + +impl OutputSizeUser for Md5Sha1Core { + type OutputSize = sec1::consts::U36; +} + +impl UpdateCore for Md5Sha1Core { + #[inline] + fn update_blocks(&mut self, blocks: &[digest::core_api::Block]) { + self.md5.update_blocks(blocks); + self.sha1.update_blocks(blocks); + } +} + +impl FixedOutputCore for Md5Sha1Core { + #[inline] + fn finalize_fixed_core( + &mut self, + buffer: &mut Buffer, + out: &mut Output, + ) { + let mut md5_output = Output::::default(); + self + .md5 + .finalize_fixed_core(&mut buffer.clone(), &mut md5_output); + let mut sha1_output = Output::::default(); + self.sha1.finalize_fixed_core(buffer, &mut sha1_output); + out[..16].copy_from_slice(&md5_output); + out[16..].copy_from_slice(&sha1_output); + } +} + +impl Default for Md5Sha1Core { + #[inline] + fn default() -> Self { + Self { + md5: Default::default(), + sha1: Default::default(), + } + } +} + +impl Clone for Md5Sha1Core { + #[inline] + fn clone(&self) -> Self { + Self { + md5: self.md5.clone(), + sha1: self.sha1.clone(), + } + } +} + +impl Reset for Md5Sha1Core { + #[inline] + fn reset(&mut self) { + *self = Default::default(); + } +} + +impl AlgorithmName for Md5Sha1Core { + fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Md5Sha1") + } +} + +impl fmt::Debug for Md5Sha1Core { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Md5Sha1Core { ... }") + } +} diff --git a/ext/node/ops/crypto/mod.rs b/ext/node/ops/crypto/mod.rs index 8ded3420a1..1b545e0243 100644 --- a/ext/node/ops/crypto/mod.rs +++ b/ext/node/ops/crypto/mod.rs @@ -46,9 +46,13 @@ use spki::EncodePublicKey; mod cipher; mod dh; mod digest; +mod md5_sha1; mod primes; pub mod x509; +use self::digest::match_fixed_digest_with_eager_block_buffer; +use self::digest::match_fixed_digest_with_oid; + #[op2(fast)] pub fn op_node_check_prime( #[bigint] num: i64, @@ -382,33 +386,30 @@ pub fn op_node_sign( RSA_ENCRYPTION_OID => { use rsa::pkcs1v15::SigningKey; let key = RsaPrivateKey::from_pkcs1_der(pkey)?; - Ok( - match digest_type { - "sha224" => { - let signing_key = SigningKey::::new(key); - signing_key.sign_prehash(digest)?.to_vec() - } - "sha256" => { - let signing_key = SigningKey::::new(key); - signing_key.sign_prehash(digest)?.to_vec() - } - "sha384" => { - let signing_key = SigningKey::::new(key); - signing_key.sign_prehash(digest)?.to_vec() - } - "sha512" => { - let signing_key = SigningKey::::new(key); - signing_key.sign_prehash(digest)?.to_vec() - } - _ => { - return Err(type_error(format!( - "Unknown digest algorithm: {}", - digest_type - ))) - } + + // md5-sha1 is special, because it's not wrapped in an ASN.1 DigestInfo + // (so has no prefix). + // See https://github.com/openssl/openssl/blob/af82623d32962b3eff5b0f0b0dedec5eb730b231/crypto/rsa/rsa_sign.c#L285 + if digest_type == "md5-sha1" { + let signing_key = SigningKey::::new_unprefixed(key); + let signature = signing_key.sign_prehash(digest)?.to_vec(); + return Ok(signature.into()); + } + + let signature = match_fixed_digest_with_oid!( + digest_type, + fn () { + let signing_key = SigningKey::::new(key); + signing_key.sign_prehash(digest)?.to_vec() + }, + _ => { + return Err(type_error(format!( + "digest not allowed for RSA signature: {}", + digest_type + ))) } - .into(), - ) + ); + Ok(signature.into()) } // signature structure encoding is DER by default for DSA and ECDSA. // @@ -456,29 +457,35 @@ pub fn op_node_verify( ))) } }; - Ok(match digest_type { - "sha224" => VerifyingKey::::new(key) + + // md5-sha1 is special, because it's not wrapped in an ASN.1 DigestInfo + // (so has no prefix). + // See https://github.com/openssl/openssl/blob/af82623d32962b3eff5b0f0b0dedec5eb730b231/crypto/rsa/rsa_sign.c#L285 + if digest_type == "md5-sha1" { + let verifying_key = + VerifyingKey::::new_unprefixed(key); + let verified = verifying_key .verify_prehash(digest, &signature.try_into()?) - .is_ok(), - "sha256" => VerifyingKey::::new(key) - .verify_prehash(digest, &signature.try_into()?) - .is_ok(), - "sha384" => VerifyingKey::::new(key) - .verify_prehash(digest, &signature.try_into()?) - .is_ok(), - "sha512" => VerifyingKey::::new(key) - .verify_prehash(digest, &signature.try_into()?) - .is_ok(), + .is_ok(); + return Ok(verified); + } + + Ok(match_fixed_digest_with_oid!( + digest_type, + fn () { + let verifying_key = VerifyingKey::::new(key); + verifying_key.verify_prehash(digest, &signature.try_into()?).is_ok() + }, _ => { return Err(type_error(format!( - "Unknown digest algorithm: {}", + "digest not allowed for RSA signature: {}", digest_type ))) } - }) + )) } _ => Err(type_error(format!( - "Verifying with {} keys is not supported yet", + "Verifying with {} keys is not supported", key_type ))), } @@ -488,28 +495,22 @@ fn pbkdf2_sync( password: &[u8], salt: &[u8], iterations: u32, - digest: &str, + algorithm_name: &str, derived_key: &mut [u8], ) -> Result<(), AnyError> { - macro_rules! pbkdf2_hmac { - ($digest:ty) => {{ - pbkdf2::pbkdf2_hmac::<$digest>(password, salt, iterations, derived_key) - }}; - } - - match digest { - "md4" => pbkdf2_hmac!(md4::Md4), - "md5" => pbkdf2_hmac!(md5::Md5), - "ripemd160" => pbkdf2_hmac!(ripemd::Ripemd160), - "sha1" => pbkdf2_hmac!(sha1::Sha1), - "sha224" => pbkdf2_hmac!(sha2::Sha224), - "sha256" => pbkdf2_hmac!(sha2::Sha256), - "sha384" => pbkdf2_hmac!(sha2::Sha384), - "sha512" => pbkdf2_hmac!(sha2::Sha512), - _ => return Err(type_error("Unknown digest")), - } - - Ok(()) + match_fixed_digest_with_eager_block_buffer!( + algorithm_name, + fn () { + pbkdf2::pbkdf2_hmac::(password, salt, iterations, derived_key); + Ok(()) + }, + _ => { + Err(type_error(format!( + "unsupported digest: {}", + algorithm_name + ))) + } + ) } #[op2] @@ -558,50 +559,40 @@ pub async fn op_node_generate_secret_async(#[smi] len: i32) -> ToJsBuffer { } fn hkdf_sync( - hash: &str, + digest_algorithm: &str, ikm: &[u8], salt: &[u8], info: &[u8], okm: &mut [u8], ) -> Result<(), AnyError> { - macro_rules! hkdf { - ($hash:ty) => {{ - let hk = Hkdf::<$hash>::new(Some(salt), ikm); + match_fixed_digest_with_eager_block_buffer!( + digest_algorithm, + fn () { + let hk = Hkdf::::new(Some(salt), ikm); hk.expand(info, okm) - .map_err(|_| type_error("HKDF-Expand failed"))?; - }}; - } - - match hash { - "md4" => hkdf!(md4::Md4), - "md5" => hkdf!(md5::Md5), - "ripemd160" => hkdf!(ripemd::Ripemd160), - "sha1" => hkdf!(sha1::Sha1), - "sha224" => hkdf!(sha2::Sha224), - "sha256" => hkdf!(sha2::Sha256), - "sha384" => hkdf!(sha2::Sha384), - "sha512" => hkdf!(sha2::Sha512), - _ => return Err(type_error("Unknown digest")), - } - - Ok(()) + .map_err(|_| type_error("HKDF-Expand failed")) + }, + _ => { + Err(type_error(format!("Unsupported digest: {}", digest_algorithm))) + } + ) } #[op2(fast)] pub fn op_node_hkdf( - #[string] hash: &str, + #[string] digest_algorithm: &str, #[buffer] ikm: &[u8], #[buffer] salt: &[u8], #[buffer] info: &[u8], #[buffer] okm: &mut [u8], ) -> Result<(), AnyError> { - hkdf_sync(hash, ikm, salt, info, okm) + hkdf_sync(digest_algorithm, ikm, salt, info, okm) } #[op2(async)] #[serde] pub async fn op_node_hkdf_async( - #[string] hash: String, + #[string] digest_algorithm: String, #[buffer] ikm: JsBuffer, #[buffer] salt: JsBuffer, #[buffer] info: JsBuffer, @@ -609,7 +600,7 @@ pub async fn op_node_hkdf_async( ) -> Result { spawn_blocking(move || { let mut okm = vec![0u8; okm_len]; - hkdf_sync(&hash, &ikm, &salt, &info, &mut okm)?; + hkdf_sync(&digest_algorithm, &ikm, &salt, &info, &mut okm)?; Ok(okm.into()) }) .await? diff --git a/ext/node/polyfills/internal/crypto/hkdf.ts b/ext/node/polyfills/internal/crypto/hkdf.ts index 0a8dcbb2e9..cca40a3c62 100644 --- a/ext/node/polyfills/internal/crypto/hkdf.ts +++ b/ext/node/polyfills/internal/crypto/hkdf.ts @@ -23,6 +23,7 @@ import { } from "ext:deno_node/internal/crypto/util.ts"; import { createSecretKey, + getKeyMaterial, isKeyObject, KeyObject, } from "ext:deno_node/internal/crypto/keys.ts"; @@ -35,7 +36,7 @@ import { const validateParameters = hideStackFrames((hash, key, salt, info, length) => { validateString(hash, "digest"); - key = new Uint8Array(prepareKey(key)); + key = getKeyMaterial(prepareKey(key)); validateByteSource(salt, "salt"); validateByteSource(info, "info"); @@ -108,6 +109,8 @@ export function hkdf( validateFunction(callback, "callback"); + hash = hash.toLowerCase(); + op_node_hkdf_async(hash, key, salt, info, length) .then((okm) => callback(null, okm.buffer)) .catch((err) => callback(new ERR_CRYPTO_INVALID_DIGEST(err), undefined)); @@ -128,6 +131,8 @@ export function hkdfSync( length, )); + hash = hash.toLowerCase(); + const okm = new Uint8Array(length); try { op_node_hkdf(hash, key, salt, info, okm); diff --git a/ext/node/polyfills/internal/crypto/pbkdf2.ts b/ext/node/polyfills/internal/crypto/pbkdf2.ts index 5cf102fa98..4e58cb68b3 100644 --- a/ext/node/polyfills/internal/crypto/pbkdf2.ts +++ b/ext/node/polyfills/internal/crypto/pbkdf2.ts @@ -7,8 +7,19 @@ import { op_node_pbkdf2, op_node_pbkdf2_async } from "ext:core/ops"; import { Buffer } from "node:buffer"; import { HASH_DATA } from "ext:deno_node/internal/crypto/types.ts"; +import { + validateFunction, + validateString, + validateUint32, +} from "ext:deno_node/internal/validators.mjs"; +import { getArrayBufferOrView } from "ext:deno_node/internal/crypto/keys.ts"; +import { + ERR_CRYPTO_INVALID_DIGEST, + ERR_OUT_OF_RANGE, +} from "ext:deno_node/internal/errors.ts"; export const MAX_ALLOC = Math.pow(2, 30) - 1; +export const MAX_I32 = 2 ** 31 - 1; export type NormalizedAlgorithms = | "md5" @@ -29,6 +40,30 @@ export type Algorithms = | "sha384" | "sha512"; +function check( + password: HASH_DATA, + salt: HASH_DATA, + iterations: number, + keylen: number, + digest: string, +) { + validateString(digest, "digest"); + password = getArrayBufferOrView(password, "password", "buffer"); + salt = getArrayBufferOrView(salt, "salt", "buffer"); + validateUint32(iterations, "iterations", true); + validateUint32(keylen, "keylen"); + + if (iterations > MAX_I32) { + throw new ERR_OUT_OF_RANGE("iterations", `<= ${MAX_I32}`, iterations); + } + + if (keylen > MAX_I32) { + throw new ERR_OUT_OF_RANGE("keylen", `<= ${MAX_I32}`, keylen); + } + + return { password, salt, iterations, keylen, digest }; +} + /** * @param iterations Needs to be higher or equal than zero * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) @@ -39,18 +74,21 @@ export function pbkdf2Sync( salt: HASH_DATA, iterations: number, keylen: number, - digest: Algorithms = "sha1", + digest: string, ): Buffer { - if (typeof iterations !== "number" || iterations < 0) { - throw new TypeError("Bad iterations"); - } - if (typeof keylen !== "number" || keylen < 0 || keylen > MAX_ALLOC) { - throw new TypeError("Bad key length"); - } + ({ password, salt, iterations, keylen, digest } = check( + password, + salt, + iterations, + keylen, + digest, + )); + + digest = digest.toLowerCase() as NormalizedAlgorithms; const DK = new Uint8Array(keylen); if (!op_node_pbkdf2(password, salt, iterations, digest, DK)) { - throw new Error("Invalid digest"); + throw new ERR_CRYPTO_INVALID_DIGEST(digest); } return Buffer.from(DK); @@ -66,16 +104,26 @@ export function pbkdf2( salt: HASH_DATA, iterations: number, keylen: number, - digest: Algorithms = "sha1", + digest: string, callback: (err: Error | null, derivedKey?: Buffer) => void, ) { - if (typeof iterations !== "number" || iterations < 0) { - throw new TypeError("Bad iterations"); - } - if (typeof keylen !== "number" || keylen < 0 || keylen > MAX_ALLOC) { - throw new TypeError("Bad key length"); + if (typeof digest === "function") { + callback = digest; + digest = undefined as unknown as string; } + ({ password, salt, iterations, keylen, digest } = check( + password, + salt, + iterations, + keylen, + digest, + )); + + validateFunction(callback, "callback"); + + digest = digest.toLowerCase() as NormalizedAlgorithms; + op_node_pbkdf2_async( password, salt, diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts index 5a96117808..473670d2a6 100644 --- a/ext/node/polyfills/internal/crypto/sig.ts +++ b/ext/node/polyfills/internal/crypto/sig.ts @@ -67,10 +67,6 @@ export class SignImpl extends Writable { algorithm = algorithm.toLowerCase(); - if (algorithm.startsWith("rsa-")) { - // Allows RSA-[digest_algorithm] as a valid algorithm - algorithm = algorithm.slice(4); - } this.#digestType = algorithm; this.hash = createHash(this.#digestType); } @@ -121,11 +117,6 @@ export class VerifyImpl extends Writable { algorithm = algorithm.toLowerCase(); - if (algorithm.startsWith("rsa-")) { - // Allows RSA-[digest_algorithm] as a valid algorithm - algorithm = algorithm.slice(4); - } - this.#digestType = algorithm; this.hash = createHash(this.#digestType); } diff --git a/ext/node/polyfills/internal/crypto/types.ts b/ext/node/polyfills/internal/crypto/types.ts index 2b3ce34fee..45c0ea2869 100644 --- a/ext/node/polyfills/internal/crypto/types.ts +++ b/ext/node/polyfills/internal/crypto/types.ts @@ -3,7 +3,7 @@ import { Buffer } from "../../buffer.ts"; -export type HASH_DATA = string | ArrayBufferView | Buffer; +export type HASH_DATA = string | ArrayBufferView | Buffer | ArrayBuffer; export type BinaryToTextEncoding = "base64" | "base64url" | "hex" | "binary"; diff --git a/tests/integration/node_unit_tests.rs b/tests/integration/node_unit_tests.rs index d0b6d1fbd7..15d2021c1a 100644 --- a/tests/integration/node_unit_tests.rs +++ b/tests/integration/node_unit_tests.rs @@ -57,11 +57,14 @@ util::unit_test_factory!( buffer_test, child_process_test, console_test, - crypto_cipher_test = crypto / crypto_cipher_test, crypto_cipher_gcm_test = crypto / crypto_cipher_gcm_test, + crypto_cipher_test = crypto / crypto_cipher_test, crypto_hash_test = crypto / crypto_hash_test, + crypto_hkdf_test = crypto / crypto_hkdf_test, crypto_key_test = crypto / crypto_key_test, crypto_misc_test = crypto / crypto_misc_test, + crypto_pbkdf2_test = crypto / crypto_pbkdf2_test, + crypto_scrypt_test = crypto / crypto_scrypt_test, crypto_sign_test = crypto / crypto_sign_test, events_test, dgram_test, @@ -72,8 +75,6 @@ util::unit_test_factory!( _randomBytes_test = internal / _randomBytes_test, _randomFill_test = internal / _randomFill_test, _randomInt_test = internal / _randomInt_test, - pbkdf2_test = internal / pbkdf2_test, - scrypt_test = internal / scrypt_test, module_test, net_test, os_test, diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc index 5c0c854a88..f0b667df44 100644 --- a/tests/node_compat/config.jsonc +++ b/tests/node_compat/config.jsonc @@ -52,6 +52,7 @@ "test-crypto-dh.js", "test-crypto-hkdf.js", "test-crypto-hmac.js", + "test-crypto-pbkdf2.js", "test-crypto-prime.js", "test-crypto-stream.js", "test-crypto-x509.js", @@ -257,6 +258,7 @@ "test-crypto-hash.js", "test-crypto-hkdf.js", "test-crypto-hmac.js", + "test-crypto-pbkdf2.js", "test-crypto-prime.js", "test-crypto-secret-keygen.js", "test-crypto-stream.js", diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md index a36ad1b634..ec2a2337e3 100644 --- a/tests/node_compat/runner/TODO.md +++ b/tests/node_compat/runner/TODO.md @@ -505,7 +505,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co - [parallel/test-crypto-op-during-process-exit.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-op-during-process-exit.js) - [parallel/test-crypto-padding-aes256.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-padding-aes256.js) - [parallel/test-crypto-padding.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-padding.js) -- [parallel/test-crypto-pbkdf2.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-pbkdf2.js) - [parallel/test-crypto-private-decrypt-gh32240.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-private-decrypt-gh32240.js) - [parallel/test-crypto-psychic-signatures.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-psychic-signatures.js) - [parallel/test-crypto-publicDecrypt-fails-first-time.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-crypto-publicDecrypt-fails-first-time.js) diff --git a/tests/node_compat/test/parallel/test-crypto-pbkdf2.js b/tests/node_compat/test/parallel/test-crypto-pbkdf2.js new file mode 100644 index 0000000000..263f73130a --- /dev/null +++ b/tests/node_compat/test/parallel/test-crypto-pbkdf2.js @@ -0,0 +1,251 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. + +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const crypto = require('crypto'); + +function runPBKDF2(password, salt, iterations, keylen, hash) { + const syncResult = + crypto.pbkdf2Sync(password, salt, iterations, keylen, hash); + + crypto.pbkdf2(password, salt, iterations, keylen, hash, + common.mustSucceed((asyncResult) => { + assert.deepStrictEqual(asyncResult, syncResult); + })); + + return syncResult; +} + +function testPBKDF2(password, salt, iterations, keylen, expected, encoding) { + const actual = runPBKDF2(password, salt, iterations, keylen, 'sha256'); + assert.strictEqual(actual.toString(encoding || 'latin1'), expected); +} + +// +// Test PBKDF2 with RFC 6070 test vectors (except #4) +// + +testPBKDF2('password', 'salt', 1, 20, + '\x12\x0f\xb6\xcf\xfc\xf8\xb3\x2c\x43\xe7\x22\x52' + + '\x56\xc4\xf8\x37\xa8\x65\x48\xc9'); + +testPBKDF2('password', 'salt', 2, 20, + '\xae\x4d\x0c\x95\xaf\x6b\x46\xd3\x2d\x0a\xdf\xf9' + + '\x28\xf0\x6d\xd0\x2a\x30\x3f\x8e'); + +testPBKDF2('password', 'salt', 4096, 20, + '\xc5\xe4\x78\xd5\x92\x88\xc8\x41\xaa\x53\x0d\xb6' + + '\x84\x5c\x4c\x8d\x96\x28\x93\xa0'); + +testPBKDF2('passwordPASSWORDpassword', + 'saltSALTsaltSALTsaltSALTsaltSALTsalt', + 4096, + 25, + '\x34\x8c\x89\xdb\xcb\xd3\x2b\x2f\x32\xd8\x14\xb8\x11' + + '\x6e\x84\xcf\x2b\x17\x34\x7e\xbc\x18\x00\x18\x1c'); + +testPBKDF2('pass\0word', 'sa\0lt', 4096, 16, + '\x89\xb6\x9d\x05\x16\xf8\x29\x89\x3c\x69\x62\x26\x65' + + '\x0a\x86\x87'); + +testPBKDF2('password', 'salt', 32, 32, + '64c486c55d30d4c5a079b8823b7d7cb37ff0556f537da8410233bcec330ed956', + 'hex'); + +// Error path should not leak memory (check with valgrind). +assert.throws( + () => crypto.pbkdf2('password', 'salt', 1, 20, 'sha1'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError' + } +); + +for (const iterations of [-1, 0, 2147483648]) { + assert.throws( + () => crypto.pbkdf2Sync('password', 'salt', iterations, 20, 'sha1'), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + } + ); +} + +['str', null, undefined, [], {}].forEach((notNumber) => { + assert.throws( + () => { + crypto.pbkdf2Sync('password', 'salt', 1, notNumber, 'sha256'); + }, { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "keylen" argument must be of type number.' + + `${common.invalidArgTypeHelper(notNumber)}` + }); +}); + +[Infinity, -Infinity, NaN].forEach((input) => { + assert.throws( + () => { + crypto.pbkdf2('password', 'salt', 1, input, 'sha256', + common.mustNotCall()); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "keylen" is out of range. It ' + + `must be an integer. Received ${input}` + }); +}); + +[-1, 2147483648, 4294967296].forEach((input) => { + assert.throws( + () => { + crypto.pbkdf2('password', 'salt', 1, input, 'sha256', + common.mustNotCall()); + }, { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + }); +}); + +// Should not get FATAL ERROR with empty password and salt +// https://github.com/nodejs/node/issues/8571 +crypto.pbkdf2('', '', 1, 32, 'sha256', common.mustSucceed()); + +assert.throws( + () => crypto.pbkdf2('password', 'salt', 8, 8, common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "digest" argument must be of type string. ' + + 'Received undefined' + }); + +assert.throws( + () => crypto.pbkdf2Sync('password', 'salt', 8, 8), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "digest" argument must be of type string. ' + + 'Received undefined' + }); + +assert.throws( + () => crypto.pbkdf2Sync('password', 'salt', 8, 8, null), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: 'The "digest" argument must be of type string. ' + + 'Received null' + }); +[1, {}, [], true, undefined, null].forEach((input) => { + assert.throws( + () => crypto.pbkdf2(input, 'salt', 8, 8, 'sha256', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); + + assert.throws( + () => crypto.pbkdf2('pass', input, 8, 8, 'sha256', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); + + assert.throws( + () => crypto.pbkdf2Sync(input, 'salt', 8, 8, 'sha256'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); + + assert.throws( + () => crypto.pbkdf2Sync('pass', input, 8, 8, 'sha256'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + } + ); +}); + +['test', {}, [], true, undefined, null].forEach((i) => { + const received = common.invalidArgTypeHelper(i); + assert.throws( + () => crypto.pbkdf2('pass', 'salt', i, 8, 'sha256', common.mustNotCall()), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: `The "iterations" argument must be of type number.${received}` + } + ); + + assert.throws( + () => crypto.pbkdf2Sync('pass', 'salt', i, 8, 'sha256'), + { + code: 'ERR_INVALID_ARG_TYPE', + name: 'TypeError', + message: `The "iterations" argument must be of type number.${received}` + } + ); +}); + +// Any TypedArray should work for password and salt. +// TODO(@lucacasonato): SharedArrayBuffer is not supported +for (const SomeArray of [Uint8Array, Uint16Array, Uint32Array, Float32Array, + Float64Array, ArrayBuffer/*, SharedArrayBuffer*/]) { + runPBKDF2(new SomeArray(10), 'salt', 8, 8, 'sha256'); + runPBKDF2('pass', new SomeArray(10), 8, 8, 'sha256'); +} + +// TODO(@lucacasonato): we throw this asynchronously +// assert.throws( +// () => crypto.pbkdf2('pass', 'salt', 8, 8, 'md55', common.mustNotCall()), +// { +// code: 'ERR_CRYPTO_INVALID_DIGEST', +// name: 'TypeError', +// message: 'Invalid digest: md55' +// } +// ); + +assert.throws( + () => crypto.pbkdf2Sync('pass', 'salt', 8, 8, 'md55'), + { + code: 'ERR_CRYPTO_INVALID_DIGEST', + name: 'TypeError', + message: 'Invalid digest: md55' + } +); + +if (!common.hasOpenSSL3) { + // TODO(@lucacasonato): we don't support blake2b512 and blake2s256 + const kNotPBKDF2Supported = ['shake128', 'shake256', 'blake2b512', 'blake2s256']; + crypto.getHashes() + .filter((hash) => !kNotPBKDF2Supported.includes(hash)) + .forEach((hash) => { + runPBKDF2(new Uint8Array(10), 'salt', 8, 8, hash); + }); +} + +{ + // This should not crash. + assert.throws( + () => crypto.pbkdf2Sync('1', '2', 1, 1, '%'), + { + code: 'ERR_CRYPTO_INVALID_DIGEST', + name: 'TypeError', + message: 'Invalid digest: %' + } + ); +} diff --git a/tests/unit_node/crypto/crypto_hkdf_test.ts b/tests/unit_node/crypto/crypto_hkdf_test.ts new file mode 100644 index 0000000000..be5d99086d --- /dev/null +++ b/tests/unit_node/crypto/crypto_hkdf_test.ts @@ -0,0 +1,36 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +import { hkdfSync } from "node:crypto"; +import { assertEquals } from "@std/assert/mod.ts"; +import { Buffer } from "node:buffer"; +import nodeFixtures from "../testdata/crypto_digest_fixtures.json" with { + type: "json", +}; + +Deno.test("crypto.hkdfSync - compare with node", async (t) => { + const DATA = "Hello, world!"; + const SALT = "salt"; + const INFO = "info"; + const KEY_LEN = 64; + + for (const { digest, hkdf } of nodeFixtures) { + await t.step({ + name: digest, + ignore: digest.includes("blake"), + fn() { + let actual: string | null; + try { + actual = Buffer.from(hkdfSync( + digest, + DATA, + SALT, + INFO, + KEY_LEN, + )).toString("hex"); + } catch { + actual = null; + } + assertEquals(actual, hkdf); + }, + }); + } +}); diff --git a/tests/unit_node/internal/pbkdf2_test.ts b/tests/unit_node/crypto/crypto_pbkdf2_test.ts similarity index 95% rename from tests/unit_node/internal/pbkdf2_test.ts rename to tests/unit_node/crypto/crypto_pbkdf2_test.ts index 6a428b0375..8ace870cb4 100644 --- a/tests/unit_node/internal/pbkdf2_test.ts +++ b/tests/unit_node/crypto/crypto_pbkdf2_test.ts @@ -1,6 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { pbkdf2, pbkdf2Sync } from "node:crypto"; import { assert, assertEquals } from "@std/assert/mod.ts"; +import nodeFixtures from "../testdata/crypto_digest_fixtures.json" with { + type: "json", +}; type Algorithms = | "md5" @@ -377,6 +380,35 @@ Deno.test("pbkdf2Sync hashes data correctly", () => { }); }); +Deno.test("crypto.pbkdf2Sync - compare with node", async (t) => { + const DATA = "Hello, world!"; + const SALT = "salt"; + const ITERATIONS = 1000; + const KEY_LEN = 64; + + for (const { digest, pkdf2 } of nodeFixtures) { + await t.step({ + name: digest, + ignore: digest.includes("blake"), + fn() { + let actual: string | null; + try { + actual = pbkdf2Sync( + DATA, + SALT, + ITERATIONS, + KEY_LEN, + digest as Algorithms, + ).toString("hex"); + } catch { + actual = null; + } + assertEquals(actual, pkdf2); + }, + }); + } +}); + // TODO(@littledivy): assertCallbackErrorUncaught exits for async operations on the thread pool. // Deno.test("[std/node/crypto] pbkdf2 callback isn't called twice if error is thrown", async () => { // const importUrl = new URL("node:crypto", import.meta.url); diff --git a/tests/unit_node/internal/scrypt_test.ts b/tests/unit_node/crypto/crypto_scrypt_test.ts similarity index 100% rename from tests/unit_node/internal/scrypt_test.ts rename to tests/unit_node/crypto/crypto_scrypt_test.ts diff --git a/tests/unit_node/crypto/crypto_sign_test.ts b/tests/unit_node/crypto/crypto_sign_test.ts index 2ca0af9438..77c5caf75e 100644 --- a/tests/unit_node/crypto/crypto_sign_test.ts +++ b/tests/unit_node/crypto/crypto_sign_test.ts @@ -3,6 +3,9 @@ import { assert, assertEquals } from "@std/testing/asserts.ts"; import { createSign, createVerify, sign, verify } from "node:crypto"; import { Buffer } from "node:buffer"; +import fixtures from "../testdata/crypto_digest_fixtures.json" with { + type: "json", +}; const rsaPrivatePem = Buffer.from( await Deno.readFile( @@ -138,3 +141,41 @@ AwEH createSign("SHA256").update("test").sign(pem, "base64"); }, }); + +Deno.test("crypto.createSign|sign - compare with node", async (t) => { + const DATA = "Hello, world!"; + const privateKey = Deno.readTextFileSync( + new URL(import.meta.resolve("../testdata/rsa_private.pem")), + ); + for (const { digest, signature } of fixtures) { + await t.step(digest, () => { + let actual: string | null; + try { + const s = createSign(digest); + s.update(DATA); + actual = s.sign(privateKey).toString("hex"); + } catch { + actual = null; + } + assertEquals(actual, signature); + }); + } +}); + +Deno.test("crypto.createVerify|verify - compare with node", async (t) => { + const DATA = "Hello, world!"; + const publicKey = Deno.readTextFileSync( + new URL(import.meta.resolve("../testdata/rsa_public.pem")), + ); + for (const { digest, signature } of fixtures) { + await t.step({ + name: digest, + ignore: signature === null, + fn: () => { + const s = createVerify(digest); + s.update(DATA); + s.verify(publicKey, signature!); + }, + }); + } +}); diff --git a/tests/unit_node/crypto/generate_fixture.mjs b/tests/unit_node/crypto/generate_fixture.mjs new file mode 100644 index 0000000000..3724fe4aff --- /dev/null +++ b/tests/unit_node/crypto/generate_fixture.mjs @@ -0,0 +1,69 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Run this file with `node` to regenerate the testdata/crypto_digest_fixtures.json file. + +import { readFileSync, writeFileSync } from "node:fs"; +import { join } from "node:path"; +import crypto from "node:crypto"; +import { Buffer } from "node:buffer"; + +const privateKey = readFileSync( + join(import.meta.dirname, "..", "testdata", "rsa_private.pem"), +); + +const fixtures = []; + +const DATA = "Hello, world!"; +const SALT = "salt"; +const INFO = "info"; +const ITERATIONS = 1000; +const KEY_LEN = 64; + +for (const digest of crypto.getHashes()) { + const hasher = crypto.createHash(digest); + hasher.update(DATA); + let hash; + try { + hash = hasher.digest().toString("hex"); + } catch { + hash = null; + } + + const sign = crypto.createSign(digest); + sign.update(DATA); + let signature; + try { + signature = sign.sign(privateKey).toString("hex"); + } catch { + signature = null; + } + + let pkdf2; + try { + pkdf2 = crypto.pbkdf2Sync(DATA, SALT, ITERATIONS, KEY_LEN, digest).toString( + "hex", + ); + } catch { + pkdf2 = null; + } + + let hkdf; + try { + hkdf = Buffer.from(crypto.hkdfSync(digest, DATA, SALT, INFO, KEY_LEN)) + .toString("hex"); + } catch { + hkdf = null; + } + + fixtures.push({ + digest, + hash, + signature, + pkdf2, + hkdf, + }); +} + +writeFileSync( + join(import.meta.dirname, "..", "testdata", "crypto_digest_fixtures.json"), + JSON.stringify(fixtures, null, 2), +); diff --git a/tests/unit_node/testdata/crypto_digest_fixtures.json b/tests/unit_node/testdata/crypto_digest_fixtures.json new file mode 100644 index 0000000000..c5b65261df --- /dev/null +++ b/tests/unit_node/testdata/crypto_digest_fixtures.json @@ -0,0 +1,366 @@ +[ + { + "digest": "RSA-MD5", + "hash": "6cd3556deb0da54bca060b4c39479839", + "signature": "45f2e7ac7c0d323c18e9c8554d15e21a8fb72944928e0b7acc2185402448c39af05340391ee9aac64c45a947bc3ed801de8791aaa5788a62e909d7c8d92e63c7f119753219a48c29ea78e371d3a89c0bd64d756b829fa6faaa8fa46bc28228dd4430aca845c02f3b6b835b140247838ac5b96434d1049115785f3f52034e0cc46f6cafc5f2921b9928403aea1edd85f3a99e12324abeeaaaa040b320f8af157c5df7fd7479415771fd116daed933b43eac51b3c274ba9283aaf3f1b6f1f527a0cea76f9a43dca3b9ff03834feed13a0fd488e6750a9d46ddb1ad69ac88090af050458eeafd1e9fa317c4ecc61d90289d5378e6e3693ef6da2e4a4803b6108145", + "pkdf2": "3083f052ca2e90daf96a87b8b8c1daa069bff60c73045c9504d3b424807b5410a724ea5ba28b9bccb6bad59c0e181663b07156a59b09cee85ffe223f21ff6db6", + "hkdf": "f516674e1b1faf2c1a505041634460d94739e2e39be4e6a097f926f5cc9788c09892efcb407019dc469c4014c8d847cf4c5a6eeefccf8e5266ca3dd02ea57529" + }, + { + "digest": "RSA-RIPEMD160", + "hash": "58262d1fbdbe4530d8865d3518c6d6e41002610f", + "signature": "31b25b680d0df8cb27df71c38fe7931a185c056b98c072a4f4956d6105a44ed53d7e457d366074cc26cd1cd0c226cf0cac509264f42651c208eb73f29f48dc2022009595e4ccf97662cf2840d69987f51118e9854fd10497a9e1dcb2ef390c9d09767f7d801aa2dd3d5c0e51249a195769593696725ba81e9a6c961a4b2c13827c467ccafa76f0ea5a9f43b936f3b35a8c316f116e0ac0d6f56ed8c0189d28ea1dc3a56b97a75c8f5c5cdc1682a76b5d852dd7282371ecb379abf0864eff0c437fca813d0ddc1ce34e8327a604336eeab6d907edf39e0303f0b8a45e0721e15d85c8c329047920b1ec8863bc6c0b172a57a6bb3611edb1f5c4d49aa1089cb5fd", + "pkdf2": "2473b58bc86761bd5e8d35153475d44e8ba2dd4a156d6e69d79cbc33244f35bfea27bb3122354d98dbd792c679d8994f10a8eaf7c946e6a65566413a13060386", + "hkdf": "c7d8a2fe1ae6d243aef0b989c2483221f0e9c3234719273438dca33b97269435047f441ae104903fafaa051267ab34ba469449511a8e40fc5444a01e03cfe651" + }, + { + "digest": "RSA-SHA1", + "hash": "943a702d06f34599aee1f8da8ef9f7296031d699", + "signature": "80174b0678707d38335940a010bd6acc1a6ade2c911251d013875816eb4c5169d9d4cb2ac9f6ce4b29b92f5750f185a273abd7c8a2434a9d1a50df5311999655d2a535c77b79997ed34fe51a863c450251d1b85bbb4dd10da1162e6e74194af606579a8724e55b93bded2d10216bb63cf00e848ceea6cdba78eab4f735ca620b36fdfd19bf15706df860a4c589160ce3c32aa51c27561de82a00cdcca709045cb8622adb6f759eb5e0f2f3f6056ca1b1a91206712ce92f5684d64931bc001bec7d2e8d2469f0818106070698160e9e21404971ea45940797fa3a61a39a25b7c6c75986accd4832db50a25ef3fceca629e104e2f03affd83b94cf074614e11c18", + "pkdf2": "e77df42e685bd1fe2e19e374e1dd8470df71182311e048a6a280f36ef24c661e46fe8e8cc763942615934a631ac3aeda24bb9fda71e04f89f0c85f2f762abd98", + "hkdf": "936c59e9394b5b1fdc26bd2eec0dc8c8d52fb97198e021805f0ae58c3f8b631d0020002b1ce0df9d890377fcc808166c906f128d06d77d404061d56560f3340d" + }, + { + "digest": "RSA-SHA1-2", + "hash": "943a702d06f34599aee1f8da8ef9f7296031d699", + "signature": "80174b0678707d38335940a010bd6acc1a6ade2c911251d013875816eb4c5169d9d4cb2ac9f6ce4b29b92f5750f185a273abd7c8a2434a9d1a50df5311999655d2a535c77b79997ed34fe51a863c450251d1b85bbb4dd10da1162e6e74194af606579a8724e55b93bded2d10216bb63cf00e848ceea6cdba78eab4f735ca620b36fdfd19bf15706df860a4c589160ce3c32aa51c27561de82a00cdcca709045cb8622adb6f759eb5e0f2f3f6056ca1b1a91206712ce92f5684d64931bc001bec7d2e8d2469f0818106070698160e9e21404971ea45940797fa3a61a39a25b7c6c75986accd4832db50a25ef3fceca629e104e2f03affd83b94cf074614e11c18", + "pkdf2": "e77df42e685bd1fe2e19e374e1dd8470df71182311e048a6a280f36ef24c661e46fe8e8cc763942615934a631ac3aeda24bb9fda71e04f89f0c85f2f762abd98", + "hkdf": "936c59e9394b5b1fdc26bd2eec0dc8c8d52fb97198e021805f0ae58c3f8b631d0020002b1ce0df9d890377fcc808166c906f128d06d77d404061d56560f3340d" + }, + { + "digest": "RSA-SHA224", + "hash": "8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568", + "signature": "70a8b455d63d2af5b12b0eefcb50ae245dcac054a74fd8a5beccca5ed12fd545e4dcdf2da3455f49e5eaf559772dc885ae24fd9e42790a1e175ec221a22329f2cc67d5c8ad1f9377fa1d713cb0d554f8335325a6080c272c10fb59f5fb7da9b39dcc76a50b49ed5f28d8cdd0499fbf5a19cae4b5b4a4a103201933a0e94e5532c6aaf05fbb060a3be5dcd5addef23b80651c51831ee479142cb1ddac0a37c6251f6eceee55217fcced2462fb7bef3e3aeeda98672598dc6aca6a49fb2df1b3f0ac569b666afeb8ca3f5b1bfa0bcca49f5fe6d3784fba1bb931077633d5a0a731c2bb22a820c8497ac1db888bd542f73ba2358d3a9bc056a7eca2f3a78b437820", + "pkdf2": "d3adee1df932bac56eecdba710702025678d6e024d315c925ae274a3796c474219291983a76f2b8bd67aad26fbebcf5bb5088c99b6124ae1b9c7c9b2eb37c8f4", + "hkdf": "1c3a0bd11a8c71c010353b075474aa2a6740f5cade0fff895019e08ebdaa68f8dfc4e74a5e013e2e9e0ab6852884311c1dac89432d34b2f762113ebf9a37597a" + }, + { + "digest": "RSA-SHA256", + "hash": "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", + "signature": "67b67a87fc4a576342fc46167395a255654dddc5036e8a7d323acf862470e0612b90b1abf22166e3d124d4866e82146b5735fb85895355d027efdcb406a8cb87c8f00aea128e74cb95ca8a69cb970e3f02a11a6911253f9fba54ae7b75d7f1f29d51338d43f4ed59a36e3b7e408591cd9feaa818f1b0c6df6155cc89cc95c6686ce82a78fd8d34e82d9047ce936c3a67ac85d2924083610d6db5040409f09a46f97d35248b5451ca51be6cfbc9f94ff630eb8ff95e114a666cc1cc011af03d77f9b24f8add5244a83e0784dbcf785ebcfcd015b876963666b113dc26f0222aa95fdfbe1e64ac6b48780e1ff69b2588ff4bce9b15f9d53abba96b813a3d657cbe", + "pkdf2": "a8a2a7dcf7b65c8498ab1fbc62d56fa47255d668c1c3f21c5b236259d1dd8b11859026e737216cceb7a8facb8d27656f2b990db98786f12a8ce68e56a30f0c0b", + "hkdf": "a35a5ba99add2c508b9014ca7eb7d461a85989257b29a46a0041361c85b2c48838199873987b5419ede395aa09129bcb5fbd607b8b769e6711e1fea5e0ebcbc0" + }, + { + "digest": "RSA-SHA3-224", + "hash": "6a33e22f20f16642697e8bd549ff7b759252ad56c05a1b0acc31dc69", + "signature": "2504c7a4735c251aa01644ae56a9f7c891f45e9fb6e25fea1c25074139dcab114e77d64ae7788bcbd8d5d452db60b47e6a0b324b0e3482ff794581da0cbe960a97ba75c1023eab2320fa9022ae715b5aa2fe1186916fb9a1c931d9f28ba8e12a4dd145efff6abfcfb68eca6536655c96cf059df406d5c51d1814b2f61c6bdc10a2b083d29f50a064bc6ffdddf5cb45df2577a7149a430e1314f87427972d8bb4775bd92e99a2298095cb83e60e50f503aab6d9fd41398b55d5dcfa6022049891561e6395a7a3e56a1d92a8667733a68d9b0374952d95e4d5a51b0b22d390cf579bb536e59cea8272b2d5749fa70d48c396b6ba94188ce677beb8f00937640096", + "pkdf2": "014d30aa9d1c1d8e3920f4bcc399f37b16d6c346d570d4345229d64ab95e504b1c6b253133c218a2b46f29aa0f9792d7ad60761159cc3550cb9a0914bfdc9951", + "hkdf": "b6c671ec02ee7f215dcee9ecade623831658470156f3edab205b7e3a70587f3c538eac8ec92e3a5bf6808b9b939d84e4f8ba5ee3532d7adb152211cc8f79cf9b" + }, + { + "digest": "RSA-SHA3-256", + "hash": "f345a219da005ebe9c1a1eaad97bbf38a10c8473e41d0af7fb617caa0c6aa722", + "signature": "b3b475d03da73b4bf8fefa5f49231dfd8f95605e0c3ea058d4607f1274ddcf4f69b1751df8493df1c5fe31883aff197688de1991885bb9ed4266f7df0db4fc99b33bee32a9237a9c2714e461fefb311923c1e91fe7cbe1830206bb0b3d962bfccdf5c37e8ce9e01f203851e34f22162175f44048002483ff2e3ffc658506a23f358f3192dbba759a8e1f66d3abf934b72295f1b51f8a92c837c76006084b65c0df8e072e43b1122bb15b3043dceb026e2cf311d04d6f4a3506e7e5b8a08fcaa0caeaa18a6397d185e5b5d1bc962f22b80db798a42fa206cd2db53b02bc5e2e69d9566f1ff3fccb4690545ff19424001cf3c0504c53adc0f2ce4354596b8a4d69", + "pkdf2": "4ba48814e191c34c9d38f5ccf6c5cf1a61082c2f8b36e99bd8210781af995734f62c6a714ad6639ecb55224d91b2ac7d69030335d2c367d833823814ef9f2213", + "hkdf": "e60f1e8214b7907d63d8cfb2bfb083051d3c9a56e4f94aabc1ff3543986aac5d4823db903090af81824964d6ca39da8388495e8838a9c0c407cb3546f44f9a04" + }, + { + "digest": "RSA-SHA3-384", + "hash": "6ba9ea268965916f5937228dde678c202f9fe756a87d8b1b7362869583a45901fd1a27289d72fc0e3ff48b1b78827d3a", + "signature": "256542ac61773cec2261fbc8cde4d6524e7a91f9fefcc08792c7c4b27fb843d099a5fce9819503949ad8fde307a7fc4185754dc5ed3d473da065376e4a9c804265dc4ff858a774d6eb19e663a26339d087a3e96b8f9133becc0ee2b404cfcfca58ffadbd48a5c31dced92f188b40bd179efdf9ae5394d0e02b513958f46adbdf745e8dada0dd97e38bf28bf7ad87aeb6771a40e3fef5f35161d4eeafe7b6ba1c2101388a22e75586fab599cbc070bf4d0fbe8304a7c579a34ce0000c87b33882c2d86ff11dbc218604fa7e7203e4efaa6e9605ae41958c0c2de0b7d76da9a6e606b3c38d29dad55675f9f138b11afc96e71ba5e41924bc5c676db777aed7ea04", + "pkdf2": "f609fda1c21cf4e59e7142438f2e04bb29f72010d49546e1c192755c6af3b97903da8483be8356afb865ffc334d23fa2439063c287ec9491618bfb5f791675f4", + "hkdf": "818ae5bc92cf582db87d73316e1a0649b457dfcd198db3e4edb1c6b20515dcf14f8758951765d9e2b4f7914a75ea04815e82050a6ca602c1a76f6c9999813955" + }, + { + "digest": "RSA-SHA3-512", + "hash": "8e47f1185ffd014d238fabd02a1a32defe698cbf38c037a90e3c0a0a32370fb52cbd641250508502295fcabcbf676c09470b27443868c8e5f70e26dc337288af", + "signature": "925bd1da0c91caba58c0a407f4f63e9b16f52444c3b71476f4043ee00ef97bb428a396e8a45d75b3204142d2e18886f36b0b0a1c6d25d80b010e4063ffb310741907e3217badadaf53d018100d78eace2e941c0ec2b6f9b47ceee455d55bfb1711b1255ae1fa0d8fb5bf869009c1b558b02fd4cabd83c55a76a1d96bdb4cdbb42f6ad1f36984c39e6c6fe4edc3a771766742ec461d29e8a93704a48323c0c0f9cc066bf1daeb4e83aa3aa7a3c2067495cf320a9e98b7f1db638a23fc135992fcc3bbfb52be115c35b055cdbf4733c30098674ff19f1ef218a4643b0e2ecb7c0597a406b304b67a36eed0300313a65a4af5621ef59a5ffe53e7d6763a83e0014a", + "pkdf2": "e1f0b454bf5d729fbb13e534229521a87aee130078555791d83834a8c51fb681ce4dfe02afd5f063082d8abba0b456084c677aeb6f8e0d70305322ec2ea97203", + "hkdf": "f27d87f9f6b87718073c8d2ad6bae00b4162cecde350c856252dd611120c433373a0c0d3946a8582bf855bf581439a14ca4f355fcd18881331f4a3b1027e84b2" + }, + { + "digest": "RSA-SHA384", + "hash": "55bc556b0d2fe0fce582ba5fe07baafff035653638c7ac0d5494c2a64c0bea1cc57331c7c12a45cdbca7f4c34a089eeb", + "signature": "77d465006b8843cf30c5e93b7d3f63b0daf2df1d693c4e1c0f9c759ec8410c088f331c5bf89bca7218edd1bdf3f2d948c9be9656f44a630748395429a09289acb05be80b0d000bacd7826c33a584dcbcf658839015d07561668e882bad4505c475a04766a7eabb35b15b8b54d6fcf9ebdd25b699f0d88906ba2999de1e8e59cc75969bc819fd514b1002a6667a03c861fafee03cccb9fd926352139dc1a916ba56b36e9e69fd328cb2403b9f747350721261158021df9ab2a75a7c5a3e39b42f3722ea1997a039ecc033f64366974336dbfd3294820400fe623d2993593c51f4398290a51bf05dc452c5991e26e8971be759472635e86b08230d04d05bce88f4", + "pkdf2": "b2e2b9e03d9e6f5218b74be7002c8704558feae855272ab3a973ee7deb387c26483f87fb6463bc5768406b398896a29ac2d17bd45afccf369a403ffa396bb455", + "hkdf": "54aef7f7256a147e52d67888a4543c6084b4ac46cd7bf919576bf8c4e9dad6b8a1fc9ad9c6f90f2af8e76d8d815ae2226d67a11ccb524a0c0be3cc858f4ddb10" + }, + { + "digest": "RSA-SHA512", + "hash": "c1527cd893c124773d811911970c8fe6e857d6df5dc9226bd8a160614c0cd963a4ddea2b94bb7d36021ef9d865d5cea294a82dd49a0bb269f51f6e7a57f79421", + "signature": "50e2973977e1be1218134a1f50732a8f54c4b0dfb836be816c304edce9bef4b1be566a295b05c6c82de00eb915bc61ca4bab073a13061987ca19af280202a4c6c496cf6262655e6a6f1a53f923ca048b4bc21e31282edd239cec8840b2411c1aa2abe5b9bf7de5066f43f6e2e0a0691ac6f2fc71ed679ae644b6d799261ed694dc9e27c81142ffb88883440ee92a6086d7c3d62572eac13480034219a9edbda7e5556f335bbe1a37ef0df26ae3ffe99b968d40a93d9ca9a3c76025409b5da041242afa977a5a0438be946562a48a764dc6d4c924fec82590e01b807d410a432b95ee2e5173d245a565cdecdc321521d6f61e965628013181bc7a9462a450d895", + "pkdf2": "284f17da4e32b7b4780b31f94405e115ca7e6ae025b05d8398f421c3a3f36d903a1fae9471674342bec7d51c6f32996df0c843a20105c8451e9cd17ecbddb3ed", + "hkdf": "cf181757f66abf44c88cd3d4ad0ed99b82cf47a8a6f870ff1561a12da7af03251590250af7473a1a3c5c5e794f29c44e8f25f69fb7985258161e74b433510262" + }, + { + "digest": "RSA-SHA512/224", + "hash": "32620068b859669b45b31008e08b7384649ad2ca3f5163a3a71e5745", + "signature": "b5d1ec54de69d29dbc77595fec62bb16019b7cc275ead04338c17ee4b759fcb8443477e1708d45374b786acfeb616c1078d199b2a28d76f46d4b74a498c2bfb2b174632da895cae5558f36101f0596adb40152ac09b49f1726f293237bcf604ab7bf461c7e2dd78c909d7503ec4132722551842f7e8ef54317f4b58784c447fd68067c689c288c8401dc95276356e1396a127eac82a0bd0753cc4c00718e7240d523af95a1cd960885da64172e3325a225557f96e6307340dd45a499ac20af01a2e3e7a4be706a40249c6ce7e24dabdcbd577744c34cf8e182d9a7923c52cd508a0e39125003a0c74ac714f5c892187c6fc7417d552127b3c24c57e484212ab6", + "pkdf2": "69e623258a79c04b84aabc9333b17fc8a5ee8d7ebd06dd80236865584ccc98fce3180319f74748f6a6c2b44833425a6ab52e8ab22db62365799f14727102485d", + "hkdf": "c155ac0b2833f309322f258625df2c9926570fabbe66eff9713b977291054b57b8cd3280079613d8167a99f259087422cff9756780148bf683c1d5c03e015907" + }, + { + "digest": "RSA-SHA512/256", + "hash": "330c723f25267587db0b9f493463e017011239169cb57a6db216c63774367115", + "signature": "6313742057ecbf4f54476799b018d1e18c2db920ef23386208f1e23e5af30d38edc0d11f1220e2cc0f898cd3e4cd9d4920fed30ff9f725404d4750f230fe9dafdfde95280a8713043359a09d45fbda484c469e61ae4fd615d616e76f6daa27acd3542d823e88f2dfd943b39f38884f9ed4695b8f004b4c67777de63474fe9dbbfeab966113907663f14c7822658aab70dceb7d1a6ed9842085762ab100ee915941295a045b29ef09af19eab58c79d3e5ffcbda61e2be35770c1d3b55156357c3cca806309b1ddc7e52f47e245d1d49237f0aa7156e89c76a17780a62fa552eeb7b623410e387d0a9412d4688725ed8df0a86e8b548ea6bb785ea48b6c0e39332", + "pkdf2": "c3d18f17c825a1630bc10695bfd4faa0309e862d010c0b74fb41b6ce5e46853cc435560a1b9cd940254103068aebc1f24d2580385b1890e7f10356ef4c8f507d", + "hkdf": "8451f1238da8577794ecc9b75832cabff44f279e5f8258cbf553c21f9735c19c7974e9a74879b4c59386116bb1a4bb50f149c9a4146b10de312fa679c1343b80" + }, + { + "digest": "RSA-SM3", + "hash": "e3bca101b496880c3653dad85861d0e784b00a8c18f7574472d156060e9096bf", + "signature": null, + "pkdf2": "70b3d2ac3b82dd7b8c35db45f99f49e029be2b6f1ef9ce1d9137267b729c06f7099dc7f571f2b05dbe5172118a7ae787cd4836fbf9ddce9aacc933c7c4e5537b", + "hkdf": "88b773032a2f131b1aa4ebdb69fdde7c42281b7c689bfe1f5bd43c7e1a6f555f101657c3f83ccd5918b4c6a8df04d0b58ebf77ec2181f8c5e7cfecb6f13fba61" + }, + { + "digest": "blake2b512", + "hash": "a2764d133a16816b5847a737a786f2ece4c148095c5faa73e24b4cc5d666c3e45ec271504e14dc6127ddfce4e144fb23b91a6f7b04b53d695502290722953b0f", + "signature": null, + "pkdf2": "ae8782ec9a457cf51606ac8cfc97c4c5f9fe2485714f7be7fddfddf02c7bb9c977ebea731b17ec2a7cf197e91be085dbce87392e53f548ac7c14e28876c9a271", + "hkdf": "65c9a327ea321c4e5beab11527862dee68b39349c909652d4e796f88ba5ede82011d6c17996c7f90c71ea1003244b0e845d02e0a852f5e08add5cea53ae21d7f" + }, + { + "digest": "blake2s256", + "hash": "30d8777f0e178582ec8cd2fcdc18af57c828ee2f89e978df52c8e7af078bd5cf", + "signature": null, + "pkdf2": "242e2926b524dd35a4684b4300a6e0e219b58ad1513847a3ac731f08696b5491d6b3a14146864fcc1d292c85908fe364267670c9a680bbe8117bf3b2519efee9", + "hkdf": "f17e4306bf70cbd74e7d16595eab65178a7d6545ff3a8d78800ea69697e6c360f24bafe2772ff80fb1a4381bb69c3f98c39033f246f86521609a4dda95b7a1d7" + }, + { + "digest": "id-rsassa-pkcs1-v1_5-with-sha3-224", + "hash": "6a33e22f20f16642697e8bd549ff7b759252ad56c05a1b0acc31dc69", + "signature": "2504c7a4735c251aa01644ae56a9f7c891f45e9fb6e25fea1c25074139dcab114e77d64ae7788bcbd8d5d452db60b47e6a0b324b0e3482ff794581da0cbe960a97ba75c1023eab2320fa9022ae715b5aa2fe1186916fb9a1c931d9f28ba8e12a4dd145efff6abfcfb68eca6536655c96cf059df406d5c51d1814b2f61c6bdc10a2b083d29f50a064bc6ffdddf5cb45df2577a7149a430e1314f87427972d8bb4775bd92e99a2298095cb83e60e50f503aab6d9fd41398b55d5dcfa6022049891561e6395a7a3e56a1d92a8667733a68d9b0374952d95e4d5a51b0b22d390cf579bb536e59cea8272b2d5749fa70d48c396b6ba94188ce677beb8f00937640096", + "pkdf2": "014d30aa9d1c1d8e3920f4bcc399f37b16d6c346d570d4345229d64ab95e504b1c6b253133c218a2b46f29aa0f9792d7ad60761159cc3550cb9a0914bfdc9951", + "hkdf": "b6c671ec02ee7f215dcee9ecade623831658470156f3edab205b7e3a70587f3c538eac8ec92e3a5bf6808b9b939d84e4f8ba5ee3532d7adb152211cc8f79cf9b" + }, + { + "digest": "id-rsassa-pkcs1-v1_5-with-sha3-256", + "hash": "f345a219da005ebe9c1a1eaad97bbf38a10c8473e41d0af7fb617caa0c6aa722", + "signature": "b3b475d03da73b4bf8fefa5f49231dfd8f95605e0c3ea058d4607f1274ddcf4f69b1751df8493df1c5fe31883aff197688de1991885bb9ed4266f7df0db4fc99b33bee32a9237a9c2714e461fefb311923c1e91fe7cbe1830206bb0b3d962bfccdf5c37e8ce9e01f203851e34f22162175f44048002483ff2e3ffc658506a23f358f3192dbba759a8e1f66d3abf934b72295f1b51f8a92c837c76006084b65c0df8e072e43b1122bb15b3043dceb026e2cf311d04d6f4a3506e7e5b8a08fcaa0caeaa18a6397d185e5b5d1bc962f22b80db798a42fa206cd2db53b02bc5e2e69d9566f1ff3fccb4690545ff19424001cf3c0504c53adc0f2ce4354596b8a4d69", + "pkdf2": "4ba48814e191c34c9d38f5ccf6c5cf1a61082c2f8b36e99bd8210781af995734f62c6a714ad6639ecb55224d91b2ac7d69030335d2c367d833823814ef9f2213", + "hkdf": "e60f1e8214b7907d63d8cfb2bfb083051d3c9a56e4f94aabc1ff3543986aac5d4823db903090af81824964d6ca39da8388495e8838a9c0c407cb3546f44f9a04" + }, + { + "digest": "id-rsassa-pkcs1-v1_5-with-sha3-384", + "hash": "6ba9ea268965916f5937228dde678c202f9fe756a87d8b1b7362869583a45901fd1a27289d72fc0e3ff48b1b78827d3a", + "signature": "256542ac61773cec2261fbc8cde4d6524e7a91f9fefcc08792c7c4b27fb843d099a5fce9819503949ad8fde307a7fc4185754dc5ed3d473da065376e4a9c804265dc4ff858a774d6eb19e663a26339d087a3e96b8f9133becc0ee2b404cfcfca58ffadbd48a5c31dced92f188b40bd179efdf9ae5394d0e02b513958f46adbdf745e8dada0dd97e38bf28bf7ad87aeb6771a40e3fef5f35161d4eeafe7b6ba1c2101388a22e75586fab599cbc070bf4d0fbe8304a7c579a34ce0000c87b33882c2d86ff11dbc218604fa7e7203e4efaa6e9605ae41958c0c2de0b7d76da9a6e606b3c38d29dad55675f9f138b11afc96e71ba5e41924bc5c676db777aed7ea04", + "pkdf2": "f609fda1c21cf4e59e7142438f2e04bb29f72010d49546e1c192755c6af3b97903da8483be8356afb865ffc334d23fa2439063c287ec9491618bfb5f791675f4", + "hkdf": "818ae5bc92cf582db87d73316e1a0649b457dfcd198db3e4edb1c6b20515dcf14f8758951765d9e2b4f7914a75ea04815e82050a6ca602c1a76f6c9999813955" + }, + { + "digest": "id-rsassa-pkcs1-v1_5-with-sha3-512", + "hash": "8e47f1185ffd014d238fabd02a1a32defe698cbf38c037a90e3c0a0a32370fb52cbd641250508502295fcabcbf676c09470b27443868c8e5f70e26dc337288af", + "signature": "925bd1da0c91caba58c0a407f4f63e9b16f52444c3b71476f4043ee00ef97bb428a396e8a45d75b3204142d2e18886f36b0b0a1c6d25d80b010e4063ffb310741907e3217badadaf53d018100d78eace2e941c0ec2b6f9b47ceee455d55bfb1711b1255ae1fa0d8fb5bf869009c1b558b02fd4cabd83c55a76a1d96bdb4cdbb42f6ad1f36984c39e6c6fe4edc3a771766742ec461d29e8a93704a48323c0c0f9cc066bf1daeb4e83aa3aa7a3c2067495cf320a9e98b7f1db638a23fc135992fcc3bbfb52be115c35b055cdbf4733c30098674ff19f1ef218a4643b0e2ecb7c0597a406b304b67a36eed0300313a65a4af5621ef59a5ffe53e7d6763a83e0014a", + "pkdf2": "e1f0b454bf5d729fbb13e534229521a87aee130078555791d83834a8c51fb681ce4dfe02afd5f063082d8abba0b456084c677aeb6f8e0d70305322ec2ea97203", + "hkdf": "f27d87f9f6b87718073c8d2ad6bae00b4162cecde350c856252dd611120c433373a0c0d3946a8582bf855bf581439a14ca4f355fcd18881331f4a3b1027e84b2" + }, + { + "digest": "md5", + "hash": "6cd3556deb0da54bca060b4c39479839", + "signature": "45f2e7ac7c0d323c18e9c8554d15e21a8fb72944928e0b7acc2185402448c39af05340391ee9aac64c45a947bc3ed801de8791aaa5788a62e909d7c8d92e63c7f119753219a48c29ea78e371d3a89c0bd64d756b829fa6faaa8fa46bc28228dd4430aca845c02f3b6b835b140247838ac5b96434d1049115785f3f52034e0cc46f6cafc5f2921b9928403aea1edd85f3a99e12324abeeaaaa040b320f8af157c5df7fd7479415771fd116daed933b43eac51b3c274ba9283aaf3f1b6f1f527a0cea76f9a43dca3b9ff03834feed13a0fd488e6750a9d46ddb1ad69ac88090af050458eeafd1e9fa317c4ecc61d90289d5378e6e3693ef6da2e4a4803b6108145", + "pkdf2": "3083f052ca2e90daf96a87b8b8c1daa069bff60c73045c9504d3b424807b5410a724ea5ba28b9bccb6bad59c0e181663b07156a59b09cee85ffe223f21ff6db6", + "hkdf": "f516674e1b1faf2c1a505041634460d94739e2e39be4e6a097f926f5cc9788c09892efcb407019dc469c4014c8d847cf4c5a6eeefccf8e5266ca3dd02ea57529" + }, + { + "digest": "md5-sha1", + "hash": "6cd3556deb0da54bca060b4c39479839943a702d06f34599aee1f8da8ef9f7296031d699", + "signature": "1b8bddcb86fca178c4318ff04be6956fc92815b54b38d7cd5c0c12be1b3c2b5defc960d9fc162d0f2e8eddeb075cb5b751c89d2e7550d50e1939f89c5c96bd7c4b8ad16fd02f5295f9806f275b0959f9fa226131c6996e64ce5f2595729036967762070033e40c3b7f63c8567158ce78143332a00669a0c4126b80c7ba9bdcf61e03eda99fddb9ea779a4038e87d6615d166e9b88272cb91de68126bbf505764feebd68b526358a0cf46526aea26566907763a5250f9169d1e6e674cc37fe5feb20f2a87b9251383829ad33d5bc1fbf78f87c625703b68a4f30e9a31b248a5d094d712cf5c8ca1394fe9a9d8800b5d90b0f51f4b2e547287239a210504807d8a", + "pkdf2": "4d8d010a2ab473080923a782f8d497d2be83c3dd78b3da755754ab098f4b9d629e583f343fabc938ab0cce8bdaa5ad4c515d10fc9c0ce416e5752a9060bb9831", + "hkdf": "9265d75fdd9ef3609109d7703c75773d02175d68376422f4078bede7e3ac4f024a6b0e28333e316c276d6bb6f1177f8300490bf78c98f20ba8e6d2f287515b56" + }, + { + "digest": "md5WithRSAEncryption", + "hash": "6cd3556deb0da54bca060b4c39479839", + "signature": "45f2e7ac7c0d323c18e9c8554d15e21a8fb72944928e0b7acc2185402448c39af05340391ee9aac64c45a947bc3ed801de8791aaa5788a62e909d7c8d92e63c7f119753219a48c29ea78e371d3a89c0bd64d756b829fa6faaa8fa46bc28228dd4430aca845c02f3b6b835b140247838ac5b96434d1049115785f3f52034e0cc46f6cafc5f2921b9928403aea1edd85f3a99e12324abeeaaaa040b320f8af157c5df7fd7479415771fd116daed933b43eac51b3c274ba9283aaf3f1b6f1f527a0cea76f9a43dca3b9ff03834feed13a0fd488e6750a9d46ddb1ad69ac88090af050458eeafd1e9fa317c4ecc61d90289d5378e6e3693ef6da2e4a4803b6108145", + "pkdf2": "3083f052ca2e90daf96a87b8b8c1daa069bff60c73045c9504d3b424807b5410a724ea5ba28b9bccb6bad59c0e181663b07156a59b09cee85ffe223f21ff6db6", + "hkdf": "f516674e1b1faf2c1a505041634460d94739e2e39be4e6a097f926f5cc9788c09892efcb407019dc469c4014c8d847cf4c5a6eeefccf8e5266ca3dd02ea57529" + }, + { + "digest": "ripemd", + "hash": "58262d1fbdbe4530d8865d3518c6d6e41002610f", + "signature": "31b25b680d0df8cb27df71c38fe7931a185c056b98c072a4f4956d6105a44ed53d7e457d366074cc26cd1cd0c226cf0cac509264f42651c208eb73f29f48dc2022009595e4ccf97662cf2840d69987f51118e9854fd10497a9e1dcb2ef390c9d09767f7d801aa2dd3d5c0e51249a195769593696725ba81e9a6c961a4b2c13827c467ccafa76f0ea5a9f43b936f3b35a8c316f116e0ac0d6f56ed8c0189d28ea1dc3a56b97a75c8f5c5cdc1682a76b5d852dd7282371ecb379abf0864eff0c437fca813d0ddc1ce34e8327a604336eeab6d907edf39e0303f0b8a45e0721e15d85c8c329047920b1ec8863bc6c0b172a57a6bb3611edb1f5c4d49aa1089cb5fd", + "pkdf2": "2473b58bc86761bd5e8d35153475d44e8ba2dd4a156d6e69d79cbc33244f35bfea27bb3122354d98dbd792c679d8994f10a8eaf7c946e6a65566413a13060386", + "hkdf": "c7d8a2fe1ae6d243aef0b989c2483221f0e9c3234719273438dca33b97269435047f441ae104903fafaa051267ab34ba469449511a8e40fc5444a01e03cfe651" + }, + { + "digest": "ripemd160", + "hash": "58262d1fbdbe4530d8865d3518c6d6e41002610f", + "signature": "31b25b680d0df8cb27df71c38fe7931a185c056b98c072a4f4956d6105a44ed53d7e457d366074cc26cd1cd0c226cf0cac509264f42651c208eb73f29f48dc2022009595e4ccf97662cf2840d69987f51118e9854fd10497a9e1dcb2ef390c9d09767f7d801aa2dd3d5c0e51249a195769593696725ba81e9a6c961a4b2c13827c467ccafa76f0ea5a9f43b936f3b35a8c316f116e0ac0d6f56ed8c0189d28ea1dc3a56b97a75c8f5c5cdc1682a76b5d852dd7282371ecb379abf0864eff0c437fca813d0ddc1ce34e8327a604336eeab6d907edf39e0303f0b8a45e0721e15d85c8c329047920b1ec8863bc6c0b172a57a6bb3611edb1f5c4d49aa1089cb5fd", + "pkdf2": "2473b58bc86761bd5e8d35153475d44e8ba2dd4a156d6e69d79cbc33244f35bfea27bb3122354d98dbd792c679d8994f10a8eaf7c946e6a65566413a13060386", + "hkdf": "c7d8a2fe1ae6d243aef0b989c2483221f0e9c3234719273438dca33b97269435047f441ae104903fafaa051267ab34ba469449511a8e40fc5444a01e03cfe651" + }, + { + "digest": "ripemd160WithRSA", + "hash": "58262d1fbdbe4530d8865d3518c6d6e41002610f", + "signature": "31b25b680d0df8cb27df71c38fe7931a185c056b98c072a4f4956d6105a44ed53d7e457d366074cc26cd1cd0c226cf0cac509264f42651c208eb73f29f48dc2022009595e4ccf97662cf2840d69987f51118e9854fd10497a9e1dcb2ef390c9d09767f7d801aa2dd3d5c0e51249a195769593696725ba81e9a6c961a4b2c13827c467ccafa76f0ea5a9f43b936f3b35a8c316f116e0ac0d6f56ed8c0189d28ea1dc3a56b97a75c8f5c5cdc1682a76b5d852dd7282371ecb379abf0864eff0c437fca813d0ddc1ce34e8327a604336eeab6d907edf39e0303f0b8a45e0721e15d85c8c329047920b1ec8863bc6c0b172a57a6bb3611edb1f5c4d49aa1089cb5fd", + "pkdf2": "2473b58bc86761bd5e8d35153475d44e8ba2dd4a156d6e69d79cbc33244f35bfea27bb3122354d98dbd792c679d8994f10a8eaf7c946e6a65566413a13060386", + "hkdf": "c7d8a2fe1ae6d243aef0b989c2483221f0e9c3234719273438dca33b97269435047f441ae104903fafaa051267ab34ba469449511a8e40fc5444a01e03cfe651" + }, + { + "digest": "rmd160", + "hash": "58262d1fbdbe4530d8865d3518c6d6e41002610f", + "signature": "31b25b680d0df8cb27df71c38fe7931a185c056b98c072a4f4956d6105a44ed53d7e457d366074cc26cd1cd0c226cf0cac509264f42651c208eb73f29f48dc2022009595e4ccf97662cf2840d69987f51118e9854fd10497a9e1dcb2ef390c9d09767f7d801aa2dd3d5c0e51249a195769593696725ba81e9a6c961a4b2c13827c467ccafa76f0ea5a9f43b936f3b35a8c316f116e0ac0d6f56ed8c0189d28ea1dc3a56b97a75c8f5c5cdc1682a76b5d852dd7282371ecb379abf0864eff0c437fca813d0ddc1ce34e8327a604336eeab6d907edf39e0303f0b8a45e0721e15d85c8c329047920b1ec8863bc6c0b172a57a6bb3611edb1f5c4d49aa1089cb5fd", + "pkdf2": "2473b58bc86761bd5e8d35153475d44e8ba2dd4a156d6e69d79cbc33244f35bfea27bb3122354d98dbd792c679d8994f10a8eaf7c946e6a65566413a13060386", + "hkdf": "c7d8a2fe1ae6d243aef0b989c2483221f0e9c3234719273438dca33b97269435047f441ae104903fafaa051267ab34ba469449511a8e40fc5444a01e03cfe651" + }, + { + "digest": "sha1", + "hash": "943a702d06f34599aee1f8da8ef9f7296031d699", + "signature": "80174b0678707d38335940a010bd6acc1a6ade2c911251d013875816eb4c5169d9d4cb2ac9f6ce4b29b92f5750f185a273abd7c8a2434a9d1a50df5311999655d2a535c77b79997ed34fe51a863c450251d1b85bbb4dd10da1162e6e74194af606579a8724e55b93bded2d10216bb63cf00e848ceea6cdba78eab4f735ca620b36fdfd19bf15706df860a4c589160ce3c32aa51c27561de82a00cdcca709045cb8622adb6f759eb5e0f2f3f6056ca1b1a91206712ce92f5684d64931bc001bec7d2e8d2469f0818106070698160e9e21404971ea45940797fa3a61a39a25b7c6c75986accd4832db50a25ef3fceca629e104e2f03affd83b94cf074614e11c18", + "pkdf2": "e77df42e685bd1fe2e19e374e1dd8470df71182311e048a6a280f36ef24c661e46fe8e8cc763942615934a631ac3aeda24bb9fda71e04f89f0c85f2f762abd98", + "hkdf": "936c59e9394b5b1fdc26bd2eec0dc8c8d52fb97198e021805f0ae58c3f8b631d0020002b1ce0df9d890377fcc808166c906f128d06d77d404061d56560f3340d" + }, + { + "digest": "sha1WithRSAEncryption", + "hash": "943a702d06f34599aee1f8da8ef9f7296031d699", + "signature": "80174b0678707d38335940a010bd6acc1a6ade2c911251d013875816eb4c5169d9d4cb2ac9f6ce4b29b92f5750f185a273abd7c8a2434a9d1a50df5311999655d2a535c77b79997ed34fe51a863c450251d1b85bbb4dd10da1162e6e74194af606579a8724e55b93bded2d10216bb63cf00e848ceea6cdba78eab4f735ca620b36fdfd19bf15706df860a4c589160ce3c32aa51c27561de82a00cdcca709045cb8622adb6f759eb5e0f2f3f6056ca1b1a91206712ce92f5684d64931bc001bec7d2e8d2469f0818106070698160e9e21404971ea45940797fa3a61a39a25b7c6c75986accd4832db50a25ef3fceca629e104e2f03affd83b94cf074614e11c18", + "pkdf2": "e77df42e685bd1fe2e19e374e1dd8470df71182311e048a6a280f36ef24c661e46fe8e8cc763942615934a631ac3aeda24bb9fda71e04f89f0c85f2f762abd98", + "hkdf": "936c59e9394b5b1fdc26bd2eec0dc8c8d52fb97198e021805f0ae58c3f8b631d0020002b1ce0df9d890377fcc808166c906f128d06d77d404061d56560f3340d" + }, + { + "digest": "sha224", + "hash": "8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568", + "signature": "70a8b455d63d2af5b12b0eefcb50ae245dcac054a74fd8a5beccca5ed12fd545e4dcdf2da3455f49e5eaf559772dc885ae24fd9e42790a1e175ec221a22329f2cc67d5c8ad1f9377fa1d713cb0d554f8335325a6080c272c10fb59f5fb7da9b39dcc76a50b49ed5f28d8cdd0499fbf5a19cae4b5b4a4a103201933a0e94e5532c6aaf05fbb060a3be5dcd5addef23b80651c51831ee479142cb1ddac0a37c6251f6eceee55217fcced2462fb7bef3e3aeeda98672598dc6aca6a49fb2df1b3f0ac569b666afeb8ca3f5b1bfa0bcca49f5fe6d3784fba1bb931077633d5a0a731c2bb22a820c8497ac1db888bd542f73ba2358d3a9bc056a7eca2f3a78b437820", + "pkdf2": "d3adee1df932bac56eecdba710702025678d6e024d315c925ae274a3796c474219291983a76f2b8bd67aad26fbebcf5bb5088c99b6124ae1b9c7c9b2eb37c8f4", + "hkdf": "1c3a0bd11a8c71c010353b075474aa2a6740f5cade0fff895019e08ebdaa68f8dfc4e74a5e013e2e9e0ab6852884311c1dac89432d34b2f762113ebf9a37597a" + }, + { + "digest": "sha224WithRSAEncryption", + "hash": "8552d8b7a7dc5476cb9e25dee69a8091290764b7f2a64fe6e78e9568", + "signature": "70a8b455d63d2af5b12b0eefcb50ae245dcac054a74fd8a5beccca5ed12fd545e4dcdf2da3455f49e5eaf559772dc885ae24fd9e42790a1e175ec221a22329f2cc67d5c8ad1f9377fa1d713cb0d554f8335325a6080c272c10fb59f5fb7da9b39dcc76a50b49ed5f28d8cdd0499fbf5a19cae4b5b4a4a103201933a0e94e5532c6aaf05fbb060a3be5dcd5addef23b80651c51831ee479142cb1ddac0a37c6251f6eceee55217fcced2462fb7bef3e3aeeda98672598dc6aca6a49fb2df1b3f0ac569b666afeb8ca3f5b1bfa0bcca49f5fe6d3784fba1bb931077633d5a0a731c2bb22a820c8497ac1db888bd542f73ba2358d3a9bc056a7eca2f3a78b437820", + "pkdf2": "d3adee1df932bac56eecdba710702025678d6e024d315c925ae274a3796c474219291983a76f2b8bd67aad26fbebcf5bb5088c99b6124ae1b9c7c9b2eb37c8f4", + "hkdf": "1c3a0bd11a8c71c010353b075474aa2a6740f5cade0fff895019e08ebdaa68f8dfc4e74a5e013e2e9e0ab6852884311c1dac89432d34b2f762113ebf9a37597a" + }, + { + "digest": "sha256", + "hash": "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", + "signature": "67b67a87fc4a576342fc46167395a255654dddc5036e8a7d323acf862470e0612b90b1abf22166e3d124d4866e82146b5735fb85895355d027efdcb406a8cb87c8f00aea128e74cb95ca8a69cb970e3f02a11a6911253f9fba54ae7b75d7f1f29d51338d43f4ed59a36e3b7e408591cd9feaa818f1b0c6df6155cc89cc95c6686ce82a78fd8d34e82d9047ce936c3a67ac85d2924083610d6db5040409f09a46f97d35248b5451ca51be6cfbc9f94ff630eb8ff95e114a666cc1cc011af03d77f9b24f8add5244a83e0784dbcf785ebcfcd015b876963666b113dc26f0222aa95fdfbe1e64ac6b48780e1ff69b2588ff4bce9b15f9d53abba96b813a3d657cbe", + "pkdf2": "a8a2a7dcf7b65c8498ab1fbc62d56fa47255d668c1c3f21c5b236259d1dd8b11859026e737216cceb7a8facb8d27656f2b990db98786f12a8ce68e56a30f0c0b", + "hkdf": "a35a5ba99add2c508b9014ca7eb7d461a85989257b29a46a0041361c85b2c48838199873987b5419ede395aa09129bcb5fbd607b8b769e6711e1fea5e0ebcbc0" + }, + { + "digest": "sha256WithRSAEncryption", + "hash": "315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3", + "signature": "67b67a87fc4a576342fc46167395a255654dddc5036e8a7d323acf862470e0612b90b1abf22166e3d124d4866e82146b5735fb85895355d027efdcb406a8cb87c8f00aea128e74cb95ca8a69cb970e3f02a11a6911253f9fba54ae7b75d7f1f29d51338d43f4ed59a36e3b7e408591cd9feaa818f1b0c6df6155cc89cc95c6686ce82a78fd8d34e82d9047ce936c3a67ac85d2924083610d6db5040409f09a46f97d35248b5451ca51be6cfbc9f94ff630eb8ff95e114a666cc1cc011af03d77f9b24f8add5244a83e0784dbcf785ebcfcd015b876963666b113dc26f0222aa95fdfbe1e64ac6b48780e1ff69b2588ff4bce9b15f9d53abba96b813a3d657cbe", + "pkdf2": "a8a2a7dcf7b65c8498ab1fbc62d56fa47255d668c1c3f21c5b236259d1dd8b11859026e737216cceb7a8facb8d27656f2b990db98786f12a8ce68e56a30f0c0b", + "hkdf": "a35a5ba99add2c508b9014ca7eb7d461a85989257b29a46a0041361c85b2c48838199873987b5419ede395aa09129bcb5fbd607b8b769e6711e1fea5e0ebcbc0" + }, + { + "digest": "sha3-224", + "hash": "6a33e22f20f16642697e8bd549ff7b759252ad56c05a1b0acc31dc69", + "signature": "2504c7a4735c251aa01644ae56a9f7c891f45e9fb6e25fea1c25074139dcab114e77d64ae7788bcbd8d5d452db60b47e6a0b324b0e3482ff794581da0cbe960a97ba75c1023eab2320fa9022ae715b5aa2fe1186916fb9a1c931d9f28ba8e12a4dd145efff6abfcfb68eca6536655c96cf059df406d5c51d1814b2f61c6bdc10a2b083d29f50a064bc6ffdddf5cb45df2577a7149a430e1314f87427972d8bb4775bd92e99a2298095cb83e60e50f503aab6d9fd41398b55d5dcfa6022049891561e6395a7a3e56a1d92a8667733a68d9b0374952d95e4d5a51b0b22d390cf579bb536e59cea8272b2d5749fa70d48c396b6ba94188ce677beb8f00937640096", + "pkdf2": "014d30aa9d1c1d8e3920f4bcc399f37b16d6c346d570d4345229d64ab95e504b1c6b253133c218a2b46f29aa0f9792d7ad60761159cc3550cb9a0914bfdc9951", + "hkdf": "b6c671ec02ee7f215dcee9ecade623831658470156f3edab205b7e3a70587f3c538eac8ec92e3a5bf6808b9b939d84e4f8ba5ee3532d7adb152211cc8f79cf9b" + }, + { + "digest": "sha3-256", + "hash": "f345a219da005ebe9c1a1eaad97bbf38a10c8473e41d0af7fb617caa0c6aa722", + "signature": "b3b475d03da73b4bf8fefa5f49231dfd8f95605e0c3ea058d4607f1274ddcf4f69b1751df8493df1c5fe31883aff197688de1991885bb9ed4266f7df0db4fc99b33bee32a9237a9c2714e461fefb311923c1e91fe7cbe1830206bb0b3d962bfccdf5c37e8ce9e01f203851e34f22162175f44048002483ff2e3ffc658506a23f358f3192dbba759a8e1f66d3abf934b72295f1b51f8a92c837c76006084b65c0df8e072e43b1122bb15b3043dceb026e2cf311d04d6f4a3506e7e5b8a08fcaa0caeaa18a6397d185e5b5d1bc962f22b80db798a42fa206cd2db53b02bc5e2e69d9566f1ff3fccb4690545ff19424001cf3c0504c53adc0f2ce4354596b8a4d69", + "pkdf2": "4ba48814e191c34c9d38f5ccf6c5cf1a61082c2f8b36e99bd8210781af995734f62c6a714ad6639ecb55224d91b2ac7d69030335d2c367d833823814ef9f2213", + "hkdf": "e60f1e8214b7907d63d8cfb2bfb083051d3c9a56e4f94aabc1ff3543986aac5d4823db903090af81824964d6ca39da8388495e8838a9c0c407cb3546f44f9a04" + }, + { + "digest": "sha3-384", + "hash": "6ba9ea268965916f5937228dde678c202f9fe756a87d8b1b7362869583a45901fd1a27289d72fc0e3ff48b1b78827d3a", + "signature": "256542ac61773cec2261fbc8cde4d6524e7a91f9fefcc08792c7c4b27fb843d099a5fce9819503949ad8fde307a7fc4185754dc5ed3d473da065376e4a9c804265dc4ff858a774d6eb19e663a26339d087a3e96b8f9133becc0ee2b404cfcfca58ffadbd48a5c31dced92f188b40bd179efdf9ae5394d0e02b513958f46adbdf745e8dada0dd97e38bf28bf7ad87aeb6771a40e3fef5f35161d4eeafe7b6ba1c2101388a22e75586fab599cbc070bf4d0fbe8304a7c579a34ce0000c87b33882c2d86ff11dbc218604fa7e7203e4efaa6e9605ae41958c0c2de0b7d76da9a6e606b3c38d29dad55675f9f138b11afc96e71ba5e41924bc5c676db777aed7ea04", + "pkdf2": "f609fda1c21cf4e59e7142438f2e04bb29f72010d49546e1c192755c6af3b97903da8483be8356afb865ffc334d23fa2439063c287ec9491618bfb5f791675f4", + "hkdf": "818ae5bc92cf582db87d73316e1a0649b457dfcd198db3e4edb1c6b20515dcf14f8758951765d9e2b4f7914a75ea04815e82050a6ca602c1a76f6c9999813955" + }, + { + "digest": "sha3-512", + "hash": "8e47f1185ffd014d238fabd02a1a32defe698cbf38c037a90e3c0a0a32370fb52cbd641250508502295fcabcbf676c09470b27443868c8e5f70e26dc337288af", + "signature": "925bd1da0c91caba58c0a407f4f63e9b16f52444c3b71476f4043ee00ef97bb428a396e8a45d75b3204142d2e18886f36b0b0a1c6d25d80b010e4063ffb310741907e3217badadaf53d018100d78eace2e941c0ec2b6f9b47ceee455d55bfb1711b1255ae1fa0d8fb5bf869009c1b558b02fd4cabd83c55a76a1d96bdb4cdbb42f6ad1f36984c39e6c6fe4edc3a771766742ec461d29e8a93704a48323c0c0f9cc066bf1daeb4e83aa3aa7a3c2067495cf320a9e98b7f1db638a23fc135992fcc3bbfb52be115c35b055cdbf4733c30098674ff19f1ef218a4643b0e2ecb7c0597a406b304b67a36eed0300313a65a4af5621ef59a5ffe53e7d6763a83e0014a", + "pkdf2": "e1f0b454bf5d729fbb13e534229521a87aee130078555791d83834a8c51fb681ce4dfe02afd5f063082d8abba0b456084c677aeb6f8e0d70305322ec2ea97203", + "hkdf": "f27d87f9f6b87718073c8d2ad6bae00b4162cecde350c856252dd611120c433373a0c0d3946a8582bf855bf581439a14ca4f355fcd18881331f4a3b1027e84b2" + }, + { + "digest": "sha384", + "hash": "55bc556b0d2fe0fce582ba5fe07baafff035653638c7ac0d5494c2a64c0bea1cc57331c7c12a45cdbca7f4c34a089eeb", + "signature": "77d465006b8843cf30c5e93b7d3f63b0daf2df1d693c4e1c0f9c759ec8410c088f331c5bf89bca7218edd1bdf3f2d948c9be9656f44a630748395429a09289acb05be80b0d000bacd7826c33a584dcbcf658839015d07561668e882bad4505c475a04766a7eabb35b15b8b54d6fcf9ebdd25b699f0d88906ba2999de1e8e59cc75969bc819fd514b1002a6667a03c861fafee03cccb9fd926352139dc1a916ba56b36e9e69fd328cb2403b9f747350721261158021df9ab2a75a7c5a3e39b42f3722ea1997a039ecc033f64366974336dbfd3294820400fe623d2993593c51f4398290a51bf05dc452c5991e26e8971be759472635e86b08230d04d05bce88f4", + "pkdf2": "b2e2b9e03d9e6f5218b74be7002c8704558feae855272ab3a973ee7deb387c26483f87fb6463bc5768406b398896a29ac2d17bd45afccf369a403ffa396bb455", + "hkdf": "54aef7f7256a147e52d67888a4543c6084b4ac46cd7bf919576bf8c4e9dad6b8a1fc9ad9c6f90f2af8e76d8d815ae2226d67a11ccb524a0c0be3cc858f4ddb10" + }, + { + "digest": "sha384WithRSAEncryption", + "hash": "55bc556b0d2fe0fce582ba5fe07baafff035653638c7ac0d5494c2a64c0bea1cc57331c7c12a45cdbca7f4c34a089eeb", + "signature": "77d465006b8843cf30c5e93b7d3f63b0daf2df1d693c4e1c0f9c759ec8410c088f331c5bf89bca7218edd1bdf3f2d948c9be9656f44a630748395429a09289acb05be80b0d000bacd7826c33a584dcbcf658839015d07561668e882bad4505c475a04766a7eabb35b15b8b54d6fcf9ebdd25b699f0d88906ba2999de1e8e59cc75969bc819fd514b1002a6667a03c861fafee03cccb9fd926352139dc1a916ba56b36e9e69fd328cb2403b9f747350721261158021df9ab2a75a7c5a3e39b42f3722ea1997a039ecc033f64366974336dbfd3294820400fe623d2993593c51f4398290a51bf05dc452c5991e26e8971be759472635e86b08230d04d05bce88f4", + "pkdf2": "b2e2b9e03d9e6f5218b74be7002c8704558feae855272ab3a973ee7deb387c26483f87fb6463bc5768406b398896a29ac2d17bd45afccf369a403ffa396bb455", + "hkdf": "54aef7f7256a147e52d67888a4543c6084b4ac46cd7bf919576bf8c4e9dad6b8a1fc9ad9c6f90f2af8e76d8d815ae2226d67a11ccb524a0c0be3cc858f4ddb10" + }, + { + "digest": "sha512", + "hash": "c1527cd893c124773d811911970c8fe6e857d6df5dc9226bd8a160614c0cd963a4ddea2b94bb7d36021ef9d865d5cea294a82dd49a0bb269f51f6e7a57f79421", + "signature": "50e2973977e1be1218134a1f50732a8f54c4b0dfb836be816c304edce9bef4b1be566a295b05c6c82de00eb915bc61ca4bab073a13061987ca19af280202a4c6c496cf6262655e6a6f1a53f923ca048b4bc21e31282edd239cec8840b2411c1aa2abe5b9bf7de5066f43f6e2e0a0691ac6f2fc71ed679ae644b6d799261ed694dc9e27c81142ffb88883440ee92a6086d7c3d62572eac13480034219a9edbda7e5556f335bbe1a37ef0df26ae3ffe99b968d40a93d9ca9a3c76025409b5da041242afa977a5a0438be946562a48a764dc6d4c924fec82590e01b807d410a432b95ee2e5173d245a565cdecdc321521d6f61e965628013181bc7a9462a450d895", + "pkdf2": "284f17da4e32b7b4780b31f94405e115ca7e6ae025b05d8398f421c3a3f36d903a1fae9471674342bec7d51c6f32996df0c843a20105c8451e9cd17ecbddb3ed", + "hkdf": "cf181757f66abf44c88cd3d4ad0ed99b82cf47a8a6f870ff1561a12da7af03251590250af7473a1a3c5c5e794f29c44e8f25f69fb7985258161e74b433510262" + }, + { + "digest": "sha512-224", + "hash": "32620068b859669b45b31008e08b7384649ad2ca3f5163a3a71e5745", + "signature": "b5d1ec54de69d29dbc77595fec62bb16019b7cc275ead04338c17ee4b759fcb8443477e1708d45374b786acfeb616c1078d199b2a28d76f46d4b74a498c2bfb2b174632da895cae5558f36101f0596adb40152ac09b49f1726f293237bcf604ab7bf461c7e2dd78c909d7503ec4132722551842f7e8ef54317f4b58784c447fd68067c689c288c8401dc95276356e1396a127eac82a0bd0753cc4c00718e7240d523af95a1cd960885da64172e3325a225557f96e6307340dd45a499ac20af01a2e3e7a4be706a40249c6ce7e24dabdcbd577744c34cf8e182d9a7923c52cd508a0e39125003a0c74ac714f5c892187c6fc7417d552127b3c24c57e484212ab6", + "pkdf2": "69e623258a79c04b84aabc9333b17fc8a5ee8d7ebd06dd80236865584ccc98fce3180319f74748f6a6c2b44833425a6ab52e8ab22db62365799f14727102485d", + "hkdf": "c155ac0b2833f309322f258625df2c9926570fabbe66eff9713b977291054b57b8cd3280079613d8167a99f259087422cff9756780148bf683c1d5c03e015907" + }, + { + "digest": "sha512-224WithRSAEncryption", + "hash": "32620068b859669b45b31008e08b7384649ad2ca3f5163a3a71e5745", + "signature": "b5d1ec54de69d29dbc77595fec62bb16019b7cc275ead04338c17ee4b759fcb8443477e1708d45374b786acfeb616c1078d199b2a28d76f46d4b74a498c2bfb2b174632da895cae5558f36101f0596adb40152ac09b49f1726f293237bcf604ab7bf461c7e2dd78c909d7503ec4132722551842f7e8ef54317f4b58784c447fd68067c689c288c8401dc95276356e1396a127eac82a0bd0753cc4c00718e7240d523af95a1cd960885da64172e3325a225557f96e6307340dd45a499ac20af01a2e3e7a4be706a40249c6ce7e24dabdcbd577744c34cf8e182d9a7923c52cd508a0e39125003a0c74ac714f5c892187c6fc7417d552127b3c24c57e484212ab6", + "pkdf2": "69e623258a79c04b84aabc9333b17fc8a5ee8d7ebd06dd80236865584ccc98fce3180319f74748f6a6c2b44833425a6ab52e8ab22db62365799f14727102485d", + "hkdf": "c155ac0b2833f309322f258625df2c9926570fabbe66eff9713b977291054b57b8cd3280079613d8167a99f259087422cff9756780148bf683c1d5c03e015907" + }, + { + "digest": "sha512-256", + "hash": "330c723f25267587db0b9f493463e017011239169cb57a6db216c63774367115", + "signature": "6313742057ecbf4f54476799b018d1e18c2db920ef23386208f1e23e5af30d38edc0d11f1220e2cc0f898cd3e4cd9d4920fed30ff9f725404d4750f230fe9dafdfde95280a8713043359a09d45fbda484c469e61ae4fd615d616e76f6daa27acd3542d823e88f2dfd943b39f38884f9ed4695b8f004b4c67777de63474fe9dbbfeab966113907663f14c7822658aab70dceb7d1a6ed9842085762ab100ee915941295a045b29ef09af19eab58c79d3e5ffcbda61e2be35770c1d3b55156357c3cca806309b1ddc7e52f47e245d1d49237f0aa7156e89c76a17780a62fa552eeb7b623410e387d0a9412d4688725ed8df0a86e8b548ea6bb785ea48b6c0e39332", + "pkdf2": "c3d18f17c825a1630bc10695bfd4faa0309e862d010c0b74fb41b6ce5e46853cc435560a1b9cd940254103068aebc1f24d2580385b1890e7f10356ef4c8f507d", + "hkdf": "8451f1238da8577794ecc9b75832cabff44f279e5f8258cbf553c21f9735c19c7974e9a74879b4c59386116bb1a4bb50f149c9a4146b10de312fa679c1343b80" + }, + { + "digest": "sha512-256WithRSAEncryption", + "hash": "330c723f25267587db0b9f493463e017011239169cb57a6db216c63774367115", + "signature": "6313742057ecbf4f54476799b018d1e18c2db920ef23386208f1e23e5af30d38edc0d11f1220e2cc0f898cd3e4cd9d4920fed30ff9f725404d4750f230fe9dafdfde95280a8713043359a09d45fbda484c469e61ae4fd615d616e76f6daa27acd3542d823e88f2dfd943b39f38884f9ed4695b8f004b4c67777de63474fe9dbbfeab966113907663f14c7822658aab70dceb7d1a6ed9842085762ab100ee915941295a045b29ef09af19eab58c79d3e5ffcbda61e2be35770c1d3b55156357c3cca806309b1ddc7e52f47e245d1d49237f0aa7156e89c76a17780a62fa552eeb7b623410e387d0a9412d4688725ed8df0a86e8b548ea6bb785ea48b6c0e39332", + "pkdf2": "c3d18f17c825a1630bc10695bfd4faa0309e862d010c0b74fb41b6ce5e46853cc435560a1b9cd940254103068aebc1f24d2580385b1890e7f10356ef4c8f507d", + "hkdf": "8451f1238da8577794ecc9b75832cabff44f279e5f8258cbf553c21f9735c19c7974e9a74879b4c59386116bb1a4bb50f149c9a4146b10de312fa679c1343b80" + }, + { + "digest": "sha512WithRSAEncryption", + "hash": "c1527cd893c124773d811911970c8fe6e857d6df5dc9226bd8a160614c0cd963a4ddea2b94bb7d36021ef9d865d5cea294a82dd49a0bb269f51f6e7a57f79421", + "signature": "50e2973977e1be1218134a1f50732a8f54c4b0dfb836be816c304edce9bef4b1be566a295b05c6c82de00eb915bc61ca4bab073a13061987ca19af280202a4c6c496cf6262655e6a6f1a53f923ca048b4bc21e31282edd239cec8840b2411c1aa2abe5b9bf7de5066f43f6e2e0a0691ac6f2fc71ed679ae644b6d799261ed694dc9e27c81142ffb88883440ee92a6086d7c3d62572eac13480034219a9edbda7e5556f335bbe1a37ef0df26ae3ffe99b968d40a93d9ca9a3c76025409b5da041242afa977a5a0438be946562a48a764dc6d4c924fec82590e01b807d410a432b95ee2e5173d245a565cdecdc321521d6f61e965628013181bc7a9462a450d895", + "pkdf2": "284f17da4e32b7b4780b31f94405e115ca7e6ae025b05d8398f421c3a3f36d903a1fae9471674342bec7d51c6f32996df0c843a20105c8451e9cd17ecbddb3ed", + "hkdf": "cf181757f66abf44c88cd3d4ad0ed99b82cf47a8a6f870ff1561a12da7af03251590250af7473a1a3c5c5e794f29c44e8f25f69fb7985258161e74b433510262" + }, + { + "digest": "shake128", + "hash": "b5ffd113fa127f4d9c7e483cb52264ed", + "signature": null, + "pkdf2": null, + "hkdf": null + }, + { + "digest": "shake256", + "hash": "cf68a0d388047ed588ad72d3808cf9a3243f04d4901748c705fbf3a27d955542", + "signature": null, + "pkdf2": null, + "hkdf": null + }, + { + "digest": "sm3", + "hash": "e3bca101b496880c3653dad85861d0e784b00a8c18f7574472d156060e9096bf", + "signature": null, + "pkdf2": "70b3d2ac3b82dd7b8c35db45f99f49e029be2b6f1ef9ce1d9137267b729c06f7099dc7f571f2b05dbe5172118a7ae787cd4836fbf9ddce9aacc933c7c4e5537b", + "hkdf": "88b773032a2f131b1aa4ebdb69fdde7c42281b7c689bfe1f5bd43c7e1a6f555f101657c3f83ccd5918b4c6a8df04d0b58ebf77ec2181f8c5e7cfecb6f13fba61" + }, + { + "digest": "sm3WithRSAEncryption", + "hash": "e3bca101b496880c3653dad85861d0e784b00a8c18f7574472d156060e9096bf", + "signature": null, + "pkdf2": "70b3d2ac3b82dd7b8c35db45f99f49e029be2b6f1ef9ce1d9137267b729c06f7099dc7f571f2b05dbe5172118a7ae787cd4836fbf9ddce9aacc933c7c4e5537b", + "hkdf": "88b773032a2f131b1aa4ebdb69fdde7c42281b7c689bfe1f5bd43c7e1a6f555f101657c3f83ccd5918b4c6a8df04d0b58ebf77ec2181f8c5e7cfecb6f13fba61" + }, + { + "digest": "ssl3-md5", + "hash": "6cd3556deb0da54bca060b4c39479839", + "signature": "45f2e7ac7c0d323c18e9c8554d15e21a8fb72944928e0b7acc2185402448c39af05340391ee9aac64c45a947bc3ed801de8791aaa5788a62e909d7c8d92e63c7f119753219a48c29ea78e371d3a89c0bd64d756b829fa6faaa8fa46bc28228dd4430aca845c02f3b6b835b140247838ac5b96434d1049115785f3f52034e0cc46f6cafc5f2921b9928403aea1edd85f3a99e12324abeeaaaa040b320f8af157c5df7fd7479415771fd116daed933b43eac51b3c274ba9283aaf3f1b6f1f527a0cea76f9a43dca3b9ff03834feed13a0fd488e6750a9d46ddb1ad69ac88090af050458eeafd1e9fa317c4ecc61d90289d5378e6e3693ef6da2e4a4803b6108145", + "pkdf2": "3083f052ca2e90daf96a87b8b8c1daa069bff60c73045c9504d3b424807b5410a724ea5ba28b9bccb6bad59c0e181663b07156a59b09cee85ffe223f21ff6db6", + "hkdf": "f516674e1b1faf2c1a505041634460d94739e2e39be4e6a097f926f5cc9788c09892efcb407019dc469c4014c8d847cf4c5a6eeefccf8e5266ca3dd02ea57529" + }, + { + "digest": "ssl3-sha1", + "hash": "943a702d06f34599aee1f8da8ef9f7296031d699", + "signature": "80174b0678707d38335940a010bd6acc1a6ade2c911251d013875816eb4c5169d9d4cb2ac9f6ce4b29b92f5750f185a273abd7c8a2434a9d1a50df5311999655d2a535c77b79997ed34fe51a863c450251d1b85bbb4dd10da1162e6e74194af606579a8724e55b93bded2d10216bb63cf00e848ceea6cdba78eab4f735ca620b36fdfd19bf15706df860a4c589160ce3c32aa51c27561de82a00cdcca709045cb8622adb6f759eb5e0f2f3f6056ca1b1a91206712ce92f5684d64931bc001bec7d2e8d2469f0818106070698160e9e21404971ea45940797fa3a61a39a25b7c6c75986accd4832db50a25ef3fceca629e104e2f03affd83b94cf074614e11c18", + "pkdf2": "e77df42e685bd1fe2e19e374e1dd8470df71182311e048a6a280f36ef24c661e46fe8e8cc763942615934a631ac3aeda24bb9fda71e04f89f0c85f2f762abd98", + "hkdf": "936c59e9394b5b1fdc26bd2eec0dc8c8d52fb97198e021805f0ae58c3f8b631d0020002b1ce0df9d890377fcc808166c906f128d06d77d404061d56560f3340d" + } +] From 233e5f6feae62e2ddc4d4046479759bd0a09b3aa Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Fri, 5 Jul 2024 05:17:53 -0700 Subject: [PATCH 4/8] chore: rename `__runtime_js_source` to `hmr` (#24442) Saves some keystrokes and easy to remember ``` cargo b --features hmr ``` --- cli/Cargo.toml | 2 +- cli/build.rs | 4 ++-- cli/js.rs | 6 +++--- runtime/Cargo.toml | 2 +- runtime/web_worker.rs | 7 +++++-- runtime/worker.rs | 7 +++++-- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 31232e093e..144de919de 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -44,7 +44,7 @@ default = ["upgrade", "__vendored_zlib_ng"] upgrade = [] # A dev feature to disable creations and loading of snapshots in favor of # loading JS sources at runtime. -__runtime_js_sources = ["deno_runtime/__runtime_js_sources"] +hmr = ["deno_runtime/hmr"] # Vendor zlib as zlib-ng __vendored_zlib_ng = ["flate2/zlib-ng-compat", "libz-sys/zlib-ng"] diff --git a/cli/build.rs b/cli/build.rs index eca254fa2a..4fe6fd1eab 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -325,7 +325,7 @@ mod ts { } } -#[cfg(not(feature = "__runtime_js_sources"))] +#[cfg(not(feature = "hmr"))] fn create_cli_snapshot(snapshot_path: PathBuf) { use deno_runtime::ops::bootstrap::SnapshotOptions; @@ -469,7 +469,7 @@ fn main() { let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin"); ts::create_compiler_snapshot(compiler_snapshot_path, &c); - #[cfg(not(feature = "__runtime_js_sources"))] + #[cfg(not(feature = "hmr"))] { let cli_snapshot_path = o.join("CLI_SNAPSHOT.bin"); create_cli_snapshot(cli_snapshot_path); diff --git a/cli/js.rs b/cli/js.rs index 85485689bc..2c93f004ca 100644 --- a/cli/js.rs +++ b/cli/js.rs @@ -2,17 +2,17 @@ use log::debug; -#[cfg(not(feature = "__runtime_js_sources"))] +#[cfg(not(feature = "hmr"))] static CLI_SNAPSHOT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin")); pub fn deno_isolate_init() -> Option<&'static [u8]> { debug!("Deno isolate init with snapshots."); - #[cfg(not(feature = "__runtime_js_sources"))] + #[cfg(not(feature = "hmr"))] { Some(CLI_SNAPSHOT) } - #[cfg(feature = "__runtime_js_sources")] + #[cfg(feature = "hmr")] { None } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index ddbd7729ce..b7dde51d87 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -22,7 +22,7 @@ include_js_files_for_snapshotting = [ ] # A dev feature to disable creations and loading of snapshots in favor of # loading JS sources at runtime. -__runtime_js_sources = ["include_js_files_for_snapshotting"] +hmr = ["include_js_files_for_snapshotting"] # Signal that only snapshotted JS sources should be used. This will # conditionally exclude the runtime source transpilation logic, and add an # assertion that a snapshot is provided. diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 07a955efb8..08de532167 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -518,8 +518,11 @@ impl WebWorker { ops::web_worker::deno_web_worker::init_ops_and_esm(), ]; - #[cfg(__runtime_js_sources)] - assert!(cfg!(not(feature = "only_snapshotted_js_sources")), "'__runtime_js_sources' is incompatible with 'only_snapshotted_js_sources'."); + #[cfg(hmr)] + assert!( + cfg!(not(feature = "only_snapshotted_js_sources")), + "'hmr' is incompatible with 'only_snapshotted_js_sources'." + ); for extension in &mut extensions { if options.startup_snapshot.is_some() { diff --git a/runtime/worker.rs b/runtime/worker.rs index 4e16afeeb1..9207de227a 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -454,8 +454,11 @@ impl MainWorker { ops::web_worker::deno_web_worker::init_ops_and_esm().disable(), ]; - #[cfg(__runtime_js_sources)] - assert!(cfg!(not(feature = "only_snapshotted_js_sources")), "'__runtime_js_sources' is incompatible with 'only_snapshotted_js_sources'."); + #[cfg(hmr)] + assert!( + cfg!(not(feature = "only_snapshotted_js_sources")), + "'hmr' is incompatible with 'only_snapshotted_js_sources'." + ); for extension in &mut extensions { if options.startup_snapshot.is_some() { From 28d2ff7bdc023a3b7aff47503aa03a8dd65fe87f Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Fri, 5 Jul 2024 16:27:06 +0200 Subject: [PATCH 5/8] chore: remove unused class private properties (#24441) Noticed that these private class properties are never used. Maybe a leftover from an earlier implementation. --- ext/node/polyfills/http.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index dde7c4e410..534bad9086 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -1650,9 +1650,6 @@ export function Server(opts, requestListener?: ServerHandler): ServerImpl { } export class ServerImpl extends EventEmitter { - #httpConnections: Set = new Set(); - #listener?: Deno.Listener; - #addr: Deno.NetAddr | null = null; #hasClosed = false; #server: Deno.HttpServer; From d4d3a3c54f5e26dec0cc79e273dc488f8a47f2b3 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:32:51 -0700 Subject: [PATCH 6/8] fix(node): Implement `fs.lchown` (and `process.getegid`) (#24418) Closes https://github.com/denoland/deno/issues/21260. Part of https://github.com/denoland/deno/issues/18218. Implements `node:fs.lchown`, and enables the node_compat test for it. The test uses `process.getegid`, which we didn't have implemented, so I went ahead and implemented that as well to get the test working. --- cli/standalone/file_system.rs | 20 ++++++ ext/fs/in_memory_fs.rs | 18 +++++ ext/fs/interface.rs | 13 ++++ ext/fs/std_fs.rs | 43 +++++++++++ ext/node/lib.rs | 4 ++ ext/node/ops/fs.rs | 41 +++++++++++ ext/node/ops/os/mod.rs | 19 +++++ ext/node/polyfills/_fs/_fs_lchown.ts | 61 ++++++++++++++++ ext/node/polyfills/fs.ts | 9 ++- ext/node/polyfills/process.ts | 10 ++- runtime/permissions/lib.rs | 4 +- tests/node_compat/config.jsonc | 1 + tests/node_compat/runner/TODO.md | 1 - .../test/parallel/test-fs-lchown.js | 71 +++++++++++++++++++ 14 files changed, 310 insertions(+), 5 deletions(-) create mode 100644 ext/node/polyfills/_fs/_fs_lchown.ts create mode 100644 tests/node_compat/test/parallel/test-fs-lchown.js diff --git a/cli/standalone/file_system.rs b/cli/standalone/file_system.rs index 13da0a729d..536b17f277 100644 --- a/cli/standalone/file_system.rs +++ b/cli/standalone/file_system.rs @@ -145,6 +145,26 @@ impl FileSystem for DenoCompileFileSystem { RealFs.chown_async(path, uid, gid).await } + fn lchown_sync( + &self, + path: &Path, + uid: Option, + gid: Option, + ) -> FsResult<()> { + self.error_if_in_vfs(path)?; + RealFs.lchown_sync(path, uid, gid) + } + + async fn lchown_async( + &self, + path: PathBuf, + uid: Option, + gid: Option, + ) -> FsResult<()> { + self.error_if_in_vfs(&path)?; + RealFs.lchown_async(path, uid, gid).await + } + fn remove_sync(&self, path: &Path, recursive: bool) -> FsResult<()> { self.error_if_in_vfs(path)?; RealFs.remove_sync(path, recursive) diff --git a/ext/fs/in_memory_fs.rs b/ext/fs/in_memory_fs.rs index e2babf40aa..027539e849 100644 --- a/ext/fs/in_memory_fs.rs +++ b/ext/fs/in_memory_fs.rs @@ -178,6 +178,24 @@ impl FileSystem for InMemoryFs { self.chown_sync(&path, uid, gid) } + fn lchown_sync( + &self, + _path: &Path, + _uid: Option, + _gid: Option, + ) -> FsResult<()> { + Err(FsError::NotSupported) + } + + async fn lchown_async( + &self, + path: PathBuf, + uid: Option, + gid: Option, + ) -> FsResult<()> { + self.lchown_sync(&path, uid, gid) + } + fn remove_sync(&self, _path: &Path, _recursive: bool) -> FsResult<()> { Err(FsError::NotSupported) } diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index cb6fc4f639..f639a700bf 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -146,6 +146,19 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync { gid: Option, ) -> FsResult<()>; + fn lchown_sync( + &self, + path: &Path, + uid: Option, + gid: Option, + ) -> FsResult<()>; + async fn lchown_async( + &self, + path: PathBuf, + uid: Option, + gid: Option, + ) -> FsResult<()>; + fn remove_sync(&self, path: &Path, recursive: bool) -> FsResult<()>; async fn remove_async(&self, path: PathBuf, recursive: bool) -> FsResult<()>; diff --git a/ext/fs/std_fs.rs b/ext/fs/std_fs.rs index c501b8928e..7903700c3b 100644 --- a/ext/fs/std_fs.rs +++ b/ext/fs/std_fs.rs @@ -303,6 +303,24 @@ impl FileSystem for RealFs { .await? } + fn lchown_sync( + &self, + path: &Path, + uid: Option, + gid: Option, + ) -> FsResult<()> { + lchown(path, uid, gid) + } + + async fn lchown_async( + &self, + path: PathBuf, + uid: Option, + gid: Option, + ) -> FsResult<()> { + spawn_blocking(move || lchown(&path, uid, gid)).await? + } + fn write_file_sync( &self, path: &Path, @@ -431,6 +449,31 @@ fn chown(_path: &Path, _uid: Option, _gid: Option) -> FsResult<()> { Err(FsError::NotSupported) } +#[cfg(unix)] +fn lchown(path: &Path, uid: Option, gid: Option) -> FsResult<()> { + use std::os::unix::ffi::OsStrExt; + let c_path = std::ffi::CString::new(path.as_os_str().as_bytes()).unwrap(); + // -1 = leave unchanged + let uid = uid + .map(|uid| uid as libc::uid_t) + .unwrap_or(-1i32 as libc::uid_t); + let gid = gid + .map(|gid| gid as libc::gid_t) + .unwrap_or(-1i32 as libc::gid_t); + // SAFETY: `c_path` is a valid C string and lives throughout this function call. + let result = unsafe { libc::lchown(c_path.as_ptr(), uid, gid) }; + if result != 0 { + return Err(io::Error::last_os_error().into()); + } + Ok(()) +} + +// TODO: implement lchown for Windows +#[cfg(not(unix))] +fn lchown(_path: &Path, _uid: Option, _gid: Option) -> FsResult<()> { + Err(FsError::NotSupported) +} + fn remove(path: &Path, recursive: bool) -> FsResult<()> { // TODO: this is racy. This should open fds, and then `unlink` those. let metadata = fs::symlink_metadata(path)?; diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 01c464df13..e864133464 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -315,6 +315,8 @@ deno_core::extension!(deno_node, ops::fs::op_node_fs_exists_sync

, ops::fs::op_node_cp_sync

, ops::fs::op_node_cp

, + ops::fs::op_node_lchown_sync

, + ops::fs::op_node_lchown

, ops::fs::op_node_lutimes_sync

, ops::fs::op_node_lutimes

, ops::fs::op_node_statfs

, @@ -365,6 +367,7 @@ deno_core::extension!(deno_node, ops::os::op_node_os_set_priority

, ops::os::op_node_os_username

, ops::os::op_geteuid

, + ops::os::op_getegid

, ops::os::op_cpus

, ops::os::op_homedir

, op_node_build_os, @@ -426,6 +429,7 @@ deno_core::extension!(deno_node, "_fs/_fs_fsync.ts", "_fs/_fs_ftruncate.ts", "_fs/_fs_futimes.ts", + "_fs/_fs_lchown.ts", "_fs/_fs_link.ts", "_fs/_fs_lstat.ts", "_fs/_fs_lutimes.ts", diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 304a6c2539..47b66ee1d5 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -273,3 +273,44 @@ where Ok(()) } + +#[op2] +pub fn op_node_lchown_sync

( + state: &mut OpState, + #[string] path: String, + uid: Option, + gid: Option, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = PathBuf::from(path); + state + .borrow_mut::

() + .check_write_with_api_name(&path, Some("node:fs.lchownSync"))?; + let fs = state.borrow::(); + fs.lchown_sync(&path, uid, gid)?; + Ok(()) +} + +#[op2(async)] +pub async fn op_node_lchown

( + state: Rc>, + #[string] path: String, + uid: Option, + gid: Option, +) -> Result<(), AnyError> +where + P: NodePermissions + 'static, +{ + let path = PathBuf::from(path); + let fs = { + let mut state = state.borrow_mut(); + state + .borrow_mut::

() + .check_write_with_api_name(&path, Some("node:fs.lchown"))?; + state.borrow::().clone() + }; + fs.lchown_async(path, uid, gid).await?; + Ok(()) +} diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index 5b32113e5b..b7374dc322 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -75,6 +75,25 @@ where Ok(euid) } +#[op2(fast)] +pub fn op_getegid

(state: &mut OpState) -> Result +where + P: NodePermissions + 'static, +{ + { + let permissions = state.borrow_mut::

(); + permissions.check_sys("getegid", "node:os.getegid()")?; + } + + #[cfg(windows)] + let egid = 0; + #[cfg(unix)] + // SAFETY: Call to libc getegid. + let egid = unsafe { libc::getegid() }; + + Ok(egid) +} + #[op2] #[serde] pub fn op_cpus

(state: &mut OpState) -> Result, AnyError> diff --git a/ext/node/polyfills/_fs/_fs_lchown.ts b/ext/node/polyfills/_fs/_fs_lchown.ts new file mode 100644 index 0000000000..8611c8021d --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_lchown.ts @@ -0,0 +1,61 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// TODO(petamoriken): enable prefer-primordials for node polyfills +// deno-lint-ignore-file prefer-primordials + +import { + type CallbackWithError, + makeCallback, +} from "ext:deno_node/_fs/_fs_common.ts"; +import { + getValidatedPath, + kMaxUserId, +} from "ext:deno_node/internal/fs/utils.mjs"; +import * as pathModule from "node:path"; +import { validateInteger } from "ext:deno_node/internal/validators.mjs"; +import type { Buffer } from "node:buffer"; +import { promisify } from "ext:deno_node/internal/util.mjs"; +import { op_node_lchown, op_node_lchown_sync } from "ext:core/ops"; + +/** + * Asynchronously changes the owner and group + * of a file, without following symlinks. + */ +export function lchown( + path: string | Buffer | URL, + uid: number, + gid: number, + callback: CallbackWithError, +) { + callback = makeCallback(callback); + path = getValidatedPath(path).toString(); + validateInteger(uid, "uid", -1, kMaxUserId); + validateInteger(gid, "gid", -1, kMaxUserId); + + op_node_lchown(pathModule.toNamespacedPath(path), uid, gid).then( + () => callback(null), + callback, + ); +} + +export const lchownPromise = promisify(lchown) as ( + path: string | Buffer | URL, + uid: number, + gid: number, +) => Promise; + +/** + * Synchronously changes the owner and group + * of a file, without following symlinks. + */ +export function lchownSync( + path: string | Buffer | URL, + uid: number, + gid: number, +) { + path = getValidatedPath(path).toString(); + validateInteger(uid, "uid", -1, kMaxUserId); + validateInteger(gid, "gid", -1, kMaxUserId); + + op_node_lchown_sync(pathModule.toNamespacedPath(path), uid, gid); +} diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts index 6f0c53e4d2..7a3cf4e67f 100644 --- a/ext/node/polyfills/fs.ts +++ b/ext/node/polyfills/fs.ts @@ -27,6 +27,11 @@ import { fstat, fstatSync } from "ext:deno_node/_fs/_fs_fstat.ts"; import { fsync, fsyncSync } from "ext:deno_node/_fs/_fs_fsync.ts"; import { ftruncate, ftruncateSync } from "ext:deno_node/_fs/_fs_ftruncate.ts"; import { futimes, futimesSync } from "ext:deno_node/_fs/_fs_futimes.ts"; +import { + lchown, + lchownPromise, + lchownSync, +} from "ext:deno_node/_fs/_fs_lchown.ts"; import { link, linkPromise, linkSync } from "ext:deno_node/_fs/_fs_link.ts"; import { lstat, lstatPromise, lstatSync } from "ext:deno_node/_fs/_fs_lstat.ts"; import { @@ -173,7 +178,7 @@ const promises = { unlink: unlinkPromise, chmod: chmodPromise, // lchmod: promisify(lchmod), - // lchown: promisify(lchown), + lchown: lchownPromise, chown: chownPromise, utimes: utimesPromise, lutimes: lutimesPromise, @@ -218,6 +223,8 @@ export default { ftruncateSync, futimes, futimesSync, + lchown, + lchownSync, link, linkSync, lstat, diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 02837f8270..de48fea3e8 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -7,6 +7,7 @@ import { core, internals } from "ext:core/mod.js"; import { initializeDebugEnv } from "ext:deno_node/internal/util/debuglog.ts"; import { + op_getegid, op_geteuid, op_node_process_kill, op_process_abort, @@ -309,15 +310,16 @@ export function kill(pid: number, sig: string | number = "SIGTERM") { return true; } -let getgid, getuid, geteuid; +let getgid, getuid, getegid, geteuid; if (!isWindows) { getgid = () => Deno.gid(); getuid = () => Deno.uid(); + getegid = () => op_getegid(); geteuid = () => op_geteuid(); } -export { geteuid, getgid, getuid }; +export { getegid, geteuid, getgid, getuid }; const ALLOWED_FLAGS = buildAllowedFlags(); @@ -685,6 +687,9 @@ Process.prototype.getgid = getgid; /** This method is removed on Windows */ Process.prototype.getuid = getuid; +/** This method is removed on Windows */ +Process.prototype.getegid = getegid; + /** This method is removed on Windows */ Process.prototype.geteuid = geteuid; @@ -726,6 +731,7 @@ Process.prototype.noDeprecation = false; if (isWindows) { delete Process.prototype.getgid; delete Process.prototype.getuid; + delete Process.prototype.getegid; delete Process.prototype.geteuid; } diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 4579eba1a3..3b3f68a530 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -915,7 +915,9 @@ impl Descriptor for SysDescriptor { pub fn parse_sys_kind(kind: &str) -> Result<&str, AnyError> { match kind { "hostname" | "osRelease" | "osUptime" | "loadavg" | "networkInterfaces" - | "systemMemoryInfo" | "uid" | "gid" | "cpus" | "homedir" => Ok(kind), + | "systemMemoryInfo" | "uid" | "gid" | "cpus" | "homedir" | "getegid" => { + Ok(kind) + } _ => Err(type_error(format!("unknown system info kind \"{kind}\""))), } } diff --git a/tests/node_compat/config.jsonc b/tests/node_compat/config.jsonc index f0b667df44..0c8d380774 100644 --- a/tests/node_compat/config.jsonc +++ b/tests/node_compat/config.jsonc @@ -327,6 +327,7 @@ "test-fs-chown-type-check.js", "test-fs-copyfile.js", "test-fs-empty-readStream.js", + "test-fs-lchown.js", "test-fs-mkdir.js", "test-fs-open-flags.js", "test-fs-open-mode-mask.js", diff --git a/tests/node_compat/runner/TODO.md b/tests/node_compat/runner/TODO.md index ec2a2337e3..cab34f8645 100644 --- a/tests/node_compat/runner/TODO.md +++ b/tests/node_compat/runner/TODO.md @@ -794,7 +794,6 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co - [parallel/test-fs-fmap.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-fmap.js) - [parallel/test-fs-fsync.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-fsync.js) - [parallel/test-fs-lchmod.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-lchmod.js) -- [parallel/test-fs-lchown.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-lchown.js) - [parallel/test-fs-link.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-link.js) - [parallel/test-fs-long-path.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-long-path.js) - [parallel/test-fs-make-callback.js](https://github.com/nodejs/node/tree/v18.12.1/test/parallel/test-fs-make-callback.js) diff --git a/tests/node_compat/test/parallel/test-fs-lchown.js b/tests/node_compat/test/parallel/test-fs-lchown.js new file mode 100644 index 0000000000..ce3333745b --- /dev/null +++ b/tests/node_compat/test/parallel/test-fs-lchown.js @@ -0,0 +1,71 @@ +// deno-fmt-ignore-file +// deno-lint-ignore-file + +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Taken from Node 18.12.1 +// This file is automatically generated by `tests/node_compat/runner/setup.ts`. Do not modify this file manually. + +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const { promises } = fs; + +// Validate the path argument. +[false, 1, {}, [], null, undefined].forEach((i) => { + const err = { name: 'TypeError', code: 'ERR_INVALID_ARG_TYPE' }; + + assert.throws(() => fs.lchown(i, 1, 1, common.mustNotCall()), err); + assert.throws(() => fs.lchownSync(i, 1, 1), err); + promises.lchown(false, 1, 1) + .then(common.mustNotCall()) + .catch(common.expectsError(err)); +}); + +// Validate the uid and gid arguments. +[false, 'test', {}, [], null, undefined].forEach((i) => { + const err = { name: 'TypeError', code: 'ERR_INVALID_ARG_TYPE' }; + + assert.throws( + () => fs.lchown('not_a_file_that_exists', i, 1, common.mustNotCall()), + err + ); + assert.throws( + () => fs.lchown('not_a_file_that_exists', 1, i, common.mustNotCall()), + err + ); + assert.throws(() => fs.lchownSync('not_a_file_that_exists', i, 1), err); + assert.throws(() => fs.lchownSync('not_a_file_that_exists', 1, i), err); + + promises.lchown('not_a_file_that_exists', i, 1) + .then(common.mustNotCall()) + .catch(common.expectsError(err)); + + promises.lchown('not_a_file_that_exists', 1, i) + .then(common.mustNotCall()) + .catch(common.expectsError(err)); +}); + +// Validate the callback argument. +[false, 1, 'test', {}, [], null, undefined].forEach((i) => { + assert.throws(() => fs.lchown('not_a_file_that_exists', 1, 1, i), { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE' + }); +}); + +if (!common.isWindows) { + const testFile = tmpdir.resolve(path.basename(__filename)); + const uid = process.geteuid(); + const gid = process.getegid(); + + tmpdir.refresh(); + fs.copyFileSync(__filename, testFile); + fs.lchownSync(testFile, uid, gid); + fs.lchown(testFile, uid, gid, common.mustSucceed(async (err) => { + await promises.lchown(testFile, uid, gid); + })); +} From 80df9aec1db449e6cc0f4513103aa442b8d43de3 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 5 Jul 2024 17:53:09 -0400 Subject: [PATCH 7/8] refactor: move `FileCollector` to deno_config (#24433) --- Cargo.lock | 6 +- Cargo.toml | 2 +- cli/Cargo.toml | 3 +- cli/args/mod.rs | 68 ++-- cli/lsp/config.rs | 8 +- cli/lsp/language_server.rs | 5 + cli/tools/bench/mod.rs | 2 +- cli/tools/coverage/mod.rs | 4 +- cli/tools/fmt.rs | 4 +- cli/tools/lint/mod.rs | 4 +- cli/tools/registry/paths.rs | 12 +- cli/tools/test/mod.rs | 2 +- cli/util/fs.rs | 339 +----------------- cli/util/gitignore.rs | 178 --------- cli/util/mod.rs | 1 - cli/util/sync/atomic_flag.rs | 35 -- cli/util/sync/mod.rs | 4 +- ext/fs/interface.rs | 71 +++- .../workspaces/non_fatal_diagnostics/lint.out | 4 +- 19 files changed, 117 insertions(+), 635 deletions(-) delete mode 100644 cli/util/gitignore.rs delete mode 100644 cli/util/sync/atomic_flag.rs diff --git a/Cargo.lock b/Cargo.lock index 70a16de465..8daaa4551c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1148,7 +1148,6 @@ dependencies = [ "fs3", "glibc_version", "glob", - "ignore", "import_map", "indexmap", "jsonc-parser", @@ -1308,13 +1307,14 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.19.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc80f97cffe52c9a430201f288111fc89d33491b1675c0e01feb3a497ce76b3" +checksum = "64772162a8e8c1b3a9c48b4a0924e29f5b8f0ae23ea2027361937e96d04d493d" dependencies = [ "anyhow", "deno_semver", "glob", + "ignore", "import_map", "indexmap", "jsonc-parser", diff --git a/Cargo.toml b/Cargo.toml index 275aa653c2..2e5464718f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,7 +101,7 @@ console_static_text = "=0.8.1" data-encoding = "2.3.3" data-url = "=0.3.0" deno_cache_dir = "=0.10.0" -deno_config = { version = "=0.19.1", default-features = false } +deno_config = { version = "=0.20.0", default-features = false } dlopen2 = "0.6.1" ecb = "=0.1.2" elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem"] } diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 144de919de..0452ac0de9 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -107,7 +107,6 @@ faster-hex.workspace = true flate2.workspace = true fs3.workspace = true glob = "0.3.1" -ignore = "0.4" import_map = { version = "=0.20.0", features = ["ext"] } indexmap.workspace = true jsonc-parser.workspace = true @@ -149,7 +148,6 @@ tower-lsp.workspace = true twox-hash.workspace = true typed-arena = "=2.0.1" uuid = { workspace = true, features = ["serde"] } -walkdir = "=2.3.2" zeromq.workspace = true zstd.workspace = true @@ -164,6 +162,7 @@ nix.workspace = true deno_bench_util.workspace = true pretty_assertions.workspace = true test_util.workspace = true +walkdir = "=2.3.2" [package.metadata.winres] # This section defines the metadata that appears in the deno.exe PE header. diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 83f038ec02..a54003277a 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -10,9 +10,11 @@ mod package_json; use deno_ast::SourceMapOption; use deno_config::workspace::CreateResolverOptions; use deno_config::workspace::PackageJsonDepResolution; +use deno_config::workspace::VendorEnablement; use deno_config::workspace::Workspace; use deno_config::workspace::WorkspaceDiscoverOptions; use deno_config::workspace::WorkspaceDiscoverStart; +use deno_config::workspace::WorkspaceEmptyOptions; use deno_config::workspace::WorkspaceMemberContext; use deno_config::workspace::WorkspaceResolver; use deno_config::WorkspaceLintConfig; @@ -778,7 +780,6 @@ pub struct CliOptions { flags: Flags, initial_cwd: PathBuf, maybe_node_modules_folder: Option, - maybe_vendor_folder: Option, npmrc: Arc, maybe_lockfile: Option>, overrides: CliOptionOverrides, @@ -822,15 +823,6 @@ impl CliOptions { root_folder.pkg_json.as_deref(), ) .with_context(|| "Resolving node_modules folder.")?; - let maybe_vendor_folder = if force_global_cache { - None - } else { - resolve_vendor_folder( - &initial_cwd, - &flags, - root_folder.deno_json.as_deref(), - ) - }; if let Some(env_file_name) = &flags.env_file { match from_filename(env_file_name) { @@ -859,7 +851,6 @@ impl CliOptions { maybe_lockfile, npmrc, maybe_node_modules_folder, - maybe_vendor_folder, overrides: Default::default(), workspace, disable_deprecated_api_warning, @@ -871,6 +862,10 @@ impl CliOptions { let initial_cwd = std::env::current_dir().with_context(|| "Failed getting cwd.")?; let config_fs_adapter = DenoConfigFsAdapter::new(&RealFs); + let maybe_vendor_override = flags.vendor.map(|v| match v { + true => VendorEnablement::Enable { cwd: &initial_cwd }, + false => VendorEnablement::Disable, + }); let resolve_workspace_discover_options = || { let additional_config_file_names: &'static [&'static str] = if matches!(flags.subcommand, DenoSubcommand::Publish(..)) { @@ -899,8 +894,16 @@ impl CliOptions { config_parse_options, additional_config_file_names, discover_pkg_json, + maybe_vendor_override, } }; + let resolve_empty_options = || WorkspaceEmptyOptions { + root_dir: Arc::new( + ModuleSpecifier::from_directory_path(&initial_cwd).unwrap(), + ), + use_vendor_dir: maybe_vendor_override + .unwrap_or(VendorEnablement::Disable), + }; let workspace = match &flags.config_flag { deno_config::ConfigFlag::Discover => { @@ -910,9 +913,7 @@ impl CliOptions { &resolve_workspace_discover_options(), )? } else { - Workspace::empty(Arc::new( - ModuleSpecifier::from_directory_path(&initial_cwd).unwrap(), - )) + Workspace::empty(resolve_empty_options()) } } deno_config::ConfigFlag::Path(path) => { @@ -922,9 +923,9 @@ impl CliOptions { &resolve_workspace_discover_options(), )? } - deno_config::ConfigFlag::Disabled => Workspace::empty(Arc::new( - ModuleSpecifier::from_directory_path(&initial_cwd).unwrap(), - )), + deno_config::ConfigFlag::Disabled => { + Workspace::empty(resolve_empty_options()) + } }; for diagnostic in workspace.diagnostics() { @@ -1258,7 +1259,6 @@ impl CliOptions { flags: self.flags.clone(), initial_cwd: self.initial_cwd.clone(), maybe_node_modules_folder: Some(path), - maybe_vendor_folder: self.maybe_vendor_folder.clone(), npmrc: self.npmrc.clone(), maybe_lockfile: self.maybe_lockfile.clone(), workspace: self.workspace.clone(), @@ -1276,7 +1276,7 @@ impl CliOptions { } pub fn vendor_dir_path(&self) -> Option<&PathBuf> { - self.maybe_vendor_folder.as_ref() + self.workspace.vendor_dir_path() } pub fn resolve_root_cert_store_provider( @@ -1801,31 +1801,6 @@ fn resolve_node_modules_folder( Ok(Some(canonicalize_path_maybe_not_exists(&path)?)) } -fn resolve_vendor_folder( - cwd: &Path, - flags: &Flags, - maybe_config_file: Option<&ConfigFile>, -) -> Option { - let use_vendor_dir = flags - .vendor - .or_else(|| maybe_config_file.and_then(|c| c.json.vendor)) - .unwrap_or(false); - // Unlike the node_modules directory, there is no need to canonicalize - // this directory because it's just used as a cache and the resolved - // specifier is not based on the canonicalized path (unlike the modules - // in the node_modules folder). - if !use_vendor_dir { - None - } else if let Some(config_path) = maybe_config_file - .as_ref() - .and_then(|c| c.specifier.to_file_path().ok()) - { - Some(config_path.parent().unwrap().join("vendor")) - } else { - Some(cwd.join("vendor")) - } -} - fn resolve_import_map_specifier( maybe_import_map_path: Option<&str>, maybe_config_file: Option<&ConfigFile>, @@ -1962,9 +1937,8 @@ pub fn config_to_deno_graph_workspace_member( #[cfg(test)] mod test { - use crate::util::fs::FileCollector; - use super::*; + use deno_config::glob::FileCollector; use pretty_assertions::assert_eq; #[test] @@ -2109,7 +2083,7 @@ mod test { let mut files = FileCollector::new(|_| true) .ignore_git_folder() .ignore_node_modules() - .collect_file_patterns(resolved_files) + .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, resolved_files) .unwrap(); files.sort(); diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 4b96511c03..3c360b683b 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1299,7 +1299,13 @@ impl ConfigData { } }; - let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path()); + let vendor_dir = config_file.as_ref().and_then(|c| { + if c.vendor() == Some(true) { + Some(c.specifier.to_file_path().ok()?.parent()?.join("vendor")) + } else { + None + } + }); // Load lockfile let lockfile = config_file.as_ref().and_then(resolve_lockfile_from_config); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index cfc58439d5..b3deef35bb 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -3568,6 +3568,11 @@ impl Inner { }, additional_config_file_names: &[], discover_pkg_json: true, + maybe_vendor_override: if force_global_cache { + Some(deno_config::workspace::VendorEnablement::Disable) + } else { + None + }, }, )?); let cli_options = CliOptions::new( diff --git a/cli/tools/bench/mod.rs b/cli/tools/bench/mod.rs index d801b908cd..5bbf5ce8d9 100644 --- a/cli/tools/bench/mod.rs +++ b/cli/tools/bench/mod.rs @@ -13,12 +13,12 @@ use crate::tools::test::format_test_error; use crate::tools::test::TestFilter; use crate::util::file_watcher; use crate::util::fs::collect_specifiers; -use crate::util::fs::WalkEntry; use crate::util::path::is_script_ext; use crate::util::path::matches_pattern_or_exact_path; use crate::version::get_user_agent; use crate::worker::CliMainWorkerFactory; +use deno_config::glob::WalkEntry; use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::error::JsError; diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index 6175bd9646..c9eda3c195 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -9,11 +9,11 @@ use crate::factory::CliFactory; use crate::npm::CliNpmResolver; use crate::tools::fmt::format_json; use crate::tools::test::is_supported_test_path; -use crate::util::fs::FileCollector; use crate::util::text_encoding::source_map_from_code; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_config::glob::PathOrPattern; use deno_config::glob::PathOrPatternSet; @@ -408,7 +408,7 @@ fn collect_coverages( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(file_patterns)?; + .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, file_patterns)?; let coverage_patterns = FilePatterns { base: initial_cwd.to_path_buf(), diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index c16be9fb2d..9a21b4c105 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -19,10 +19,10 @@ use crate::factory::CliFactory; use crate::util::diff::diff; use crate::util::file_watcher; use crate::util::fs::canonicalize_path; -use crate::util::fs::FileCollector; use crate::util::path::get_extension; use async_trait::async_trait; use deno_ast::ParsedSource; +use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_core::anyhow::anyhow; use deno_core::anyhow::bail; @@ -200,7 +200,7 @@ fn collect_fmt_files( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(files) + .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files) } /// Formats markdown (using ) and its code blocks diff --git a/cli/tools/lint/mod.rs b/cli/tools/lint/mod.rs index e3f2844a7b..606d5835cb 100644 --- a/cli/tools/lint/mod.rs +++ b/cli/tools/lint/mod.rs @@ -8,6 +8,7 @@ use deno_ast::ModuleSpecifier; use deno_ast::ParsedSource; use deno_ast::SourceRange; use deno_ast::SourceTextInfo; +use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_config::workspace::Workspace; use deno_config::workspace::WorkspaceMemberContext; @@ -60,7 +61,6 @@ use crate::tools::fmt::run_parallelized; use crate::util::file_watcher; use crate::util::fs::canonicalize_path; use crate::util::fs::specifier_from_file_path; -use crate::util::fs::FileCollector; use crate::util::path::is_script_ext; use crate::util::sync::AtomicFlag; @@ -401,7 +401,7 @@ fn collect_lint_files( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) - .collect_file_patterns(files) + .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files) } #[allow(clippy::print_stdout)] diff --git a/cli/tools/registry/paths.rs b/cli/tools/registry/paths.rs index 721ef6ecea..1fe8830dd5 100644 --- a/cli/tools/registry/paths.rs +++ b/cli/tools/registry/paths.rs @@ -8,12 +8,12 @@ use std::path::PathBuf; use deno_ast::MediaType; use deno_ast::ModuleSpecifier; +use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_core::error::AnyError; use thiserror::Error; use crate::args::CliOptions; -use crate::util::fs::FileCollector; use super::diagnostics::PublishDiagnostic; use super::diagnostics::PublishDiagnosticsCollector; @@ -319,14 +319,14 @@ fn collect_paths( file_patterns: FilePatterns, ) -> Result, AnyError> { FileCollector::new(|e| { - if !e.file_type.is_file() { + if !e.metadata.is_file { if let Ok(specifier) = ModuleSpecifier::from_file_path(e.path) { diagnostics_collector.push(PublishDiagnostic::UnsupportedFileType { specifier, - kind: if e.file_type.is_symlink() { - "symlink".to_owned() + kind: if e.metadata.is_symlink { + "symlink".to_string() } else { - format!("{:?}", e.file_type) + "Unknown".to_string() }, }); } @@ -341,5 +341,5 @@ fn collect_paths( .ignore_node_modules() .set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned)) .use_gitignore() - .collect_file_patterns(file_patterns) + .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, file_patterns) } diff --git a/cli/tools/test/mod.rs b/cli/tools/test/mod.rs index 7042a82b97..e7273c0694 100644 --- a/cli/tools/test/mod.rs +++ b/cli/tools/test/mod.rs @@ -15,7 +15,6 @@ use crate::graph_util::has_graph_root_local_dependent_changed; use crate::ops; use crate::util::file_watcher; use crate::util::fs::collect_specifiers; -use crate::util::fs::WalkEntry; use crate::util::path::get_extension; use crate::util::path::is_script_ext; use crate::util::path::mapped_specifier_for_tsc; @@ -27,6 +26,7 @@ use deno_ast::swc::common::comments::CommentKind; use deno_ast::MediaType; use deno_ast::SourceRangedForSpanned; use deno_config::glob::FilePatterns; +use deno_config::glob::WalkEntry; use deno_core::anyhow; use deno_core::anyhow::bail; use deno_core::anyhow::Context as _; diff --git a/cli/util/fs.rs b/cli/util/fs.rs index f33368d1a0..c414abd591 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -1,8 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use std::collections::HashSet; use std::env::current_dir; -use std::fs::FileType; use std::fs::OpenOptions; use std::io::Error; use std::io::ErrorKind; @@ -11,11 +9,12 @@ use std::path::Path; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; -use walkdir::WalkDir; +use deno_config::glob::FileCollector; use deno_config::glob::FilePatterns; use deno_config::glob::PathOrPattern; use deno_config::glob::PathOrPatternSet; +use deno_config::glob::WalkEntry; use deno_core::anyhow::anyhow; use deno_core::anyhow::Context; use deno_core::error::AnyError; @@ -25,8 +24,6 @@ use deno_core::ModuleSpecifier; use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_node::PathClean; -use crate::util::gitignore::DirGitIgnores; -use crate::util::gitignore::GitIgnoreTree; use crate::util::path::get_atomic_file_path; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; @@ -270,192 +267,6 @@ pub fn resolve_from_cwd(path: &Path) -> Result { Ok(normalize_path(resolved_path)) } -#[derive(Debug, Clone)] -pub struct WalkEntry<'a> { - pub path: &'a Path, - pub file_type: &'a FileType, - pub patterns: &'a FilePatterns, -} - -/// Collects file paths that satisfy the given predicate, by recursively walking `files`. -/// If the walker visits a path that is listed in `ignore`, it skips descending into the directory. -pub struct FileCollector bool> { - file_filter: TFilter, - ignore_git_folder: bool, - ignore_node_modules: bool, - vendor_folder: Option, - use_gitignore: bool, -} - -impl bool> FileCollector { - pub fn new(file_filter: TFilter) -> Self { - Self { - file_filter, - ignore_git_folder: false, - ignore_node_modules: false, - vendor_folder: None, - use_gitignore: false, - } - } - - pub fn ignore_node_modules(mut self) -> Self { - self.ignore_node_modules = true; - self - } - - pub fn set_vendor_folder(mut self, vendor_folder: Option) -> Self { - self.vendor_folder = vendor_folder; - self - } - - pub fn ignore_git_folder(mut self) -> Self { - self.ignore_git_folder = true; - self - } - - pub fn use_gitignore(mut self) -> Self { - self.use_gitignore = true; - self - } - - pub fn collect_file_patterns( - &self, - file_patterns: FilePatterns, - ) -> Result, AnyError> { - fn is_pattern_matched( - maybe_git_ignore: Option<&DirGitIgnores>, - path: &Path, - is_dir: bool, - file_patterns: &FilePatterns, - ) -> bool { - use deno_config::glob::FilePatternsMatch; - - let path_kind = match is_dir { - true => deno_config::glob::PathKind::Directory, - false => deno_config::glob::PathKind::File, - }; - match file_patterns.matches_path_detail(path, path_kind) { - FilePatternsMatch::Passed => { - // check gitignore - let is_gitignored = maybe_git_ignore - .as_ref() - .map(|git_ignore| git_ignore.is_ignored(path, is_dir)) - .unwrap_or(false); - !is_gitignored - } - FilePatternsMatch::PassedOptedOutExclude => true, - FilePatternsMatch::Excluded => false, - } - } - - let mut maybe_git_ignores = if self.use_gitignore { - // Override explicitly specified include paths in the - // .gitignore file. This does not apply to globs because - // that is way too complicated to reason about. - let include_paths = file_patterns - .include - .as_ref() - .map(|include| { - include - .inner() - .iter() - .filter_map(|path_or_pattern| { - if let PathOrPattern::Path(p) = path_or_pattern { - Some(p.clone()) - } else { - None - } - }) - .collect::>() - }) - .unwrap_or_default(); - Some(GitIgnoreTree::new( - Arc::new(deno_runtime::deno_fs::RealFs), - include_paths, - )) - } else { - None - }; - let mut target_files = Vec::new(); - let mut visited_paths = HashSet::new(); - let file_patterns_by_base = file_patterns.split_by_base(); - for file_patterns in file_patterns_by_base { - let file = normalize_path(&file_patterns.base); - // use an iterator in order to minimize the number of file system operations - let mut iterator = WalkDir::new(&file) - .follow_links(false) // the default, but be explicit - .into_iter(); - loop { - let e = match iterator.next() { - None => break, - Some(Err(_)) => continue, - Some(Ok(entry)) => entry, - }; - let file_type = e.file_type(); - let is_dir = file_type.is_dir(); - let path = e.path().to_path_buf(); - let maybe_gitignore = - maybe_git_ignores.as_mut().and_then(|git_ignores| { - if is_dir { - git_ignores.get_resolved_git_ignore_for_dir(&path) - } else { - git_ignores.get_resolved_git_ignore_for_file(&path) - } - }); - if !is_pattern_matched( - maybe_gitignore.as_deref(), - &path, - is_dir, - &file_patterns, - ) { - if is_dir { - iterator.skip_current_dir(); - } - } else if is_dir { - // allow the user to opt out of ignoring by explicitly specifying the dir - let opt_out_ignore = file == path; - let should_ignore_dir = !opt_out_ignore && self.is_ignored_dir(&path); - if should_ignore_dir || !visited_paths.insert(path.clone()) { - iterator.skip_current_dir(); - } - } else if (self.file_filter)(WalkEntry { - path: &path, - file_type: &file_type, - patterns: &file_patterns, - }) && visited_paths.insert(path.clone()) - { - target_files.push(path); - } - } - } - Ok(target_files) - } - - fn is_ignored_dir(&self, path: &Path) -> bool { - path - .file_name() - .map(|dir_name| { - let dir_name = dir_name.to_string_lossy().to_lowercase(); - let is_ignored_file = match dir_name.as_str() { - "node_modules" => self.ignore_node_modules, - ".git" => self.ignore_git_folder, - _ => false, - }; - is_ignored_file - }) - .unwrap_or(false) - || self.is_vendor_folder(path) - } - - fn is_vendor_folder(&self, path: &Path) -> bool { - self - .vendor_folder - .as_ref() - .map(|vendor_folder| path == *vendor_folder) - .unwrap_or(false) - } -} - /// Collects module specifiers that satisfy the given predicate as a file path, by recursively walking `include`. /// Specifiers that start with http and https are left intact. /// Note: This ignores all .git and node_modules folders. @@ -501,7 +312,7 @@ pub fn collect_specifiers( .ignore_git_folder() .ignore_node_modules() .set_vendor_folder(vendor_folder) - .collect_file_patterns(files)?; + .collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)?; let mut collected_files_as_urls = collected_files .iter() .map(|f| specifier_from_file_path(f).unwrap()) @@ -953,150 +764,6 @@ mod tests { assert_eq!(resolve_from_cwd(expected).unwrap(), absolute_expected); } - #[test] - fn test_collect_files() { - fn create_files(dir_path: &PathRef, files: &[&str]) { - dir_path.create_dir_all(); - for f in files { - dir_path.join(f).write(""); - } - } - - // dir.ts - // ├── a.ts - // ├── b.js - // ├── child - // | ├── git - // | | └── git.js - // | ├── node_modules - // | | └── node_modules.js - // | ├── vendor - // | | └── vendor.js - // │ ├── e.mjs - // │ ├── f.mjsx - // │ ├── .foo.TS - // │ └── README.md - // ├── c.tsx - // ├── d.jsx - // └── ignore - // ├── g.d.ts - // └── .gitignore - - let t = TempDir::new(); - - let root_dir_path = t.path().join("dir.ts"); - let root_dir_files = ["a.ts", "b.js", "c.tsx", "d.jsx"]; - create_files(&root_dir_path, &root_dir_files); - - let child_dir_path = root_dir_path.join("child"); - let child_dir_files = ["e.mjs", "f.mjsx", ".foo.TS", "README.md"]; - create_files(&child_dir_path, &child_dir_files); - - t.create_dir_all("dir.ts/child/node_modules"); - t.write("dir.ts/child/node_modules/node_modules.js", ""); - t.create_dir_all("dir.ts/child/.git"); - t.write("dir.ts/child/.git/git.js", ""); - t.create_dir_all("dir.ts/child/vendor"); - t.write("dir.ts/child/vendor/vendor.js", ""); - - let ignore_dir_path = root_dir_path.join("ignore"); - let ignore_dir_files = ["g.d.ts", ".gitignore"]; - create_files(&ignore_dir_path, &ignore_dir_files); - - let file_patterns = FilePatterns { - base: root_dir_path.to_path_buf(), - include: None, - exclude: PathOrPatternSet::new(vec![PathOrPattern::Path( - ignore_dir_path.to_path_buf(), - )]), - }; - let file_collector = FileCollector::new(|e| { - // exclude dotfiles - e.path - .file_name() - .and_then(|f| f.to_str()) - .map(|f| !f.starts_with('.')) - .unwrap_or(false) - }); - - let result = file_collector - .collect_file_patterns(file_patterns.clone()) - .unwrap(); - let expected = [ - "README.md", - "a.ts", - "b.js", - "c.tsx", - "d.jsx", - "e.mjs", - "f.mjsx", - "git.js", - "node_modules.js", - "vendor.js", - ]; - let mut file_names = result - .into_iter() - .map(|r| r.file_name().unwrap().to_string_lossy().to_string()) - .collect::>(); - file_names.sort(); - assert_eq!(file_names, expected); - - // test ignoring the .git and node_modules folder - let file_collector = file_collector - .ignore_git_folder() - .ignore_node_modules() - .set_vendor_folder(Some(child_dir_path.join("vendor").to_path_buf())); - let result = file_collector - .collect_file_patterns(file_patterns.clone()) - .unwrap(); - let expected = [ - "README.md", - "a.ts", - "b.js", - "c.tsx", - "d.jsx", - "e.mjs", - "f.mjsx", - ]; - let mut file_names = result - .into_iter() - .map(|r| r.file_name().unwrap().to_string_lossy().to_string()) - .collect::>(); - file_names.sort(); - assert_eq!(file_names, expected); - - // test opting out of ignoring by specifying the dir - let file_patterns = FilePatterns { - base: root_dir_path.to_path_buf(), - include: Some(PathOrPatternSet::new(vec![ - PathOrPattern::Path(root_dir_path.to_path_buf()), - PathOrPattern::Path( - root_dir_path.to_path_buf().join("child/node_modules/"), - ), - ])), - exclude: PathOrPatternSet::new(vec![PathOrPattern::Path( - ignore_dir_path.to_path_buf(), - )]), - }; - let result = file_collector.collect_file_patterns(file_patterns).unwrap(); - let expected = [ - "README.md", - "a.ts", - "b.js", - "c.tsx", - "d.jsx", - "e.mjs", - "f.mjsx", - "node_modules.js", - ]; - let mut file_names = result - .into_iter() - .map(|r| r.file_name().unwrap().to_string_lossy().to_string()) - .collect::>(); - file_names.sort(); - assert_eq!(file_names, expected); - } - #[test] fn test_collect_specifiers() { fn create_files(dir_path: &PathRef, files: &[&str]) { diff --git a/cli/util/gitignore.rs b/cli/util/gitignore.rs deleted file mode 100644 index 4538e0912f..0000000000 --- a/cli/util/gitignore.rs +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::collections::HashMap; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; - -/// Resolved gitignore for a directory. -pub struct DirGitIgnores { - current: Option>, - parent: Option>, -} - -impl DirGitIgnores { - pub fn is_ignored(&self, path: &Path, is_dir: bool) -> bool { - let mut is_ignored = false; - if let Some(parent) = &self.parent { - is_ignored = parent.is_ignored(path, is_dir); - } - if let Some(current) = &self.current { - match current.matched(path, is_dir) { - ignore::Match::None => {} - ignore::Match::Ignore(_) => { - is_ignored = true; - } - ignore::Match::Whitelist(_) => { - is_ignored = false; - } - } - } - is_ignored - } -} - -/// Resolves gitignores in a directory tree taking into account -/// ancestor gitignores that may be found in a directory. -pub struct GitIgnoreTree { - fs: Arc, - ignores: HashMap>>, - include_paths: Vec, -} - -impl GitIgnoreTree { - pub fn new( - fs: Arc, - // paths that should override what's in the gitignore - include_paths: Vec, - ) -> Self { - Self { - fs, - ignores: Default::default(), - include_paths, - } - } - - pub fn get_resolved_git_ignore_for_dir( - &mut self, - dir_path: &Path, - ) -> Option> { - // for directories, provide itself in order to tell - // if it should stop searching for gitignores because - // maybe this dir_path is a .git directory - let parent = dir_path.parent()?; - self.get_resolved_git_ignore_inner(parent, Some(dir_path)) - } - - pub fn get_resolved_git_ignore_for_file( - &mut self, - file_path: &Path, - ) -> Option> { - let dir_path = file_path.parent()?; - self.get_resolved_git_ignore_inner(dir_path, None) - } - - fn get_resolved_git_ignore_inner( - &mut self, - dir_path: &Path, - maybe_parent: Option<&Path>, - ) -> Option> { - let maybe_resolved = self.ignores.get(dir_path).cloned(); - if let Some(resolved) = maybe_resolved { - resolved - } else { - let resolved = self.resolve_gitignore_in_dir(dir_path, maybe_parent); - self.ignores.insert(dir_path.to_owned(), resolved.clone()); - resolved - } - } - - fn resolve_gitignore_in_dir( - &mut self, - dir_path: &Path, - maybe_parent: Option<&Path>, - ) -> Option> { - if let Some(parent) = maybe_parent { - // stop searching if the parent dir had a .git directory in it - if self.fs.exists_sync(&parent.join(".git")) { - return None; - } - } - - let parent = dir_path.parent().and_then(|parent| { - self.get_resolved_git_ignore_inner(parent, Some(dir_path)) - }); - let current = self - .fs - .read_text_file_lossy_sync(&dir_path.join(".gitignore"), None) - .ok() - .and_then(|text| { - let mut builder = ignore::gitignore::GitignoreBuilder::new(dir_path); - for line in text.lines() { - builder.add_line(None, line).ok()?; - } - // override the gitignore contents to include these paths - for path in &self.include_paths { - if let Ok(suffix) = path.strip_prefix(dir_path) { - let suffix = suffix.to_string_lossy().replace('\\', "/"); - let _ignore = builder.add_line(None, &format!("!/{}", suffix)); - if !suffix.ends_with('/') { - let _ignore = builder.add_line(None, &format!("!/{}/", suffix)); - } - } - } - let gitignore = builder.build().ok()?; - Some(Rc::new(gitignore)) - }); - if parent.is_none() && current.is_none() { - None - } else { - Some(Rc::new(DirGitIgnores { current, parent })) - } - } -} - -#[cfg(test)] -mod test { - use deno_runtime::deno_fs::InMemoryFs; - - use super::*; - - #[test] - fn git_ignore_tree() { - let fs = InMemoryFs::default(); - fs.setup_text_files(vec![ - ("/.gitignore".into(), "file.txt".into()), - ("/sub_dir/.gitignore".into(), "data.txt".into()), - ( - "/sub_dir/sub_dir/.gitignore".into(), - "!file.txt\nignore.txt".into(), - ), - ]); - let mut ignore_tree = GitIgnoreTree::new(Arc::new(fs), Vec::new()); - let mut run_test = |path: &str, expected: bool| { - let path = PathBuf::from(path); - let gitignore = - ignore_tree.get_resolved_git_ignore_for_file(&path).unwrap(); - assert_eq!( - gitignore.is_ignored(&path, /* is_dir */ false), - expected, - "Path: {}", - path.display() - ); - }; - run_test("/file.txt", true); - run_test("/other.txt", false); - run_test("/data.txt", false); - run_test("/sub_dir/file.txt", true); - run_test("/sub_dir/other.txt", false); - run_test("/sub_dir/data.txt", true); - run_test("/sub_dir/sub_dir/file.txt", false); // unignored up here - run_test("/sub_dir/sub_dir/sub_dir/file.txt", false); - run_test("/sub_dir/sub_dir/sub_dir/ignore.txt", true); - run_test("/sub_dir/sub_dir/ignore.txt", true); - run_test("/sub_dir/ignore.txt", false); - run_test("/ignore.txt", false); - } -} diff --git a/cli/util/mod.rs b/cli/util/mod.rs index 69cdc77c34..2b6583fbc5 100644 --- a/cli/util/mod.rs +++ b/cli/util/mod.rs @@ -9,7 +9,6 @@ pub mod display; pub mod draw_thread; pub mod file_watcher; pub mod fs; -pub mod gitignore; pub mod logger; pub mod path; pub mod progress_bar; diff --git a/cli/util/sync/atomic_flag.rs b/cli/util/sync/atomic_flag.rs deleted file mode 100644 index 75396dcf4d..0000000000 --- a/cli/util/sync/atomic_flag.rs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -use std::sync::atomic::AtomicBool; -use std::sync::atomic::Ordering; - -/// Simplifies the use of an atomic boolean as a flag. -#[derive(Debug, Default)] -pub struct AtomicFlag(AtomicBool); - -impl AtomicFlag { - /// Raises the flag returning if the raise was successful. - pub fn raise(&self) -> bool { - !self.0.swap(true, Ordering::SeqCst) - } - - /// Gets if the flag is raised. - pub fn is_raised(&self) -> bool { - self.0.load(Ordering::SeqCst) - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn atomic_flag_raises() { - let flag = AtomicFlag::default(); - assert!(!flag.is_raised()); // false by default - assert!(flag.raise()); - assert!(flag.is_raised()); - assert!(!flag.raise()); - assert!(flag.is_raised()); - } -} diff --git a/cli/util/sync/mod.rs b/cli/util/sync/mod.rs index 28aab7f477..f584375035 100644 --- a/cli/util/sync/mod.rs +++ b/cli/util/sync/mod.rs @@ -1,14 +1,14 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. mod async_flag; -mod atomic_flag; mod sync_read_async_write_lock; mod task_queue; mod value_creator; pub use async_flag::AsyncFlag; -pub use atomic_flag::AtomicFlag; pub use sync_read_async_write_lock::SyncReadAsyncWriteLock; pub use task_queue::TaskQueue; pub use task_queue::TaskQueuePermit; pub use value_creator::MultiRuntimeAsyncValueCreator; +// todo(dsherret): this being in the unsync module is slightly confusing, but it's Sync +pub use deno_core::unsync::AtomicFlag; diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs index f639a700bf..8f791f4c23 100644 --- a/ext/fs/interface.rs +++ b/ext/fs/interface.rs @@ -343,23 +343,68 @@ impl<'a> DenoConfigFsAdapter<'a> { } impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> { - fn read_to_string(&self, path: &Path) -> Result { - use deno_io::fs::FsError; - use std::io::ErrorKind; + fn read_to_string_lossy( + &self, + path: &Path, + ) -> Result { self .0 .read_text_file_lossy_sync(path, None) - .map_err(|err| match err { - FsError::Io(io) => io, - FsError::FileBusy => std::io::Error::new(ErrorKind::Other, "file busy"), - FsError::NotSupported => { - std::io::Error::new(ErrorKind::Other, "not supported") - } - FsError::PermissionDenied(name) => std::io::Error::new( - ErrorKind::PermissionDenied, - format!("requires {}", name), - ), + .map_err(map_deno_fs_to_config_err) + } + + fn stat_sync( + &self, + path: &Path, + ) -> Result { + self + .0 + .stat_sync(path) + .map(|stat| deno_config::fs::FsMetadata { + is_file: stat.is_file, + is_directory: stat.is_directory, + is_symlink: stat.is_symlink, }) + .map_err(map_deno_fs_to_config_err) + } + + fn read_dir( + &self, + path: &Path, + ) -> Result, std::io::Error> { + self + .0 + .read_dir_sync(path) + .map_err(map_deno_fs_to_config_err) + .map(|entries| { + entries + .into_iter() + .map(|e| deno_config::fs::FsDirEntry { + path: path.join(e.name), + metadata: deno_config::fs::FsMetadata { + is_file: e.is_file, + is_directory: e.is_directory, + is_symlink: e.is_symlink, + }, + }) + .collect() + }) + } +} + +fn map_deno_fs_to_config_err(fs_err: deno_io::fs::FsError) -> std::io::Error { + use deno_io::fs::FsError; + use std::io::ErrorKind; + match fs_err { + FsError::Io(io) => io, + FsError::FileBusy => std::io::Error::new(ErrorKind::Other, "file busy"), + FsError::NotSupported => { + std::io::Error::new(ErrorKind::Other, "not supported") + } + FsError::PermissionDenied(name) => std::io::Error::new( + ErrorKind::PermissionDenied, + format!("requires {}", name), + ), } } diff --git a/tests/specs/workspaces/non_fatal_diagnostics/lint.out b/tests/specs/workspaces/non_fatal_diagnostics/lint.out index 28ac6b0eb3..f2fbd1822b 100644 --- a/tests/specs/workspaces/non_fatal_diagnostics/lint.out +++ b/tests/specs/workspaces/non_fatal_diagnostics/lint.out @@ -1,5 +1,5 @@ -The 'compilerOptions' field can only be specified in the root workspace deno.json file. +The "compilerOptions" field can only be specified in the root workspace deno.json file. at file:///[WILDLINE]/sub/deno.json -The 'lint.report' field can only be specified in the root workspace deno.json file. +The "lint.report" field can only be specified in the root workspace deno.json file. at file:///[WILDLINE]/sub/deno.json Checked 1 file From 74ac29bae666cd910c6f66d47b20e3209afc1fe7 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Fri, 5 Jul 2024 23:45:06 +0100 Subject: [PATCH 8/8] fix(permissions): handle ipv6 addresses correctly (#24397) Also don't panic on invalid domain names and addresses. Extracted with cleanups up from #24080 Co-authored-by: Yazan AbdAl-Rahman --- cli/args/flags.rs | 16 +- cli/args/flags_net.rs | 57 ++- runtime/ops/permissions.rs | 18 +- runtime/permissions/lib.rs | 448 ++++++++++++++---- .../permissions_blob_remote.ts.out | 2 +- .../permissions_data_remote.ts.out | 2 +- .../permissions_remote_remote.ts.out | 2 +- .../workers/permissions_blob_remote.ts.out | 2 +- .../workers/permissions_data_remote.ts.out | 2 +- .../workers/permissions_dynamic_remote.ts.out | 2 +- .../workers/permissions_remote_remote.ts.out | 2 +- 11 files changed, 415 insertions(+), 138 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 4d0465caab..710dbc0a64 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -7859,7 +7859,7 @@ mod tests { let r = flags_from_vec(svec![ "deno", "run", - "--unsafely-ignore-certificate-errors=deno.land,localhost,::,127.0.0.1,[::1],1.2.3.4", + "--unsafely-ignore-certificate-errors=deno.land,localhost,[::],127.0.0.1,[::1],1.2.3.4", "script.ts" ]); assert_eq!( @@ -7871,7 +7871,7 @@ mod tests { unsafely_ignore_certificate_errors: Some(svec![ "deno.land", "localhost", - "::", + "[::]", "127.0.0.1", "[::1]", "1.2.3.4" @@ -7887,7 +7887,7 @@ mod tests { let r = flags_from_vec(svec![ "deno", "repl", - "--unsafely-ignore-certificate-errors=deno.land,localhost,::,127.0.0.1,[::1],1.2.3.4"]); + "--unsafely-ignore-certificate-errors=deno.land,localhost,[::],127.0.0.1,[::1],1.2.3.4"]); assert_eq!( r.unwrap(), Flags { @@ -7899,7 +7899,7 @@ mod tests { unsafely_ignore_certificate_errors: Some(svec![ "deno.land", "localhost", - "::", + "[::]", "127.0.0.1", "[::1]", "1.2.3.4" @@ -8091,7 +8091,7 @@ mod tests { let r = flags_from_vec(svec![ "deno", "run", - "--allow-net=deno.land,deno.land:80,::,127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080", + "--allow-net=deno.land,deno.land:80,[::],127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080", "script.ts" ]); assert_eq!( @@ -8104,7 +8104,7 @@ mod tests { allow_net: Some(svec![ "deno.land", "deno.land:80", - "::", + "[::]", "127.0.0.1", "[::1]", "1.2.3.4:5678", @@ -8126,7 +8126,7 @@ mod tests { let r = flags_from_vec(svec![ "deno", "run", - "--deny-net=deno.land,deno.land:80,::,127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080", + "--deny-net=deno.land,deno.land:80,[::],127.0.0.1,[::1],1.2.3.4:5678,:5678,[::1]:8080", "script.ts" ]); assert_eq!( @@ -8139,7 +8139,7 @@ mod tests { deny_net: Some(svec![ "deno.land", "deno.land:80", - "::", + "[::]", "127.0.0.1", "[::1]", "1.2.3.4:5678", diff --git a/cli/args/flags_net.rs b/cli/args/flags_net.rs index 2ea4670563..57cf8d527c 100644 --- a/cli/args/flags_net.rs +++ b/cli/args/flags_net.rs @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::url::Url; +use deno_runtime::deno_permissions::NetDescriptor; use std::net::IpAddr; use std::str::FromStr; @@ -42,21 +43,17 @@ pub fn validator(host_and_port: &str) -> Result { /// `127.0.0.1:port` and `localhost:port`. pub fn parse(paths: Vec) -> clap::error::Result> { let mut out: Vec = vec![]; - for host_and_port in paths.iter() { - if Url::parse(&format!("internal://{host_and_port}")).is_ok() - || host_and_port.parse::().is_ok() - { - out.push(host_and_port.to_owned()) - } else if let Ok(port) = host_and_port.parse::() { + for host_and_port in paths.into_iter() { + if let Ok(port) = host_and_port.parse::() { // we got bare port, let's add default hosts for host in ["0.0.0.0", "127.0.0.1", "localhost"].iter() { out.push(format!("{}:{}", host, port.0)); } } else { - return Err(clap::Error::raw( - clap::error::ErrorKind::InvalidValue, - format!("Bad host:port pair: {host_and_port}"), - )); + host_and_port.parse::().map_err(|e| { + clap::Error::raw(clap::error::ErrorKind::InvalidValue, format!("{e:?}")) + })?; + out.push(host_and_port) } } Ok(out) @@ -121,8 +118,8 @@ mod tests { let entries = svec![ "deno.land", "deno.land:80", - "::", - "::1", + "[::]", + "[::1]", "127.0.0.1", "[::1]", "1.2.3.4:5678", @@ -142,8 +139,8 @@ mod tests { let expected = svec![ "deno.land", "deno.land:80", - "::", - "::1", + "[::]", + "[::1]", "127.0.0.1", "[::1]", "1.2.3.4:5678", @@ -174,10 +171,8 @@ mod tests { #[test] fn parse_net_args_ipv6() { - let entries = - svec!["::", "::1", "[::1]", "[::]:5678", "[::1]:5678", "::cafe"]; - let expected = - svec!["::", "::1", "[::1]", "[::]:5678", "[::1]:5678", "::cafe"]; + let entries = svec!["[::1]", "[::]:5678", "[::1]:5678"]; + let expected = svec!["[::1]", "[::]:5678", "[::1]:5678"]; let actual = parse(entries).unwrap(); assert_eq!(actual, expected); } @@ -190,12 +185,36 @@ mod tests { #[test] fn parse_net_args_ipv6_error2() { - let entries = svec!["0123:4567:890a:bcde:fg::"]; + let entries = svec!["::1"]; assert!(parse(entries).is_err()); } #[test] fn parse_net_args_ipv6_error3() { + let entries = svec!["::"]; + assert!(parse(entries).is_err()); + } + + #[test] + fn parse_net_args_ipv6_error4() { + let entries = svec!["::cafe"]; + assert!(parse(entries).is_err()); + } + + #[test] + fn parse_net_args_ipv6_error5() { + let entries = svec!["1::1"]; + assert!(parse(entries).is_err()); + } + + #[test] + fn parse_net_args_ipv6_error6() { + let entries = svec!["0123:4567:890a:bcde:fg::"]; + assert!(parse(entries).is_err()); + } + + #[test] + fn parse_net_args_ipv6_error7() { let entries = svec!["[::q]:8080"]; assert!(parse(entries).is_err()); } diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs index a961fd3ea7..c15e7d0135 100644 --- a/runtime/ops/permissions.rs +++ b/runtime/ops/permissions.rs @@ -4,10 +4,8 @@ use ::deno_permissions::parse_sys_kind; use ::deno_permissions::PermissionState; use ::deno_permissions::PermissionsContainer; use deno_core::error::custom_error; -use deno_core::error::uri_error; use deno_core::error::AnyError; use deno_core::op2; -use deno_core::url; use deno_core::OpState; use serde::Deserialize; use serde::Serialize; @@ -65,7 +63,7 @@ pub fn op_query_permission( "net" => permissions.net.query( match args.host.as_deref() { None => None, - Some(h) => Some(parse_host(h)?), + Some(h) => Some(h.parse()?), } .as_ref(), ), @@ -100,7 +98,7 @@ pub fn op_revoke_permission( "net" => permissions.net.revoke( match args.host.as_deref() { None => None, - Some(h) => Some(parse_host(h)?), + Some(h) => Some(h.parse()?), } .as_ref(), ), @@ -135,7 +133,7 @@ pub fn op_request_permission( "net" => permissions.net.request( match args.host.as_deref() { None => None, - Some(h) => Some(parse_host(h)?), + Some(h) => Some(h.parse()?), } .as_ref(), ), @@ -155,13 +153,3 @@ pub fn op_request_permission( }; Ok(PermissionStatus::from(perm)) } - -fn parse_host(host_str: &str) -> Result<(String, Option), AnyError> { - let url = url::Url::parse(&format!("http://{host_str}/")) - .map_err(|_| uri_error("Invalid host"))?; - if url.path() != "/" { - return Err(uri_error("Invalid host")); - } - let hostname = url.host_str().unwrap(); - Ok((hostname.to_string(), url.port())) -} diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 3b3f68a530..a2245316b7 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -16,7 +16,6 @@ use deno_core::url; use deno_core::url::Url; use deno_core::ModuleSpecifier; use deno_terminal::colors; -use fqdn::fqdn; use fqdn::FQDN; use once_cell::sync::Lazy; use std::borrow::Cow; @@ -25,6 +24,8 @@ use std::ffi::OsStr; use std::fmt; use std::fmt::Debug; use std::hash::Hash; +use std::net::IpAddr; +use std::net::Ipv6Addr; use std::path::Path; use std::path::PathBuf; use std::str::FromStr; @@ -691,14 +692,49 @@ impl Descriptor for WriteDescriptor { } #[derive(Clone, Eq, PartialEq, Hash, Debug)] -pub struct NetDescriptor(pub FQDN, pub Option); +pub enum Host { + Fqdn(FQDN), + Ip(IpAddr), +} -impl NetDescriptor { - fn new>(host: &&(T, Option)) -> Self { - NetDescriptor(fqdn!(host.0.as_ref()), host.1) +impl FromStr for Host { + type Err = AnyError; + + fn from_str(s: &str) -> Result { + if s.starts_with('[') && s.ends_with(']') { + let ip = s[1..s.len() - 1] + .parse::() + .map_err(|_| uri_error(format!("invalid IPv6 address: '{s}'")))?; + return Ok(Host::Ip(IpAddr::V6(ip))); + } + let (without_trailing_dot, has_trailing_dot) = + s.strip_suffix('.').map_or((s, false), |s| (s, true)); + if let Ok(ip) = without_trailing_dot.parse::() { + if has_trailing_dot { + return Err(uri_error(format!( + "invalid host: '{without_trailing_dot}'" + ))); + } + Ok(Host::Ip(ip)) + } else { + let lower = if s.chars().all(|c| c.is_ascii_lowercase()) { + Cow::Borrowed(s) + } else { + Cow::Owned(s.to_ascii_lowercase()) + }; + let fqdn = FQDN::from_str(&lower) + .with_context(|| format!("invalid host: '{s}'"))?; + if fqdn.is_root() { + return Err(uri_error(format!("invalid empty host: '{s}'"))); + } + Ok(Host::Fqdn(fqdn)) + } } } +#[derive(Clone, Eq, PartialEq, Hash, Debug)] +pub struct NetDescriptor(pub Host, pub Option); + impl Descriptor for NetDescriptor { type Arg = String; @@ -731,26 +767,72 @@ impl Descriptor for NetDescriptor { impl FromStr for NetDescriptor { type Err = AnyError; - fn from_str(s: &str) -> Result { - // Set the scheme to `unknown` to parse the URL, as we really don't know - // what the scheme is. We only using Url::parse to parse the host and port - // and don't care about the scheme. - let url = url::Url::parse(&format!("unknown://{s}"))?; - let hostname = url - .host_str() - .ok_or(url::ParseError::EmptyHost)? - .to_string(); + fn from_str(hostname: &str) -> Result { + // If this is a IPv6 address enclosed in square brackets, parse it as such. + if hostname.starts_with('[') { + if let Some((ip, after)) = hostname.split_once(']') { + let ip = ip[1..].parse::().map_err(|_| { + uri_error(format!("invalid IPv6 address in '{hostname}': '{ip}'")) + })?; + let port = if let Some(port) = after.strip_prefix(':') { + let port = port.parse::().map_err(|_| { + uri_error(format!("invalid port in '{hostname}': '{port}'")) + })?; + Some(port) + } else if after.is_empty() { + None + } else { + return Err(uri_error(format!("invalid host: '{hostname}'"))); + }; + return Ok(NetDescriptor(Host::Ip(IpAddr::V6(ip)), port)); + } else { + return Err(uri_error(format!("invalid host: '{hostname}'"))); + } + } - Ok(NetDescriptor(fqdn!(&hostname), url.port())) + // Otherwise it is an IPv4 address or a FQDN with an optional port. + let (host, port) = match hostname.split_once(':') { + Some((_, "")) => { + return Err(uri_error(format!("invalid empty port in '{hostname}'"))); + } + Some((host, port)) => (host, port), + None => (hostname, ""), + }; + let host = host.parse::()?; + + let port = if port.is_empty() { + None + } else { + let port = port.parse::().map_err(|_| { + // If the user forgot to enclose an IPv6 address in square brackets, we + // should give them a hint. There are always at least two colons in an + // IPv6 address, so this heuristic finds likely a bare IPv6 address. + if port.contains(':') { + uri_error(format!( + "ipv6 addresses must be enclosed in square brackets: '{hostname}'" + )) + } else { + uri_error(format!("invalid port in '{hostname}': '{port}'")) + } + })?; + Some(port) + }; + + Ok(NetDescriptor(host, port)) } } impl fmt::Display for NetDescriptor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&match self.1 { - None => self.0.to_string(), - Some(port) => format!("{}:{}", self.0, port), - }) + match &self.0 { + Host::Fqdn(fqdn) => write!(f, "{fqdn}"), + Host::Ip(IpAddr::V4(ip)) => write!(f, "{ip}"), + Host::Ip(IpAddr::V6(ip)) => write!(f, "[{ip}]"), + }?; + if let Some(port) = self.1 { + write!(f, ":{}", port)?; + } + Ok(()) } } @@ -1107,37 +1189,25 @@ impl UnaryPermission { } impl UnaryPermission { - pub fn query>( - &self, - host: Option<&(T, Option)>, - ) -> PermissionState { - self.query_desc( - host.map(|h| NetDescriptor::new(&h)).as_ref(), - AllowPartial::TreatAsPartialGranted, - ) + pub fn query(&self, host: Option<&NetDescriptor>) -> PermissionState { + self.query_desc(host, AllowPartial::TreatAsPartialGranted) } - pub fn request>( - &mut self, - host: Option<&(T, Option)>, - ) -> PermissionState { - self.request_desc(host.map(|h| NetDescriptor::new(&h)).as_ref(), || None) + pub fn request(&mut self, host: Option<&NetDescriptor>) -> PermissionState { + self.request_desc(host, || None) } - pub fn revoke>( - &mut self, - host: Option<&(T, Option)>, - ) -> PermissionState { - self.revoke_desc(host.map(|h| NetDescriptor::new(&h)).as_ref()) + pub fn revoke(&mut self, host: Option<&NetDescriptor>) -> PermissionState { + self.revoke_desc(host) } - pub fn check>( + pub fn check( &mut self, - host: &(T, Option), + host: &NetDescriptor, api_name: Option<&str>, ) -> Result<(), AnyError> { skip_check_if_is_permission_fully_granted!(self); - self.check_desc(Some(&NetDescriptor::new(&host)), false, api_name, || None) + self.check_desc(Some(host), false, api_name, || None) } pub fn check_url( @@ -1146,17 +1216,14 @@ impl UnaryPermission { api_name: Option<&str>, ) -> Result<(), AnyError> { skip_check_if_is_permission_fully_granted!(self); - let hostname = url + let host = url .host_str() - .ok_or_else(|| uri_error("Missing host"))? - .to_string(); - let host = &(&hostname, url.port_or_known_default()); - let display_host = match url.port() { - None => hostname.clone(), - Some(port) => format!("{hostname}:{port}"), - }; - self.check_desc(Some(&NetDescriptor::new(&host)), false, api_name, || { - Some(format!("\"{}\"", display_host)) + .ok_or_else(|| type_error(format!("Missing host in url: '{}'", url)))?; + let host = host.parse::()?; + let port = url.port_or_known_default(); + let descriptor = NetDescriptor(host, port); + self.check_desc(Some(&descriptor), false, api_name, || { + Some(format!("\"{descriptor}\"")) }) } @@ -1782,7 +1849,9 @@ impl PermissionsContainer { host: &(T, Option), api_name: &str, ) -> Result<(), AnyError> { - self.0.lock().net.check(host, Some(api_name)) + let hostname = host.0.as_ref().parse::()?; + let descriptor = NetDescriptor(hostname, host.1); + self.0.lock().net.check(&descriptor, Some(api_name)) } #[inline(always)] @@ -2209,7 +2278,9 @@ pub fn create_child_permissions( mod tests { use super::*; use deno_core::serde_json::json; + use fqdn::fqdn; use prompter::tests::*; + use std::net::Ipv4Addr; // Creates vector of strings, Vec macro_rules! svec { @@ -2363,12 +2434,12 @@ mod tests { ]; for (host, port, is_ok) in domain_tests { + let host = host.parse().unwrap(); + let descriptor = NetDescriptor(host, Some(port)); assert_eq!( is_ok, - perms.net.check(&(host, Some(port)), None).is_ok(), - "{}:{}", - host, - port, + perms.net.check(&descriptor, None).is_ok(), + "{descriptor}", ); } } @@ -2404,8 +2475,13 @@ mod tests { ("192.168.0.1", 0), ]; - for (host, port) in domain_tests { - assert!(perms.net.check(&(host, Some(port)), None).is_ok()); + for (host_str, port) in domain_tests { + let host = host_str.parse().unwrap(); + let descriptor = NetDescriptor(host, Some(port)); + assert!( + perms.net.check(&descriptor, None).is_ok(), + "expected {host_str}:{port} to pass" + ); } } @@ -2440,8 +2516,13 @@ mod tests { ("192.168.0.1", 0), ]; - for (host, port) in domain_tests { - assert!(perms.net.check(&(host, Some(port)), None).is_err()); + for (host_str, port) in domain_tests { + let host = host_str.parse().unwrap(); + let descriptor = NetDescriptor(host, Some(port)); + assert!( + perms.net.check(&descriptor, None).is_err(), + "expected {host_str}:{port} to fail" + ); } } @@ -2716,15 +2797,15 @@ mod tests { assert_eq!(perms4.ffi.query(Some(Path::new("/foo"))), PermissionState::Denied); assert_eq!(perms4.ffi.query(Some(Path::new("/foo/bar"))), PermissionState::Denied); assert_eq!(perms4.ffi.query(Some(Path::new("/bar"))), PermissionState::Granted); - assert_eq!(perms1.net.query::<&str>(None), PermissionState::Granted); - assert_eq!(perms1.net.query(Some(&("127.0.0.1", None))), PermissionState::Granted); - assert_eq!(perms2.net.query::<&str>(None), PermissionState::Prompt); - assert_eq!(perms2.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); - assert_eq!(perms3.net.query::<&str>(None), PermissionState::Prompt); - assert_eq!(perms3.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Denied); - assert_eq!(perms4.net.query::<&str>(None), PermissionState::GrantedPartial); - assert_eq!(perms4.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Denied); - assert_eq!(perms4.net.query(Some(&("192.168.0.1", Some(8000)))), PermissionState::Granted); + assert_eq!(perms1.net.query(None), PermissionState::Granted); + assert_eq!(perms1.net.query(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), None))), PermissionState::Granted); + assert_eq!(perms2.net.query(None), PermissionState::Prompt); + assert_eq!(perms2.net.query(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)))), PermissionState::Granted); + assert_eq!(perms3.net.query(None), PermissionState::Prompt); + assert_eq!(perms3.net.query(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)))), PermissionState::Denied); + assert_eq!(perms4.net.query(None), PermissionState::GrantedPartial); + assert_eq!(perms4.net.query(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)))), PermissionState::Denied); + assert_eq!(perms4.net.query(Some(&NetDescriptor("192.168.0.1".parse().unwrap(), Some(8000)))), PermissionState::Granted); assert_eq!(perms1.env.query(None), PermissionState::Granted); assert_eq!(perms1.env.query(Some("HOME")), PermissionState::Granted); assert_eq!(perms2.env.query(None), PermissionState::Prompt); @@ -2782,9 +2863,9 @@ mod tests { prompt_value.set(true); assert_eq!(perms.ffi.request(None), PermissionState::Denied); prompt_value.set(true); - assert_eq!(perms.net.request(Some(&("127.0.0.1", None))), PermissionState::Granted); + assert_eq!(perms.net.request(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), None))), PermissionState::Granted); prompt_value.set(false); - assert_eq!(perms.net.request(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); + assert_eq!(perms.net.request(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)))), PermissionState::Granted); prompt_value.set(true); assert_eq!(perms.env.request(Some("HOME")), PermissionState::Granted); assert_eq!(perms.env.query(None), PermissionState::Prompt); @@ -2853,9 +2934,9 @@ mod tests { assert_eq!(perms.ffi.revoke(Some(Path::new("/foo/bar"))), PermissionState::Prompt); assert_eq!(perms.ffi.query(Some(Path::new("/foo"))), PermissionState::Prompt); assert_eq!(perms.ffi.query(Some(Path::new("/foo/baz"))), PermissionState::Granted); - assert_eq!(perms.net.revoke(Some(&("127.0.0.1", Some(9000)))), PermissionState::Prompt); - assert_eq!(perms.net.query(Some(&("127.0.0.1", None))), PermissionState::Prompt); - assert_eq!(perms.net.query(Some(&("127.0.0.1", Some(8000)))), PermissionState::Granted); + assert_eq!(perms.net.revoke(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), Some(9000)))), PermissionState::Prompt); + assert_eq!(perms.net.query(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), None))), PermissionState::Prompt); + assert_eq!(perms.net.query(Some(&NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)))), PermissionState::Granted); assert_eq!(perms.env.revoke(Some("HOME")), PermissionState::Prompt); assert_eq!(perms.env.revoke(Some("hostname")), PermissionState::Prompt); assert_eq!(perms.run.revoke(Some("deno")), PermissionState::Prompt); @@ -2888,13 +2969,43 @@ mod tests { assert!(perms.ffi.check(Path::new("/bar"), None).is_err()); prompt_value.set(true); - assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_ok()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)), + None + ) + .is_ok()); prompt_value.set(false); - assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_ok()); - assert!(perms.net.check(&("127.0.0.1", Some(8001)), None).is_err()); - assert!(perms.net.check(&("127.0.0.1", None), None).is_err()); - assert!(perms.net.check(&("deno.land", Some(8000)), None).is_err()); - assert!(perms.net.check(&("deno.land", None), None).is_err()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)), + None + ) + .is_ok()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8001)), + None + ) + .is_err()); + assert!(perms + .net + .check(&NetDescriptor("127.0.0.1".parse().unwrap(), None), None) + .is_err()); + assert!(perms + .net + .check( + &NetDescriptor("deno.land".parse().unwrap(), Some(8000)), + None + ) + .is_err()); + assert!(perms + .net + .check(&NetDescriptor("deno.land".parse().unwrap(), None), None) + .is_err()); prompt_value.set(true); assert!(perms.run.check("cat", None).is_ok()); @@ -2948,14 +3059,50 @@ mod tests { assert!(perms.ffi.check(Path::new("/bar"), None).is_ok()); prompt_value.set(false); - assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_err()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)), + None + ) + .is_err()); prompt_value.set(true); - assert!(perms.net.check(&("127.0.0.1", Some(8000)), None).is_err()); - assert!(perms.net.check(&("127.0.0.1", Some(8001)), None).is_ok()); - assert!(perms.net.check(&("deno.land", Some(8000)), None).is_ok()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8000)), + None + ) + .is_err()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8001)), + None + ) + .is_ok()); + assert!(perms + .net + .check( + &NetDescriptor("deno.land".parse().unwrap(), Some(8000)), + None + ) + .is_ok()); prompt_value.set(false); - assert!(perms.net.check(&("127.0.0.1", Some(8001)), None).is_ok()); - assert!(perms.net.check(&("deno.land", Some(8000)), None).is_ok()); + assert!(perms + .net + .check( + &NetDescriptor("127.0.0.1".parse().unwrap(), Some(8001)), + None + ) + .is_ok()); + assert!(perms + .net + .check( + &NetDescriptor("deno.land".parse().unwrap(), Some(8000)), + None + ) + .is_ok()); prompt_value.set(false); assert!(perms.run.check("cat", None).is_err()); @@ -3044,10 +3191,28 @@ mod tests { ..Permissions::none_without_prompt() }; - perms.net.check(&("allowed.domain.", None), None).unwrap(); - perms.net.check(&("1.1.1.1.", None), None).unwrap(); - assert!(perms.net.check(&("denied.domain.", None), None).is_err()); - assert!(perms.net.check(&("2.2.2.2.", None), None).is_err()); + perms + .net + .check( + &NetDescriptor("allowed.domain.".parse().unwrap(), None), + None, + ) + .unwrap(); + perms + .net + .check(&NetDescriptor("1.1.1.1".parse().unwrap(), None), None) + .unwrap(); + assert!(perms + .net + .check( + &NetDescriptor("denied.domain.".parse().unwrap(), None), + None + ) + .is_err()); + assert!(perms + .net + .check(&NetDescriptor("2.2.2.2".parse().unwrap(), None), None) + .is_err()); } #[test] @@ -3333,4 +3498,109 @@ mod tests { ) .is_err()); } + + #[test] + fn test_host_parse() { + let hosts = &[ + ("deno.land", Some(Host::Fqdn(fqdn!("deno.land")))), + ("DENO.land", Some(Host::Fqdn(fqdn!("deno.land")))), + ("deno.land.", Some(Host::Fqdn(fqdn!("deno.land")))), + ( + "1.1.1.1", + Some(Host::Ip(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)))), + ), + ( + "::1", + Some(Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))), + ), + ( + "[::1]", + Some(Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))), + ), + ("[::1", None), + ("::1]", None), + ("deno. land", None), + ("1. 1.1.1", None), + ("1.1.1.1.", None), + ("1::1.", None), + ("deno.land.", Some(Host::Fqdn(fqdn!("deno.land")))), + (".deno.land", None), + ( + "::ffff:1.1.1.1", + Some(Host::Ip(IpAddr::V6(Ipv6Addr::new( + 0, 0, 0, 0, 0, 0xffff, 0x0101, 0x0101, + )))), + ), + ]; + + for (host_str, expected) in hosts { + assert_eq!(host_str.parse::().ok(), *expected, "{host_str}"); + } + } + + #[test] + fn test_net_descriptor_parse() { + let cases = &[ + ( + "deno.land", + Some(NetDescriptor(Host::Fqdn(fqdn!("deno.land")), None)), + ), + ( + "DENO.land", + Some(NetDescriptor(Host::Fqdn(fqdn!("deno.land")), None)), + ), + ( + "deno.land:8000", + Some(NetDescriptor(Host::Fqdn(fqdn!("deno.land")), Some(8000))), + ), + ("deno.land:", None), + ("deno.land:a", None), + ("deno. land:a", None), + ("deno.land.: a", None), + ( + "1.1.1.1", + Some(NetDescriptor( + Host::Ip(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1))), + None, + )), + ), + ("1.1.1.1.", None), + ("1.1.1.1..", None), + ( + "1.1.1.1:8000", + Some(NetDescriptor( + Host::Ip(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1))), + Some(8000), + )), + ), + ("::", None), + (":::80", None), + ("::80", None), + ( + "[::]", + Some(NetDescriptor( + Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0))), + None, + )), + ), + ("[::1", None), + ("::1]", None), + ("::1]", None), + ("[::1]:", None), + ("[::1]:a", None), + ( + "[::1]:443", + Some(NetDescriptor( + Host::Ip(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))), + Some(443), + )), + ), + ("", None), + ("deno.land..", None), + ]; + + for (input, expected) in cases { + assert_eq!(input.parse::().ok(), *expected, "'{input}'"); + } + } } diff --git a/tests/testdata/dynamic_import/permissions_blob_remote.ts.out b/tests/testdata/dynamic_import/permissions_blob_remote.ts.out index a00c02d72f..f436a5eb84 100644 --- a/tests/testdata/dynamic_import/permissions_blob_remote.ts.out +++ b/tests/testdata/dynamic_import/permissions_blob_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in promise) TypeError: Requires net access to "example.com:443", run again with the --allow-net flag at blob:null/[WILDCARD]:1:8 await import(URL.createObjectURL(blob)); ^ diff --git a/tests/testdata/dynamic_import/permissions_data_remote.ts.out b/tests/testdata/dynamic_import/permissions_data_remote.ts.out index cb2a7ccf79..00248e2778 100644 --- a/tests/testdata/dynamic_import/permissions_data_remote.ts.out +++ b/tests/testdata/dynamic_import/permissions_data_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in promise) TypeError: Requires net access to "example.com:443", run again with the --allow-net flag at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8 await import(`data:application/javascript;base64,${btoa(code)}`); ^ diff --git a/tests/testdata/dynamic_import/permissions_remote_remote.ts.out b/tests/testdata/dynamic_import/permissions_remote_remote.ts.out index bd88dd4d98..0e8b0fc1f1 100644 --- a/tests/testdata/dynamic_import/permissions_remote_remote.ts.out +++ b/tests/testdata/dynamic_import/permissions_remote_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in promise) TypeError: Requires net access to "example.com:443", run again with the --allow-net flag at http://localhost:4545/dynamic_import/static_remote.ts:2:8 await import( ^ diff --git a/tests/testdata/workers/permissions_blob_remote.ts.out b/tests/testdata/workers/permissions_blob_remote.ts.out index 618f552dc1..6dc4f0f5fa 100644 --- a/tests/testdata/workers/permissions_blob_remote.ts.out +++ b/tests/testdata/workers/permissions_blob_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in worker "") Requires net access to "example.com:443", run again with the --allow-net flag at blob:null/[WILDCARD]:1:8 error: Uncaught (in promise) Error: Unhandled error in child worker. at Worker.#pollControl[WILDCARD] diff --git a/tests/testdata/workers/permissions_data_remote.ts.out b/tests/testdata/workers/permissions_data_remote.ts.out index 3f7c8cb634..e7af110bb5 100644 --- a/tests/testdata/workers/permissions_data_remote.ts.out +++ b/tests/testdata/workers/permissions_data_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in worker "") Requires net access to "example.com:443", run again with the --allow-net flag at data:application/javascript;base64,aW1wb3J0ICJodHRwczovL2V4YW1wbGUuY29tL3NvbWUvZmlsZS50cyI7:1:8 error: Uncaught (in promise) Error: Unhandled error in child worker. at Worker.#pollControl[WILDCARD] diff --git a/tests/testdata/workers/permissions_dynamic_remote.ts.out b/tests/testdata/workers/permissions_dynamic_remote.ts.out index 91f3cc6d5b..4fb2c2234f 100644 --- a/tests/testdata/workers/permissions_dynamic_remote.ts.out +++ b/tests/testdata/workers/permissions_dynamic_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in worker "") (in promise) TypeError: Requires net access to "example.com:443", run again with the --allow-net flag await import("" + "https://example.com/some/file.ts"); ^ at async http://localhost:4545/workers/dynamic_remote.ts:2:1 diff --git a/tests/testdata/workers/permissions_remote_remote.ts.out b/tests/testdata/workers/permissions_remote_remote.ts.out index bb065740aa..a095f1938b 100644 --- a/tests/testdata/workers/permissions_remote_remote.ts.out +++ b/tests/testdata/workers/permissions_remote_remote.ts.out @@ -1,4 +1,4 @@ -error: Uncaught (in worker "") Requires net access to "example.com", run again with the --allow-net flag +error: Uncaught (in worker "") Requires net access to "example.com:443", run again with the --allow-net flag at http://localhost:4545/workers/static_remote.ts:2:8 error: Uncaught (in promise) Error: Unhandled error in child worker. at Worker.#pollControl [WILDCARD]