1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 23:59:59 -05:00

refactor: add deno_permissions crate (#22236)

Issue https://github.com/denoland/deno/issues/22222


![image](https://github.com/denoland/deno/assets/34997667/2af8474b-b919-4519-98ce-9d29bc7829f2)

This PR moves `runtime/permissions` code to a upstream crate called
`deno_permissions`. The `deno_permissions::PermissionsContainer` is put
into the OpState and can be used instead of the current trait-based
permissions system.

For this PR, I've migrated `deno_fetch` to the new crate but kept the
rest of the trait-based system as a wrapper of `deno_permissions` crate.
Doing the migration all at once is error prone and hard to review.

Comparing incremental compile times for `ext/fetch` on Mac M1:

| profile | `cargo build --bin deno` | `cargo plonk build --bin deno` |
| --------- | ------------- | ------------------- |
| `debug`   | 20 s          | 0.8s                |
| `release` | 4 mins 12 s   | 1.4s                  |
This commit is contained in:
Divy Srivastava 2024-03-12 10:42:26 -07:00 committed by GitHub
parent 4a88695563
commit de28e6fc09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 381 additions and 210 deletions

17
Cargo.lock generated
View file

@ -1728,6 +1728,22 @@ dependencies = [
"thiserror",
]
[[package]]
name = "deno_permissions"
version = "0.1.0"
dependencies = [
"console_static_text",
"deno_core",
"deno_terminal",
"libc",
"log",
"once_cell",
"serde",
"termcolor",
"which 4.4.2",
"winapi",
]
[[package]]
name = "deno_runtime"
version = "0.149.0"
@ -1750,6 +1766,7 @@ dependencies = [
"deno_napi",
"deno_net",
"deno_node",
"deno_permissions",
"deno_terminal",
"deno_tls",
"deno_url",

View file

@ -28,6 +28,7 @@ members = [
"ext/websocket",
"ext/webstorage",
"runtime",
"runtime/permissions",
"tests",
"tests/ffi",
"tests/napi",
@ -48,6 +49,7 @@ deno_core = { version = "0.269.0", features = ["lazy_eval_snapshot"] }
deno_bench_util = { version = "0.135.0", path = "./bench_util" }
deno_lockfile = "0.19.0"
deno_media_type = { version = "0.1.1", features = ["module_specifier"] }
deno_permissions = { version = "0.1.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.149.0", path = "./runtime" }
deno_terminal = "0.1.1"
napi_sym = { version = "0.71.0", path = "./cli/napi/sym" }

View file

@ -56,7 +56,7 @@ pub fn op_pledge_test_permissions(
let token = Uuid::new_v4();
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = {
let mut parent_permissions = parent_permissions.0.lock();
let mut parent_permissions = parent_permissions.0 .0.lock();
let perms = create_child_permissions(&mut parent_permissions, args)?;
PermissionsContainer::new(perms)
};
@ -69,6 +69,7 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker
state.put(worker_permissions.0.clone());
state.put::<PermissionsContainer>(worker_permissions);
Ok(token)
@ -85,6 +86,7 @@ pub fn op_restore_test_permissions(
}
let permissions = permissions_holder.1;
state.put(permissions.0.clone());
state.put::<PermissionsContainer>(permissions);
Ok(())
} else {

View file

@ -57,7 +57,7 @@ pub fn op_pledge_test_permissions(
let token = Uuid::new_v4();
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = {
let mut parent_permissions = parent_permissions.0.lock();
let mut parent_permissions = parent_permissions.0 .0.lock();
let perms = create_child_permissions(&mut parent_permissions, args)?;
PermissionsContainer::new(perms)
};
@ -69,6 +69,7 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker
state.put(worker_permissions.0.clone());
state.put::<PermissionsContainer>(worker_permissions);
Ok(token)
@ -85,6 +86,7 @@ pub fn op_restore_test_permissions(
}
let permissions = permissions_holder.1;
state.put(permissions.0.clone());
state.put::<PermissionsContainer>(permissions);
Ok(())
} else {

View file

@ -168,15 +168,6 @@ impl FetchHandler for DefaultFileFetchHandler {
}
}
pub trait FetchPermissions {
fn check_net_url(
&mut self,
_url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
fn check_read(&mut self, _p: &Path, api_name: &str) -> Result<(), AnyError>;
}
pub fn get_declaration() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_fetch.d.ts")
}
@ -268,6 +259,15 @@ impl Drop for ResourceToBodyAdapter {
}
}
pub trait FetchPermissions {
fn check_net_url(
&mut self,
_url: &Url,
api_name: &str,
) -> Result<(), AnyError>;
fn check_read(&mut self, _p: &Path, api_name: &str) -> Result<(), AnyError>;
}
#[op2]
#[serde]
#[allow(clippy::too_many_arguments)]

View file

@ -90,6 +90,7 @@ deno_kv.workspace = true
deno_napi.workspace = true
deno_net.workspace = true
deno_node.workspace = true
deno_permissions.workspace = true
deno_terminal.workspace = true
deno_tls.workspace = true
deno_url.workspace = true

View file

@ -16,6 +16,8 @@ pub use deno_kv;
pub use deno_napi;
pub use deno_net;
pub use deno_node;
pub use deno_permissions;
pub use deno_terminal::colors;
pub use deno_tls;
pub use deno_url;
pub use deno_web;

View file

@ -1,8 +1,8 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::permissions::parse_sys_kind;
use crate::permissions::PermissionState;
use crate::permissions::PermissionsContainer;
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;

View file

@ -153,7 +153,7 @@ fn op_create_worker(
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = if let Some(child_permissions_arg) = args.permissions
{
let mut parent_permissions = parent_permissions.0.lock();
let mut parent_permissions = parent_permissions.0 .0.lock();
let perms =
create_child_permissions(&mut parent_permissions, child_permissions_arg)?;
PermissionsContainer::new(perms)

238
runtime/permissions.rs Normal file
View file

@ -0,0 +1,238 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::path::Path;
use deno_core::error::AnyError;
use deno_core::url::Url;
pub use deno_permissions::create_child_permissions;
pub use deno_permissions::parse_sys_kind;
pub use deno_permissions::set_prompt_callbacks;
pub use deno_permissions::ChildPermissionsArg;
pub use deno_permissions::Permissions;
pub use deno_permissions::PermissionsOptions;
// NOTE: Temporary permissions container to satisfy traits. We are migrating to the deno_permissions
// crate.
#[derive(Debug, Clone)]
pub struct PermissionsContainer(pub deno_permissions::PermissionsContainer);
impl PermissionsContainer {
pub fn new(permissions: deno_permissions::Permissions) -> Self {
Self(deno_permissions::PermissionsContainer::new(permissions))
}
pub fn allow_all() -> Self {
Self(deno_permissions::PermissionsContainer::allow_all())
}
}
impl std::ops::Deref for PermissionsContainer {
type Target = deno_permissions::PermissionsContainer;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::DerefMut for PermissionsContainer {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl deno_node::NodePermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
&mut self,
url: &Url,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_net_url(url, api_name)
}
#[inline(always)]
fn check_read_with_api_name(
&self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
self.0.check_read_with_api_name(path, api_name)
}
#[inline(always)]
fn check_write_with_api_name(
&self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
self.0.check_write_with_api_name(path, api_name)
}
fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
self.0.check_sys(kind, api_name)
}
}
impl deno_fetch::FetchPermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
&mut self,
url: &Url,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_net_url(url, api_name)
}
#[inline(always)]
fn check_read(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_read(path, api_name)
}
}
impl deno_net::NetPermissions for PermissionsContainer {
#[inline(always)]
fn check_net<T: AsRef<str>>(
&mut self,
host: &(T, Option<u16>),
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_net(host, api_name)
}
#[inline(always)]
fn check_read(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_read(path, api_name)
}
#[inline(always)]
fn check_write(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_write(path, api_name)
}
}
impl deno_web::TimersPermission for PermissionsContainer {
#[inline(always)]
fn allow_hrtime(&mut self) -> bool {
self.0.allow_hrtime()
}
}
impl deno_websocket::WebSocketPermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
&mut self,
url: &Url,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_net_url(url, api_name)
}
}
impl deno_fs::FsPermissions for PermissionsContainer {
fn check_read(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_read(path, api_name)
}
fn check_read_blind(
&mut self,
path: &Path,
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_read_blind(path, display, api_name)
}
fn check_write(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_write(path, api_name)
}
fn check_write_partial(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_write_partial(path, api_name)
}
fn check_write_blind(
&mut self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_write_blind(p, display, api_name)
}
fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError> {
self.0.check_read_all(api_name)
}
fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError> {
self.0.check_write_all(api_name)
}
}
// NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might
// change in the future.
impl deno_napi::NapiPermissions for PermissionsContainer {
#[inline(always)]
fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.0.check_ffi(path)
}
}
impl deno_ffi::FfiPermissions for PermissionsContainer {
#[inline(always)]
fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.0.check_ffi_partial(path)
}
}
impl deno_kv::sqlite::SqliteDbHandlerPermissions for PermissionsContainer {
#[inline(always)]
fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
self.0.check_read(p, api_name)
}
#[inline(always)]
fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
self.0.check_write(p, api_name)
}
}
impl deno_kv::remote::RemoteDbHandlerPermissions for PermissionsContainer {
#[inline(always)]
fn check_env(&mut self, var: &str) -> Result<(), AnyError> {
self.0.check_env(var)
}
#[inline(always)]
fn check_net_url(
&mut self,
url: &Url,
api_name: &str,
) -> Result<(), AnyError> {
self.0.check_net_url(url, api_name)
}
}

View file

@ -0,0 +1,28 @@
# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
[package]
name = "deno_permissions"
version = "0.1.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
description = "Provides the deno permissions implementation."
[lib]
name = "deno_permissions"
path = "lib.rs"
[dependencies]
console_static_text.workspace = true
deno_core.workspace = true
deno_terminal.workspace = true
libc.workspace = true
log.workspace = true
once_cell.workspace = true
serde.workspace = true
termcolor.workspace = true
which = "4.2.5"
[target.'cfg(windows)'.dependencies]
winapi = { workspace = true, features = ["commapi", "knownfolders", "mswsock", "objbase", "psapi", "shlobj", "tlhelp32", "winbase", "winerror", "winuser", "winsock2"] }

View file

View file

@ -1,10 +1,11 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::fs_util::resolve_from_cwd;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
use deno_core::error::type_error;
use deno_core::error::uri_error;
use deno_core::error::AnyError;
use deno_core::normalize_path;
use deno_core::parking_lot::Mutex;
use deno_core::serde::de;
use deno_core::serde::Deserialize;
@ -15,7 +16,6 @@ use deno_core::url;
use deno_core::url::Url;
use deno_core::ModuleSpecifier;
use deno_terminal::colors;
use log;
use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::collections::HashSet;
@ -28,7 +28,7 @@ use std::string::ToString;
use std::sync::Arc;
use which::which;
mod prompter;
pub mod prompter;
use prompter::permission_prompt;
use prompter::PromptResponse;
use prompter::PERMISSION_EMOJI;
@ -36,6 +36,18 @@ use prompter::PERMISSION_EMOJI;
pub use prompter::set_prompt_callbacks;
pub use prompter::PromptCallback;
#[inline]
fn resolve_from_cwd(path: &Path) -> Result<PathBuf, AnyError> {
if path.is_absolute() {
Ok(normalize_path(path))
} else {
#[allow(clippy::disallowed_methods)]
let cwd = std::env::current_dir()
.context("Failed to get current working directory")?;
Ok(normalize_path(cwd.join(path)))
}
}
static DEBUG_LOG_ENABLED: Lazy<bool> =
Lazy::new(|| log::log_enabled!(log::Level::Debug));
@ -1271,6 +1283,11 @@ impl PermissionsContainer {
Self(Arc::new(Mutex::new(perms)))
}
#[inline(always)]
pub fn allow_hrtime(&mut self) -> bool {
self.0.lock().hrtime.check().is_ok()
}
pub fn allow_all() -> Self {
Self::new(Permissions::allow_all())
}
@ -1292,6 +1309,15 @@ impl PermissionsContainer {
self.0.lock().read.check(path, Some(api_name))
}
#[inline(always)]
pub fn check_read_with_api_name(
&self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
self.0.lock().read.check(path, api_name)
}
#[inline(always)]
pub fn check_read_blind(
&mut self,
@ -1316,6 +1342,15 @@ impl PermissionsContainer {
self.0.lock().write.check(path, Some(api_name))
}
#[inline(always)]
pub fn check_write_with_api_name(
&self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
self.0.lock().write.check(path, api_name)
}
#[inline(always)]
pub fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError> {
self.0.lock().write.check_all(Some(api_name))
@ -1331,6 +1366,15 @@ impl PermissionsContainer {
self.0.lock().write.check_blind(path, display, api_name)
}
#[inline(always)]
pub fn check_write_partial(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().write.check_partial(path, Some(api_name))
}
#[inline(always)]
pub fn check_run(
&mut self,
@ -1346,11 +1390,7 @@ impl PermissionsContainer {
}
#[inline(always)]
pub fn check_sys(
&mut self,
kind: &str,
api_name: &str,
) -> Result<(), AnyError> {
pub fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
self.0.lock().sys.check(kind, Some(api_name))
}
@ -1363,11 +1403,9 @@ impl PermissionsContainer {
pub fn check_env_all(&mut self) -> Result<(), AnyError> {
self.0.lock().env.check_all()
}
}
impl deno_node::NodePermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
pub fn check_net_url(
&mut self,
url: &Url,
api_name: &str,
@ -1376,31 +1414,7 @@ impl deno_node::NodePermissions for PermissionsContainer {
}
#[inline(always)]
fn check_read_with_api_name(
&self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
self.0.lock().read.check(path, api_name)
}
#[inline(always)]
fn check_write_with_api_name(
&self,
path: &Path,
api_name: Option<&str>,
) -> Result<(), AnyError> {
self.0.lock().write.check(path, api_name)
}
fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
self.0.lock().sys.check(kind, Some(api_name))
}
}
impl deno_net::NetPermissions for PermissionsContainer {
#[inline(always)]
fn check_net<T: AsRef<str>>(
pub fn check_net<T: AsRef<str>>(
&mut self,
host: &(T, Option<u16>),
api_name: &str,
@ -1409,155 +1423,16 @@ impl deno_net::NetPermissions for PermissionsContainer {
}
#[inline(always)]
fn check_read(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().read.check(path, Some(api_name))
}
#[inline(always)]
fn check_write(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().write.check(path, Some(api_name))
}
}
impl deno_fetch::FetchPermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
&mut self,
url: &url::Url,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().net.check_url(url, Some(api_name))
}
#[inline(always)]
fn check_read(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().read.check(path, Some(api_name))
}
}
impl deno_web::TimersPermission for PermissionsContainer {
#[inline(always)]
fn allow_hrtime(&mut self) -> bool {
self.0.lock().hrtime.check().is_ok()
}
}
impl deno_websocket::WebSocketPermissions for PermissionsContainer {
#[inline(always)]
fn check_net_url(
&mut self,
url: &url::Url,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().net.check_url(url, Some(api_name))
}
}
impl deno_fs::FsPermissions for PermissionsContainer {
fn check_read(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().read.check(path, Some(api_name))
}
fn check_read_blind(
&mut self,
path: &Path,
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().read.check_blind(path, display, api_name)
}
fn check_write(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().write.check(path, Some(api_name))
}
fn check_write_partial(
&mut self,
path: &Path,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().write.check_partial(path, Some(api_name))
}
fn check_write_blind(
&mut self,
p: &Path,
display: &str,
api_name: &str,
) -> Result<(), AnyError> {
self.0.lock().write.check_blind(p, display, api_name)
}
fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError> {
self.0.lock().read.check_all(Some(api_name))
}
fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError> {
self.0.lock().write.check_all(Some(api_name))
}
}
// NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might
// change in the future.
impl deno_napi::NapiPermissions for PermissionsContainer {
#[inline(always)]
fn check(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
pub fn check_ffi(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.0.lock().ffi.check(path.unwrap(), None)
}
}
impl deno_ffi::FfiPermissions for PermissionsContainer {
#[inline(always)]
fn check_partial(&mut self, path: Option<&Path>) -> Result<(), AnyError> {
self.0.lock().ffi.check_partial(path)
}
}
impl deno_kv::sqlite::SqliteDbHandlerPermissions for PermissionsContainer {
#[inline(always)]
fn check_read(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
self.0.lock().read.check(p, Some(api_name))
}
#[inline(always)]
fn check_write(&mut self, p: &Path, api_name: &str) -> Result<(), AnyError> {
self.0.lock().write.check(p, Some(api_name))
}
}
impl deno_kv::remote::RemoteDbHandlerPermissions for PermissionsContainer {
#[inline(always)]
fn check_env(&mut self, var: &str) -> Result<(), AnyError> {
self.0.lock().env.check(var)
}
#[inline(always)]
fn check_net_url(
pub fn check_ffi_partial(
&mut self,
url: &url::Url,
api_name: &str,
path: Option<&Path>,
) -> Result<(), AnyError> {
self.0.lock().net.check_url(url, Some(api_name))
self.0.lock().ffi.check_partial(path)
}
}

View file

@ -19,6 +19,22 @@ use std::sync::Arc;
#[derive(Clone)]
struct Permissions;
impl deno_websocket::WebSocketPermissions for Permissions {
fn check_net_url(
&mut self,
_url: &deno_core::url::Url,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
}
impl deno_web::TimersPermission for Permissions {
fn allow_hrtime(&mut self) -> bool {
unreachable!("snapshotting!")
}
}
impl deno_fetch::FetchPermissions for Permissions {
fn check_net_url(
&mut self,
@ -37,22 +53,6 @@ impl deno_fetch::FetchPermissions for Permissions {
}
}
impl deno_websocket::WebSocketPermissions for Permissions {
fn check_net_url(
&mut self,
_url: &deno_core::url::Url,
_api_name: &str,
) -> Result<(), deno_core::error::AnyError> {
unreachable!("snapshotting!")
}
}
impl deno_web::TimersPermission for Permissions {
fn allow_hrtime(&mut self) -> bool {
unreachable!("snapshotting!")
}
}
impl deno_ffi::FfiPermissions for Permissions {
fn check_partial(
&mut self,

View file

@ -390,6 +390,7 @@ impl WebWorker {
enable_testing_features: bool,
},
state = |state, options| {
state.put(options.permissions.0.clone());
state.put::<PermissionsContainer>(options.permissions);
state.put(ops::TestingFeaturesEnabled(options.enable_testing_features));
},

View file

@ -315,6 +315,9 @@ impl MainWorker {
enable_testing_features: bool,
},
state = |state, options| {
// Save the permissions container and the wrapper.
state.put(options.permissions.0.clone());
// This is temporary until we migrate all exts/ to the deno_permissions crate.
state.put::<PermissionsContainer>(options.permissions);
state.put(ops::TestingFeaturesEnabled(options.enable_testing_features));
},