mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-03 10:11:15 +02:00
perf: completely remove endpoints if none of its APIs is allowlisted (#3958)
This commit is contained in:
committed by
GitHub
parent
24e4ff208e
commit
ed467c275b
@@ -141,8 +141,10 @@ impl WebviewIdStore {
|
||||
#[macro_export]
|
||||
macro_rules! getter {
|
||||
($self: ident, $rx: expr, $message: expr) => {{
|
||||
crate::send_user_message(&$self.context, $message)?;
|
||||
$rx.recv().map_err(|_| crate::Error::FailedToReceiveMessage)
|
||||
$crate::send_user_message(&$self.context, $message)?;
|
||||
$rx
|
||||
.recv()
|
||||
.map_err(|_| $crate::Error::FailedToReceiveMessage)
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
@@ -104,7 +104,8 @@ version = "0.30.0"
|
||||
features = [ "Win32_Foundation" ]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1.1"
|
||||
heck = "0.4"
|
||||
once_cell = "1.10"
|
||||
|
||||
[dev-dependencies]
|
||||
mockito = "0.31"
|
||||
|
||||
@@ -2,108 +2,160 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use cfg_aliases::cfg_aliases;
|
||||
use heck::ToSnakeCase;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
fn main() {
|
||||
cfg_aliases! {
|
||||
custom_protocol: { feature = "custom-protocol" },
|
||||
dev: { not(feature = "custom-protocol") },
|
||||
updater: { any(feature = "updater", feature = "__updater-docs") },
|
||||
use std::{path::Path, sync::Mutex};
|
||||
|
||||
api_all: { feature = "api-all" },
|
||||
static CHECKED_FEATURES: OnceCell<Mutex<Vec<String>>> = OnceCell::new();
|
||||
|
||||
// fs
|
||||
fs_all: { any(api_all, feature = "fs-all") },
|
||||
fs_read_file: { any(fs_all, feature = "fs-read-file") },
|
||||
fs_write_file: { any(fs_all, feature = "fs-write-file") },
|
||||
fs_write_binary_file: { any(fs_all, feature = "fs-write-binary-file") },
|
||||
fs_read_dir: { any(fs_all, feature = "fs-read-dir") },
|
||||
fs_copy_file: { any(fs_all, feature = "fs-copy-file") },
|
||||
fs_create_dir: { any(fs_all, feature = "fs-create_dir") },
|
||||
fs_remove_dir: { any(fs_all, feature = "fs-remove-dir") },
|
||||
fs_remove_file: { any(fs_all, feature = "fs-remove-file") },
|
||||
fs_rename_file: { any(fs_all, feature = "fs-rename-file") },
|
||||
// checks if the given Cargo feature is enabled.
|
||||
fn has_feature(feature: &str) -> bool {
|
||||
CHECKED_FEATURES
|
||||
.get_or_init(Default::default)
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push(feature.to_string());
|
||||
|
||||
// window
|
||||
window_all: { any(api_all, feature = "window-all") },
|
||||
window_create: { any(window_all, feature = "window-create") },
|
||||
window_center: { any(window_all, feature = "window-center") },
|
||||
window_request_user_attention: { any(window_all, feature = "window-request-user-attention") },
|
||||
window_set_resizable: { any(window_all, feature = "window-set-resizable") },
|
||||
window_set_title: { any(window_all, feature = "window-set-title") },
|
||||
window_maximize: { any(window_all, feature = "window-maximize") },
|
||||
window_unmaximize: { any(window_all, feature = "window-unmaximize") },
|
||||
window_minimize: { any(window_all, feature = "window-minimize") },
|
||||
window_unminimize: { any(window_all, feature = "window-unminimize") },
|
||||
window_show: { any(window_all, feature = "window-show") },
|
||||
window_hide: { any(window_all, feature = "window-hide") },
|
||||
window_close: { any(window_all, feature = "window-close") },
|
||||
window_set_decorations: { any(window_all, feature = "window-set-decorations") },
|
||||
window_set_always_on_top: { any(window_all, feature = "window-set-always-on-top") },
|
||||
window_set_size: { any(window_all, feature = "window-set-size") },
|
||||
window_set_min_size: { any(window_all, feature = "window-set-min-size") },
|
||||
window_set_max_size: { any(window_all, feature = "window-set-max-size") },
|
||||
window_set_position: { any(window_all, feature = "window-set-position") },
|
||||
window_set_fullscreen: { any(window_all, feature = "window-set-fullscreen") },
|
||||
window_set_focus: { any(window_all, feature = "window-set-focus") },
|
||||
window_set_icon: { any(window_all, feature = "window-set-icon") },
|
||||
window_set_skip_taskbar: { any(window_all, feature = "window-set-skip-taskbar") },
|
||||
window_set_cursor_grab: { any(window_all, feature = "window-set-cursor-grab") },
|
||||
window_set_cursor_visible: { any(window_all, feature = "window-set-cursor-visible") },
|
||||
window_set_cursor_icon: { any(window_all, feature = "window-set-cursor-icon") },
|
||||
window_set_cursor_position: { any(window_all, feature = "window-set-cursor-position") },
|
||||
window_start_dragging: { any(window_all, feature = "window-start-dragging") },
|
||||
window_print: { any(window_all, feature = "window-print") },
|
||||
// when a feature is enabled, Cargo sets the `CARGO_FEATURE_<name` env var to 1
|
||||
// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
|
||||
std::env::var(format!(
|
||||
"CARGO_FEATURE_{}",
|
||||
feature.to_uppercase().to_snake_case()
|
||||
))
|
||||
.map(|x| x == "1")
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
// shell
|
||||
shell_all: { any(api_all, feature = "shell-all") },
|
||||
shell_execute: { any(shell_all, feature = "shell-execute") },
|
||||
shell_sidecar: { any(shell_all, feature = "shell-sidecar") },
|
||||
shell_open: { any(shell_all, feature = "shell-open") },
|
||||
// helper for the command module macro
|
||||
shell_script: { any(shell_execute, shell_sidecar) },
|
||||
// helper for the shell scope functionality
|
||||
shell_scope: { any(shell_execute, shell_sidecar, feature = "shell-open-api") },
|
||||
|
||||
// dialog
|
||||
dialog_all: { any(api_all, feature = "dialog-all") },
|
||||
dialog_open: { any(dialog_all, feature = "dialog-open") },
|
||||
dialog_save: { any(dialog_all, feature = "dialog-save") },
|
||||
dialog_message: { any(dialog_all, feature = "dialog-message") },
|
||||
dialog_ask: { any(dialog_all, feature = "dialog-ask") },
|
||||
dialog_confirm: { any(dialog_all, feature = "dialog-confirm") },
|
||||
|
||||
// http
|
||||
http_all: { any(api_all, feature = "http-all") },
|
||||
http_request: { any(http_all, feature = "http-request") },
|
||||
|
||||
// cli
|
||||
cli: { feature = "cli" },
|
||||
|
||||
// notification
|
||||
notification_all: { any(api_all, feature = "notification-all") },
|
||||
|
||||
// global shortcut
|
||||
global_shortcut_all: { any(api_all, feature = "global_shortcut-all") },
|
||||
|
||||
// os
|
||||
os_all: { any(api_all, feature = "os-all") },
|
||||
|
||||
// path
|
||||
path_all: { any(api_all, feature = "path-all") },
|
||||
|
||||
// protocol
|
||||
protocol_all: { any(api_all, feature = "protocol-all") },
|
||||
protocol_asset: { any(protocol_all, feature = "protocol-asset") },
|
||||
|
||||
// process
|
||||
process_all: { any(api_all, feature = "process-all") },
|
||||
process_relaunch: { any(protocol_all, feature = "process-relaunch") },
|
||||
process_exit: { any(protocol_all, feature = "process-exit") },
|
||||
|
||||
// clipboard
|
||||
clipboard_all: { any(api_all, feature = "clipboard-all") },
|
||||
clipboard_write_text: { any(protocol_all, feature = "clipboard-write-text") },
|
||||
clipboard_read_text: { any(protocol_all, feature = "clipboard-read-text") },
|
||||
// creates a cfg alias if `has_feature` is true.
|
||||
// `alias` must be a snake case string.
|
||||
fn alias(alias: &str, has_feature: bool) {
|
||||
if has_feature {
|
||||
println!("cargo:rustc-cfg={}", alias);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
alias("custom_protocol", has_feature("custom-protocol"));
|
||||
alias("dev", !has_feature("custom-protocol"));
|
||||
alias(
|
||||
"updater",
|
||||
has_feature("updater") || has_feature("__updater-docs"),
|
||||
);
|
||||
|
||||
let api_all = has_feature("api-all");
|
||||
alias("api_all", api_all);
|
||||
|
||||
alias_module(
|
||||
"fs",
|
||||
&[
|
||||
"read-file",
|
||||
"write-file",
|
||||
"read-dir",
|
||||
"copy-file",
|
||||
"create-dir",
|
||||
"remove-dir",
|
||||
"remove-file",
|
||||
"rename-file",
|
||||
],
|
||||
api_all,
|
||||
);
|
||||
|
||||
alias_module(
|
||||
"window",
|
||||
&[
|
||||
"create",
|
||||
"center",
|
||||
"request-user-attention",
|
||||
"set-resizable",
|
||||
"set-title",
|
||||
"maximize",
|
||||
"unmaximize",
|
||||
"minimize",
|
||||
"unminimize",
|
||||
"show",
|
||||
"hide",
|
||||
"close",
|
||||
"set-decorations",
|
||||
"set-always-on-top",
|
||||
"set-size",
|
||||
"set-min-size",
|
||||
"set-max-size",
|
||||
"set-position",
|
||||
"set-fullscreen",
|
||||
"set-focus",
|
||||
"set-icon",
|
||||
"set-skip-taskbar",
|
||||
"set-cursor-grab",
|
||||
"set-cursor-visible",
|
||||
"set-cursor-icon",
|
||||
"set-cursor-position",
|
||||
"start-dragging",
|
||||
"print",
|
||||
],
|
||||
api_all,
|
||||
);
|
||||
|
||||
alias_module("shell", &["execute", "sidecar", "open"], api_all);
|
||||
// helper for the command module macro
|
||||
let shell_script = has_feature("shell-execute") || has_feature("shell-sidecar");
|
||||
alias("shell_script", shell_script);
|
||||
alias("shell_scope", shell_script || has_feature("shell-open-api"));
|
||||
|
||||
alias_module(
|
||||
"dialog",
|
||||
&["open", "save", "message", "ask", "confirm"],
|
||||
api_all,
|
||||
);
|
||||
|
||||
alias_module("http", &["request"], api_all);
|
||||
|
||||
alias("cli", has_feature("cli"));
|
||||
|
||||
alias_module("notification", &[], api_all);
|
||||
alias_module("global-shortcut", &[], api_all);
|
||||
alias_module("os", &[], api_all);
|
||||
alias_module("path", &[], api_all);
|
||||
|
||||
alias_module("protocol", &["asset"], api_all);
|
||||
|
||||
alias_module("process", &["relaunch", "exit"], api_all);
|
||||
|
||||
alias_module("clipboard", &["write-text", "read-text"], api_all);
|
||||
|
||||
let checked_features_out_path =
|
||||
Path::new(&std::env::var("OUT_DIR").unwrap()).join("checked_features");
|
||||
std::fs::write(
|
||||
&checked_features_out_path,
|
||||
&CHECKED_FEATURES.get().unwrap().lock().unwrap().join(","),
|
||||
)
|
||||
.expect("failed to write checked_features file");
|
||||
}
|
||||
|
||||
// create aliases for the given module with its apis.
|
||||
// each api is translated into a feature flag in the format of `<module>-<api>`
|
||||
// and aliased as `<module_snake_case>_<api_snake_case>`.
|
||||
//
|
||||
// The `<module>-all` feature is also aliased to `<module>_all`.
|
||||
//
|
||||
// If any of the features is enabled, the `<module_snake_case>_any` alias is created.
|
||||
//
|
||||
// Note that both `module` and `apis` strings must be written in kebab case.
|
||||
fn alias_module(module: &str, apis: &[&str], api_all: bool) {
|
||||
let all_feature_name = format!("{}-all", module);
|
||||
let all = api_all || has_feature(&all_feature_name);
|
||||
alias(&all_feature_name.to_snake_case(), all);
|
||||
|
||||
let mut any = all;
|
||||
|
||||
for api in apis {
|
||||
let has = all || has_feature(&format!("{}-{}", module, api));
|
||||
alias(
|
||||
&format!("{}_{}", module.to_snake_case(), api.to_snake_case()),
|
||||
has,
|
||||
);
|
||||
any = any || has;
|
||||
}
|
||||
|
||||
alias(&format!("{}_any", module.to_snake_case()), any);
|
||||
}
|
||||
|
||||
@@ -34,10 +34,12 @@ impl SafePathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn new_unchecked(path: std::path::PathBuf) -> Self {
|
||||
Self(path)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn display(&self) -> Display<'_> {
|
||||
self.0.display()
|
||||
}
|
||||
|
||||
@@ -13,17 +13,27 @@ use serde_json::Value as JsonValue;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod app;
|
||||
#[cfg(cli)]
|
||||
mod cli;
|
||||
#[cfg(clipboard_any)]
|
||||
mod clipboard;
|
||||
#[cfg(dialog_any)]
|
||||
mod dialog;
|
||||
mod event;
|
||||
#[cfg(fs_any)]
|
||||
mod file_system;
|
||||
#[cfg(global_shortcut_any)]
|
||||
mod global_shortcut;
|
||||
#[cfg(http_any)]
|
||||
mod http;
|
||||
mod notification;
|
||||
#[cfg(os_any)]
|
||||
mod operating_system;
|
||||
#[cfg(path_any)]
|
||||
mod path;
|
||||
#[cfg(process_any)]
|
||||
mod process;
|
||||
#[cfg(shell_any)]
|
||||
mod shell;
|
||||
mod window;
|
||||
|
||||
@@ -62,18 +72,28 @@ impl<T: Serialize> From<T> for InvokeResponse {
|
||||
#[serde(tag = "module", content = "message")]
|
||||
enum Module {
|
||||
App(app::Cmd),
|
||||
#[cfg(process_any)]
|
||||
Process(process::Cmd),
|
||||
#[cfg(fs_any)]
|
||||
Fs(file_system::Cmd),
|
||||
#[cfg(os_any)]
|
||||
Os(operating_system::Cmd),
|
||||
#[cfg(path_any)]
|
||||
Path(path::Cmd),
|
||||
Window(Box<window::Cmd>),
|
||||
#[cfg(shell_any)]
|
||||
Shell(shell::Cmd),
|
||||
Event(event::Cmd),
|
||||
#[cfg(dialog_any)]
|
||||
Dialog(dialog::Cmd),
|
||||
#[cfg(cli)]
|
||||
Cli(cli::Cmd),
|
||||
Notification(notification::Cmd),
|
||||
#[cfg(http_any)]
|
||||
Http(http::Cmd),
|
||||
#[cfg(global_shortcut_any)]
|
||||
GlobalShortcut(global_shortcut::Cmd),
|
||||
#[cfg(clipboard_any)]
|
||||
Clipboard(clipboard::Cmd),
|
||||
}
|
||||
|
||||
@@ -97,24 +117,28 @@ impl Module {
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(process_any)]
|
||||
Self::Process(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(fs_any)]
|
||||
Self::Fs(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(path_any)]
|
||||
Self::Path(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(os_any)]
|
||||
Self::Os(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
@@ -128,6 +152,7 @@ impl Module {
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(shell_any)]
|
||||
Self::Shell(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
@@ -140,12 +165,14 @@ impl Module {
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(dialog_any)]
|
||||
Self::Dialog(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(cli)]
|
||||
Self::Cli(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
@@ -158,6 +185,7 @@ impl Module {
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(http_any)]
|
||||
Self::Http(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
@@ -165,12 +193,14 @@ impl Module {
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(global_shortcut_any)]
|
||||
Self::GlobalShortcut(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
.and_then(|r| r.json)
|
||||
.map_err(InvokeError::from_anyhow)
|
||||
}),
|
||||
#[cfg(clipboard_any)]
|
||||
Self::Clipboard(cmd) => resolver.respond_async(async move {
|
||||
cmd
|
||||
.run(context)
|
||||
@@ -195,11 +225,28 @@ pub(crate) fn handle<R: Runtime>(
|
||||
} = message;
|
||||
|
||||
if let JsonValue::Object(ref mut obj) = payload {
|
||||
obj.insert("module".to_string(), JsonValue::String(module));
|
||||
obj.insert("module".to_string(), JsonValue::String(module.clone()));
|
||||
}
|
||||
|
||||
match serde_json::from_value::<Module>(payload) {
|
||||
Ok(module) => module.run(window, resolver, config, package_info.clone()),
|
||||
Err(e) => resolver.reject(e.to_string()),
|
||||
Err(e) => {
|
||||
let message = e.to_string();
|
||||
if message.starts_with("unknown variant") {
|
||||
let mut s = message.split('`');
|
||||
s.next();
|
||||
if let Some(unknown_variant_name) = s.next() {
|
||||
if unknown_variant_name == module {
|
||||
return resolver.reject(format!(
|
||||
"The `{}` module is not enabled. You must enable one of its APIs in the allowlist.",
|
||||
module
|
||||
));
|
||||
} else if module == "Window" {
|
||||
return resolver.reject(window::into_allowlist_error(unknown_variant_name).to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
resolver.reject(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,38 +72,67 @@ pub enum WindowManagerCmd {
|
||||
AvailableMonitors,
|
||||
Theme,
|
||||
// Setters
|
||||
#[cfg(window_center)]
|
||||
Center,
|
||||
#[cfg(window_request_user_attention)]
|
||||
RequestUserAttention(Option<UserAttentionType>),
|
||||
#[cfg(window_set_resizable)]
|
||||
SetResizable(bool),
|
||||
#[cfg(window_set_title)]
|
||||
SetTitle(String),
|
||||
#[cfg(window_maximize)]
|
||||
Maximize,
|
||||
#[cfg(window_unmaximize)]
|
||||
Unmaximize,
|
||||
#[cfg(all(window_maximize, window_unmaximize))]
|
||||
ToggleMaximize,
|
||||
#[cfg(window_minimize)]
|
||||
Minimize,
|
||||
#[cfg(window_unminimize)]
|
||||
Unminimize,
|
||||
#[cfg(window_show)]
|
||||
Show,
|
||||
#[cfg(window_hide)]
|
||||
Hide,
|
||||
#[cfg(window_close)]
|
||||
Close,
|
||||
#[cfg(window_set_decorations)]
|
||||
SetDecorations(bool),
|
||||
#[cfg(window_set_always_on_top)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
SetAlwaysOnTop(bool),
|
||||
#[cfg(window_set_size)]
|
||||
SetSize(Size),
|
||||
#[cfg(window_set_min_size)]
|
||||
SetMinSize(Option<Size>),
|
||||
#[cfg(window_set_max_size)]
|
||||
SetMaxSize(Option<Size>),
|
||||
#[cfg(window_set_position)]
|
||||
SetPosition(Position),
|
||||
#[cfg(window_set_fullscreen)]
|
||||
SetFullscreen(bool),
|
||||
#[cfg(window_set_focus)]
|
||||
SetFocus,
|
||||
#[cfg(window_set_icon)]
|
||||
SetIcon {
|
||||
icon: IconDto,
|
||||
},
|
||||
#[cfg(window_set_skip_taskbar)]
|
||||
SetSkipTaskbar(bool),
|
||||
#[cfg(window_set_cursor_grab)]
|
||||
SetCursorGrab(bool),
|
||||
#[cfg(window_set_cursor_visible)]
|
||||
SetCursorVisible(bool),
|
||||
#[cfg(window_set_cursor_icon)]
|
||||
SetCursorIcon(CursorIcon),
|
||||
#[cfg(window_set_cursor_position)]
|
||||
SetCursorPosition(Position),
|
||||
#[cfg(window_start_dragging)]
|
||||
StartDragging,
|
||||
#[cfg(window_print)]
|
||||
Print,
|
||||
// internals
|
||||
#[cfg(all(window_maximize, window_unmaximize))]
|
||||
#[serde(rename = "__toggleMaximize")]
|
||||
InternalToggleMaximize,
|
||||
#[cfg(any(debug_assertions, feature = "devtools"))]
|
||||
@@ -111,61 +140,45 @@ pub enum WindowManagerCmd {
|
||||
InternalToggleDevtools,
|
||||
}
|
||||
|
||||
impl WindowManagerCmd {
|
||||
fn into_allowlist_error(self) -> crate::Error {
|
||||
match self {
|
||||
Self::Center => crate::Error::ApiNotAllowlisted("window > center".to_string()),
|
||||
Self::RequestUserAttention(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > requestUserAttention".to_string())
|
||||
}
|
||||
Self::SetResizable(_) => crate::Error::ApiNotAllowlisted("window > setResizable".to_string()),
|
||||
Self::SetTitle(_) => crate::Error::ApiNotAllowlisted("window > setTitle".to_string()),
|
||||
Self::Maximize => crate::Error::ApiNotAllowlisted("window > maximize".to_string()),
|
||||
Self::Unmaximize => crate::Error::ApiNotAllowlisted("window > unmaximize".to_string()),
|
||||
Self::ToggleMaximize => {
|
||||
crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string())
|
||||
}
|
||||
Self::Minimize => crate::Error::ApiNotAllowlisted("window > minimize".to_string()),
|
||||
Self::Unminimize => crate::Error::ApiNotAllowlisted("window > unminimize".to_string()),
|
||||
Self::Show => crate::Error::ApiNotAllowlisted("window > show".to_string()),
|
||||
Self::Hide => crate::Error::ApiNotAllowlisted("window > hide".to_string()),
|
||||
Self::Close => crate::Error::ApiNotAllowlisted("window > close".to_string()),
|
||||
Self::SetDecorations(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setDecorations".to_string())
|
||||
}
|
||||
Self::SetAlwaysOnTop(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setAlwaysOnTop".to_string())
|
||||
}
|
||||
Self::SetSize(_) => crate::Error::ApiNotAllowlisted("window > setSize".to_string()),
|
||||
Self::SetMinSize(_) => crate::Error::ApiNotAllowlisted("window > setMinSize".to_string()),
|
||||
Self::SetMaxSize(_) => crate::Error::ApiNotAllowlisted("window > setMaxSize".to_string()),
|
||||
Self::SetPosition(_) => crate::Error::ApiNotAllowlisted("window > setPosition".to_string()),
|
||||
Self::SetFullscreen(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setFullscreen".to_string())
|
||||
}
|
||||
Self::SetIcon { .. } => crate::Error::ApiNotAllowlisted("window > setIcon".to_string()),
|
||||
Self::SetSkipTaskbar(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setSkipTaskbar".to_string())
|
||||
}
|
||||
Self::SetCursorGrab(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setCursorGrab".to_string())
|
||||
}
|
||||
Self::SetCursorVisible(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setCursorVisible".to_string())
|
||||
}
|
||||
Self::SetCursorIcon(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setCursorIcon".to_string())
|
||||
}
|
||||
Self::SetCursorPosition(_) => {
|
||||
crate::Error::ApiNotAllowlisted("window > setCursorPosition".to_string())
|
||||
}
|
||||
Self::StartDragging => crate::Error::ApiNotAllowlisted("window > startDragging".to_string()),
|
||||
Self::Print => crate::Error::ApiNotAllowlisted("window > print".to_string()),
|
||||
Self::InternalToggleMaximize => {
|
||||
crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string())
|
||||
}
|
||||
_ => crate::Error::ApiNotAllowlisted("window > all".to_string()),
|
||||
pub fn into_allowlist_error(variant: &str) -> crate::Error {
|
||||
match variant {
|
||||
"center" => crate::Error::ApiNotAllowlisted("window > center".to_string()),
|
||||
"requestUserAttention" => {
|
||||
crate::Error::ApiNotAllowlisted("window > requestUserAttention".to_string())
|
||||
}
|
||||
"setResizable" => crate::Error::ApiNotAllowlisted("window > setResizable".to_string()),
|
||||
"setTitle" => crate::Error::ApiNotAllowlisted("window > setTitle".to_string()),
|
||||
"maximize" => crate::Error::ApiNotAllowlisted("window > maximize".to_string()),
|
||||
"unmaximize" => crate::Error::ApiNotAllowlisted("window > unmaximize".to_string()),
|
||||
"toggleMaximize" => {
|
||||
crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string())
|
||||
}
|
||||
"minimize" => crate::Error::ApiNotAllowlisted("window > minimize".to_string()),
|
||||
"nnminimize" => crate::Error::ApiNotAllowlisted("window > unminimize".to_string()),
|
||||
"show" => crate::Error::ApiNotAllowlisted("window > show".to_string()),
|
||||
"hide" => crate::Error::ApiNotAllowlisted("window > hide".to_string()),
|
||||
"close" => crate::Error::ApiNotAllowlisted("window > close".to_string()),
|
||||
"setDecorations" => crate::Error::ApiNotAllowlisted("window > setDecorations".to_string()),
|
||||
"setAlwaysOnTop" => crate::Error::ApiNotAllowlisted("window > setAlwaysOnTop".to_string()),
|
||||
"setSize" => crate::Error::ApiNotAllowlisted("window > setSize".to_string()),
|
||||
"setMinSize" => crate::Error::ApiNotAllowlisted("window > setMinSize".to_string()),
|
||||
"setMaxSize" => crate::Error::ApiNotAllowlisted("window > setMaxSize".to_string()),
|
||||
"setPosition" => crate::Error::ApiNotAllowlisted("window > setPosition".to_string()),
|
||||
"setFullscreen" => crate::Error::ApiNotAllowlisted("window > setFullscreen".to_string()),
|
||||
"setIcon" => crate::Error::ApiNotAllowlisted("window > setIcon".to_string()),
|
||||
"setSkipTaskbar" => crate::Error::ApiNotAllowlisted("window > setSkipTaskbar".to_string()),
|
||||
"setCursorGrab" => crate::Error::ApiNotAllowlisted("window > setCursorGrab".to_string()),
|
||||
"setCursorVisible" => crate::Error::ApiNotAllowlisted("window > setCursorVisible".to_string()),
|
||||
"setCursorIcon" => crate::Error::ApiNotAllowlisted("window > setCursorIcon".to_string()),
|
||||
"setCursorPosition" => {
|
||||
crate::Error::ApiNotAllowlisted("window > setCursorPosition".to_string())
|
||||
}
|
||||
"startDragging" => crate::Error::ApiNotAllowlisted("window > startDragging".to_string()),
|
||||
"print" => crate::Error::ApiNotAllowlisted("window > print".to_string()),
|
||||
"internalToggleMaximize" => {
|
||||
crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string())
|
||||
}
|
||||
_ => crate::Error::ApiNotAllowlisted("window".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,8 +331,6 @@ impl Cmd {
|
||||
window.open_devtools();
|
||||
}
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => return Err(cmd.into_allowlist_error()),
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
Ok(().into())
|
||||
|
||||
@@ -803,23 +803,100 @@ pub mod test;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use cargo_toml::Manifest;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::{env::var, fs::read_to_string, path::PathBuf};
|
||||
|
||||
static MANIFEST: OnceCell<Manifest> = OnceCell::new();
|
||||
const CHECKED_FEATURES: &str = include_str!(concat!(env!("OUT_DIR"), "/checked_features"));
|
||||
|
||||
fn get_manifest() -> &'static Manifest {
|
||||
MANIFEST.get_or_init(|| {
|
||||
let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let manifest = Manifest::from_path(manifest_dir.join("Cargo.toml"))
|
||||
.expect("failed to parse Cargo manifest");
|
||||
manifest
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn features_are_documented() {
|
||||
use cargo_toml::Manifest;
|
||||
use std::{env::var, fs::read_to_string, path::PathBuf};
|
||||
// this env var is always set by Cargo
|
||||
let manifest_dir = PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let manifest =
|
||||
Manifest::from_path(manifest_dir.join("Cargo.toml")).expect("failed to parse Cargo manifest");
|
||||
|
||||
let lib_code = read_to_string(manifest_dir.join("src/lib.rs")).expect("failed to read lib.rs");
|
||||
|
||||
for (f, _) in manifest.features {
|
||||
for (f, _) in &get_manifest().features {
|
||||
if !(f.starts_with("__") || f == "default" || lib_code.contains(&format!("*{}**", f))) {
|
||||
panic!("Feature {} is not documented", f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliased_features_exist() {
|
||||
let checked_features = CHECKED_FEATURES.split(',');
|
||||
let manifest = get_manifest();
|
||||
for checked_feature in checked_features {
|
||||
if !manifest.features.iter().any(|(f, _)| f == checked_feature) {
|
||||
panic!(
|
||||
"Feature {} was checked in the alias build step but it does not exist in core/tauri/Cargo.toml",
|
||||
checked_feature
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_allowlist_features_are_aliased() {
|
||||
let manifest = get_manifest();
|
||||
let all_modules = manifest
|
||||
.features
|
||||
.iter()
|
||||
.find(|(f, _)| f.as_str() == "api-all")
|
||||
.map(|(_, enabled)| enabled)
|
||||
.expect("api-all feature must exist");
|
||||
|
||||
let checked_features = CHECKED_FEATURES.split(',').collect::<Vec<&str>>();
|
||||
assert!(
|
||||
checked_features.contains(&"api-all"),
|
||||
"`api-all` is not aliased"
|
||||
);
|
||||
|
||||
// features that look like an allowlist feature, but are not
|
||||
let allowed = [
|
||||
"fs-extract-api",
|
||||
"http-api",
|
||||
"http-multipart",
|
||||
"process-command-api",
|
||||
"process-relaunch-dangerous-allow-symlink-macos",
|
||||
"window-data-url",
|
||||
];
|
||||
|
||||
for module_all_feature in all_modules {
|
||||
let module = module_all_feature.replace("-all", "");
|
||||
assert!(
|
||||
checked_features.contains(&module_all_feature.as_str()),
|
||||
"`{}` is not aliased",
|
||||
module
|
||||
);
|
||||
|
||||
let module_prefix = format!("{}-", module);
|
||||
// we assume that module features are the ones that start with `<module>-`
|
||||
// though it's not 100% accurate, we have an allowed list to fix it
|
||||
let module_features = manifest
|
||||
.features
|
||||
.iter()
|
||||
.map(|(f, _)| f)
|
||||
.filter(|f| f.starts_with(&module_prefix));
|
||||
for module_feature in module_features {
|
||||
assert!(
|
||||
allowed.contains(&module_feature.as_str())
|
||||
|| checked_features.contains(&module_feature.as_str()),
|
||||
"`{}` is not aliased",
|
||||
module_feature
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -605,8 +605,12 @@ impl<R: Runtime> Window<R> {
|
||||
|
||||
let invoke = Invoke { message, resolver };
|
||||
if let Some(module) = &payload.tauri_module {
|
||||
let module = module.to_string();
|
||||
crate::endpoints::handle(module, invoke, manager.config(), manager.package_info());
|
||||
crate::endpoints::handle(
|
||||
module.to_string(),
|
||||
invoke,
|
||||
manager.config(),
|
||||
manager.package_info(),
|
||||
);
|
||||
} else if payload.cmd.starts_with("plugin:") {
|
||||
manager.extend_api(invoke);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user