mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-04-11 10:43:31 +02:00
Compare commits
19 Commits
@tauri-app
...
1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
68d4397b6c | ||
|
|
8799060663 | ||
|
|
d2bb02fe3d | ||
|
|
33bff7db92 | ||
|
|
f7ae62140f | ||
|
|
0d529c9497 | ||
|
|
daf21c7199 | ||
|
|
fa90214b05 | ||
|
|
34e03b8455 | ||
|
|
f0602e7c29 | ||
|
|
10dda1900e | ||
|
|
ceb6972ec2 | ||
|
|
264c087b5e | ||
|
|
bcd9dc7f85 | ||
|
|
e7af22c9d9 | ||
|
|
c89ed86dbd | ||
|
|
90e56c454c | ||
|
|
0bd3a90178 | ||
|
|
bb17882908 |
@@ -5,7 +5,7 @@
|
||||
"rust": {
|
||||
"errorOnVersionRange": "^2.0.0-0",
|
||||
"version": true,
|
||||
"getPublishedVersion": "cargo search ${ pkgFile.pkg.package.name } --limit 1 | sed -nE \"s/^[^\\\"]*\\\"//; s/\\\".*//1p\"",
|
||||
"getPublishedVersion": "node ../../.scripts/covector/package-latest-version.js cargo ${ pkgFile.pkg.package.name } ${ pkgFile.pkg.package.version }",
|
||||
"prepublish": [
|
||||
"sudo apt-get update",
|
||||
"sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libayatana-appindicator3-dev librsvg2-dev patchelf",
|
||||
@@ -66,7 +66,6 @@
|
||||
}
|
||||
],
|
||||
"postpublish": [
|
||||
"git tag ${ pkg.pkg }-v${ pkgFile.versionMajor } -f",
|
||||
"git tag ${ pkg.pkg }-v${ pkgFile.versionMajor }.${ pkgFile.versionMinor } -f",
|
||||
"git push --tags -f"
|
||||
],
|
||||
@@ -80,7 +79,7 @@
|
||||
"javascript": {
|
||||
"errorOnVersionRange": "^2.0.0-0",
|
||||
"version": true,
|
||||
"getPublishedVersion": "npm view ${ pkgFile.pkg.name } version",
|
||||
"getPublishedVersion": "node ../../.scripts/covector/package-latest-version.js npm ${ pkgFile.pkg.name } ${ pkgFile.pkg.version }",
|
||||
"prepublish": [
|
||||
{
|
||||
"command": "yarn",
|
||||
@@ -136,7 +135,6 @@
|
||||
}
|
||||
],
|
||||
"postpublish": [
|
||||
"git tag ${ pkg.pkg }-v${ pkgFile.versionMajor } -f",
|
||||
"git tag ${ pkg.pkg }-v${ pkgFile.versionMajor }.${ pkgFile.versionMinor } -f",
|
||||
"git push --tags -f"
|
||||
]
|
||||
@@ -255,7 +253,7 @@
|
||||
"cli.js": {
|
||||
"path": "./tooling/cli/node",
|
||||
"manager": "javascript",
|
||||
"dependencies": ["cli.rs"],
|
||||
"getPublishedVersion": "node ../../../.scripts/covector/package-latest-version.js npm ${ pkgFile.pkg.name } ${ pkgFile.pkg.version }",
|
||||
"postversion": "node ../../../.scripts/covector/sync-cli-metadata.js ${ pkg.pkg } ${ release.type }",
|
||||
"prepublish": [],
|
||||
"publish": [],
|
||||
|
||||
@@ -136,7 +136,7 @@ jobs:
|
||||
uses: jbolda/covector/packages/action@covector-v0
|
||||
id: covector
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }}
|
||||
CARGO_AUDIT_OPTIONS: ${{ secrets.CARGO_AUDIT_OPTIONS }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
2
.github/workflows/publish-cli.yml
vendored
2
.github/workflows/publish-cli.yml
vendored
@@ -396,4 +396,4 @@ jobs:
|
||||
npm publish
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.ORG_NPM_TOKEN }}
|
||||
|
||||
58
.github/workflows/publish-hotfix.yml
vendored
Normal file
58
.github/workflows/publish-hotfix.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
# Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
name: version or publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '1.*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 65
|
||||
outputs:
|
||||
change: ${{ steps.covector.outputs.change }}
|
||||
commandRan: ${{ steps.covector.outputs.commandRan }}
|
||||
successfulPublish: ${{ steps.covector.outputs.successfulPublish }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
cache: yarn
|
||||
cache-dependency-path: tooling/*/yarn.lock
|
||||
|
||||
- name: cargo login
|
||||
run: cargo login ${{ secrets.ORG_CRATES_IO_TOKEN }}
|
||||
- name: git config
|
||||
run: |
|
||||
git config --global user.name "${{ github.event.pusher.name }}"
|
||||
git config --global user.email "${{ github.event.pusher.email }}"
|
||||
|
||||
- name: covector version or publish (publish when no change files present)
|
||||
uses: jbolda/covector/packages/action@covector-v0
|
||||
id: covector
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.ORG_NPM_TOKEN }}
|
||||
CARGO_AUDIT_OPTIONS: ${{ secrets.CARGO_AUDIT_OPTIONS }}
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
command: 'version-or-publish'
|
||||
createRelease: true
|
||||
|
||||
- name: Trigger cli.js publishing workflow
|
||||
if: |
|
||||
steps.covector.outputs.successfulPublish == 'true' &&
|
||||
contains(steps.covector.outputs.packagesPublished, 'cli.rs')
|
||||
uses: peter-evans/repository-dispatch@v1
|
||||
with:
|
||||
token: ${{ secrets.TAURI_BOT_PAT }}
|
||||
repository: tauri-apps/tauri
|
||||
event-type: publish-clijs
|
||||
54
.scripts/covector/package-latest-version.js
Normal file
54
.scripts/covector/package-latest-version.js
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env node
|
||||
// Copyright 2019-2022 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/*
|
||||
This script is solely intended to be run as part of the `covector publish` step to
|
||||
check the latest version of a crate, considering the current minor version.
|
||||
*/
|
||||
|
||||
const https = require('https')
|
||||
|
||||
const kind = process.argv[2]
|
||||
const packageName = process.argv[3]
|
||||
const packageVersion = process.argv[4]
|
||||
const target = packageVersion.substring(0, packageVersion.lastIndexOf('.'))
|
||||
|
||||
let url = null
|
||||
switch (kind) {
|
||||
case 'cargo':
|
||||
url = `https://crates.io/api/v1/crates/${packageName}`
|
||||
break;
|
||||
case 'npm':
|
||||
url = `https://registry.npmjs.org/${packageName}`
|
||||
break;
|
||||
default:
|
||||
throw new Error('unexpected kind ' + kind)
|
||||
}
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'tauri (https://github.com/tauri-apps/tauri)'
|
||||
}
|
||||
}
|
||||
|
||||
https.get(url, options, (response) => {
|
||||
let chunks = []
|
||||
response.on('data', function (chunk) {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
|
||||
response.on('end', function () {
|
||||
const data = JSON.parse(chunks.join(''))
|
||||
if (kind === 'cargo') {
|
||||
const versions = data.versions.filter(v => v.num.startsWith(target))
|
||||
console.log(versions.length ? versions[0].num : '0.0.0')
|
||||
} else if (kind === 'npm') {
|
||||
const versions = Object.keys(data.versions).filter(v => v.startsWith(target))
|
||||
console.log(versions[versions.length - 1] || '0.0.0')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -54,5 +54,5 @@ fn override_msvcrt_lib() {
|
||||
f.write_all(bytes).unwrap();
|
||||
}
|
||||
// Add the output directory to the native library path.
|
||||
println!("cargo:rustc-link-search=native={}", out_dir);
|
||||
println!("cargo:rustc-link-search=native={out_dir}");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## \[0.10.3]
|
||||
|
||||
- Block remote URLs from accessing the IPC.
|
||||
- [fa90214b0](https://www.github.com/tauri-apps/tauri/commit/fa90214b052b1a5d38d54fbf1ca422b4c37cfd1f) feat(core): block remote URLs from accessing the IPC on 2023-04-12
|
||||
|
||||
## \[0.10.2]
|
||||
|
||||
- Disable drag-n-drop of tao based on `fileDropEnabled` value.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "0.10.2"
|
||||
version = "0.10.3"
|
||||
authors = [ "Tauri Programme within The Commons Conservancy" ]
|
||||
categories = [ "gui", "web-programming" ]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
@@ -14,11 +14,12 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
wry = { version = "0.19", default-features = false, features = [ "file-drop", "protocol" ] }
|
||||
tauri-runtime = { version = "0.10.2", path = "../tauri-runtime" }
|
||||
tauri-runtime = { version = "0.10.3", path = "../tauri-runtime" }
|
||||
tauri-utils = { version = "1.0.3", path = "../tauri-utils" }
|
||||
uuid = { version = "1", features = [ "v4" ] }
|
||||
rand = "0.8"
|
||||
raw-window-handle = "0.4.3"
|
||||
url = "2"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.16.0"
|
||||
|
||||
@@ -41,6 +41,7 @@ use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWind
|
||||
use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};
|
||||
|
||||
use tauri_utils::{config::WindowConfig, debug_eprintln, Theme};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
use wry::{
|
||||
application::{
|
||||
@@ -216,6 +217,7 @@ impl<T: UserEvent> Context<T> {
|
||||
impl<T: UserEvent> Context<T> {
|
||||
fn create_webview(&self, pending: PendingWindow<T, Wry<T>>) -> Result<DetachedWindow<T, Wry<T>>> {
|
||||
let label = pending.label.clone();
|
||||
let current_url = pending.current_url.clone();
|
||||
let menu_ids = pending.menu_ids.clone();
|
||||
let js_event_listeners = pending.js_event_listeners.clone();
|
||||
let context = self.clone();
|
||||
@@ -237,6 +239,7 @@ impl<T: UserEvent> Context<T> {
|
||||
};
|
||||
Ok(DetachedWindow {
|
||||
label,
|
||||
current_url,
|
||||
dispatcher,
|
||||
menu_ids,
|
||||
js_event_listeners,
|
||||
@@ -1862,6 +1865,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
|
||||
fn create_window(&self, pending: PendingWindow<T, Self>) -> Result<DetachedWindow<T, Self>> {
|
||||
let label = pending.label.clone();
|
||||
let current_url = pending.current_url.clone();
|
||||
let menu_ids = pending.menu_ids.clone();
|
||||
let js_event_listeners = pending.js_event_listeners.clone();
|
||||
let window_id = rand::random();
|
||||
@@ -1889,6 +1893,7 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
|
||||
|
||||
Ok(DetachedWindow {
|
||||
label,
|
||||
current_url,
|
||||
dispatcher,
|
||||
menu_ids,
|
||||
js_event_listeners,
|
||||
@@ -2833,7 +2838,7 @@ fn create_webview<T: UserEvent>(
|
||||
mut window_builder,
|
||||
ipc_handler,
|
||||
label,
|
||||
url,
|
||||
current_url,
|
||||
menu_ids,
|
||||
js_event_listeners,
|
||||
..
|
||||
@@ -2871,16 +2876,22 @@ fn create_webview<T: UserEvent>(
|
||||
}
|
||||
let mut webview_builder = WebViewBuilder::new(window)
|
||||
.map_err(|e| Error::CreateWebview(Box::new(e)))?
|
||||
.with_url(&url)
|
||||
.with_url(current_url.lock().unwrap().as_str())
|
||||
.unwrap() // safe to unwrap because we validate the URL beforehand
|
||||
.with_transparent(is_window_transparent);
|
||||
if webview_attributes.file_drop_handler_enabled {
|
||||
webview_builder = webview_builder.with_file_drop_handler(create_file_drop_handler(&context));
|
||||
}
|
||||
if let Some(navigation_handler) = pending.navigation_handler {
|
||||
webview_builder = webview_builder.with_navigation_handler(move |url| {
|
||||
Url::parse(&url).map(&navigation_handler).unwrap_or(true)
|
||||
});
|
||||
}
|
||||
if let Some(handler) = ipc_handler {
|
||||
webview_builder = webview_builder.with_ipc_handler(create_ipc_handler(
|
||||
context,
|
||||
label.clone(),
|
||||
current_url,
|
||||
menu_ids,
|
||||
js_event_listeners,
|
||||
handler,
|
||||
@@ -2984,6 +2995,7 @@ fn create_webview<T: UserEvent>(
|
||||
fn create_ipc_handler<T: UserEvent>(
|
||||
context: Context<T>,
|
||||
label: String,
|
||||
current_url: Arc<Mutex<Url>>,
|
||||
menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
|
||||
js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
|
||||
handler: WebviewIpcHandler<T, Wry<T>>,
|
||||
@@ -2992,6 +3004,7 @@ fn create_ipc_handler<T: UserEvent>(
|
||||
let window_id = context.webview_id_map.get(&window.id());
|
||||
handler(
|
||||
DetachedWindow {
|
||||
current_url: current_url.clone(),
|
||||
dispatcher: WryDispatcher {
|
||||
window_id,
|
||||
context: context.clone(),
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# Changelog
|
||||
|
||||
## \[0.10.3]
|
||||
|
||||
- Block remote URLs from accessing the IPC.
|
||||
- [fa90214b0](https://www.github.com/tauri-apps/tauri/commit/fa90214b052b1a5d38d54fbf1ca422b4c37cfd1f) feat(core): block remote URLs from accessing the IPC on 2023-04-12
|
||||
|
||||
## \[0.10.2]
|
||||
|
||||
- Added option to disable tray menu on left click on macOS.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "tauri-runtime"
|
||||
version = "0.10.2"
|
||||
version = "0.10.3"
|
||||
authors = [ "Tauri Programme within The Commons Conservancy" ]
|
||||
categories = [ "gui", "web-programming" ]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
@@ -32,6 +32,7 @@ http = "0.2.4"
|
||||
http-range = "0.1.4"
|
||||
infer = "0.7"
|
||||
raw-window-handle = "0.4.3"
|
||||
url = "2"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.16.0"
|
||||
|
||||
@@ -12,6 +12,7 @@ use crate::{
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use tauri_utils::{config::WindowConfig, Theme};
|
||||
use url::Url;
|
||||
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -226,14 +227,17 @@ pub struct PendingWindow<T: UserEvent, R: Runtime<T>> {
|
||||
/// How to handle IPC calls on the webview window.
|
||||
pub ipc_handler: Option<WebviewIpcHandler<T, R>>,
|
||||
|
||||
/// The resolved URL to load on the webview.
|
||||
pub url: String,
|
||||
|
||||
/// Maps runtime id to a string menu id.
|
||||
pub menu_ids: Arc<Mutex<HashMap<MenuHash, MenuId>>>,
|
||||
|
||||
/// A HashMap mapping JS event names with associated listener ids.
|
||||
pub js_event_listeners: Arc<Mutex<HashMap<JsEventListenerKey, HashSet<u64>>>>,
|
||||
|
||||
/// A handler to decide if incoming url is allowed to navigate.
|
||||
pub navigation_handler: Option<Box<dyn Fn(Url) -> bool + Send>>,
|
||||
|
||||
/// The current webview URL.
|
||||
pub current_url: Arc<Mutex<Url>>,
|
||||
}
|
||||
|
||||
pub fn is_label_valid(label: &str) -> bool {
|
||||
@@ -270,9 +274,10 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
|
||||
uri_scheme_protocols: Default::default(),
|
||||
label,
|
||||
ipc_handler: None,
|
||||
url: "tauri://localhost".to_string(),
|
||||
menu_ids: Arc::new(Mutex::new(menu_ids)),
|
||||
js_event_listeners: Default::default(),
|
||||
navigation_handler: Default::default(),
|
||||
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -299,9 +304,10 @@ impl<T: UserEvent, R: Runtime<T>> PendingWindow<T, R> {
|
||||
uri_scheme_protocols: Default::default(),
|
||||
label,
|
||||
ipc_handler: None,
|
||||
url: "tauri://localhost".to_string(),
|
||||
menu_ids: Arc::new(Mutex::new(menu_ids)),
|
||||
js_event_listeners: Default::default(),
|
||||
navigation_handler: Default::default(),
|
||||
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -342,6 +348,9 @@ pub struct JsEventListenerKey {
|
||||
/// A webview window that is not yet managed by Tauri.
|
||||
#[derive(Debug)]
|
||||
pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
|
||||
/// The current webview URL.
|
||||
pub current_url: Arc<Mutex<Url>>,
|
||||
|
||||
/// Name of the window
|
||||
pub label: String,
|
||||
|
||||
@@ -358,6 +367,7 @@ pub struct DetachedWindow<T: UserEvent, R: Runtime<T>> {
|
||||
impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWindow<T, R> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
current_url: self.current_url.clone(),
|
||||
label: self.label.clone(),
|
||||
dispatcher: self.dispatcher.clone(),
|
||||
menu_ids: self.menu_ids.clone(),
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# Changelog
|
||||
|
||||
## \[1.0.9]
|
||||
|
||||
- Block remote URLs from accessing the IPC.
|
||||
- [fa90214b0](https://www.github.com/tauri-apps/tauri/commit/fa90214b052b1a5d38d54fbf1ca422b4c37cfd1f) feat(core): block remote URLs from accessing the IPC on 2023-04-12
|
||||
|
||||
## \[1.0.8]
|
||||
|
||||
- Fix the filesystem scope allowing sub-directories of the directory picked by the dialog when `recursive` option was `false`.
|
||||
- [f0602e7c](https://www.github.com/tauri-apps/tauri/commit/f0602e7c294245ab6ef6fbf2a976ef398340ef58) Merge pull request from GHSA-6mv3-wm7j-h4w5 on 2022-12-22
|
||||
|
||||
## \[1.0.7]
|
||||
|
||||
- Escape glob special characters in files/directories when dropping files or using the open/save dialogs.
|
||||
- [bcd9dc7f](https://www.github.com/tauri-apps/tauri/commit/bcd9dc7f859fa7e65fea5de835fa938ca1368eaf) fix(core): escape glob characters in drop/dialogs , closes [#5234](https://www.github.com/tauri-apps/tauri/pull/5234) ([#5237](https://www.github.com/tauri-apps/tauri/pull/5237)) on 2022-11-08
|
||||
|
||||
## \[1.0.6]
|
||||
|
||||
- Fix `fs.readDir` recursive option reading symlinked directories that are not allowed by the scope.
|
||||
- [bb178829](https://www.github.com/tauri-apps/tauri/commit/bb178829086e80916f9be190f02d83bc25802799) fix(endpoints/fs/readDir): don't read symlinks that are not allowed b… ([#5123](https://www.github.com/tauri-apps/tauri/pull/5123)) on 2022-09-08
|
||||
|
||||
## \[1.0.5]
|
||||
|
||||
- Escape the MSI file path when running msiexec via powershell.
|
||||
|
||||
@@ -16,7 +16,7 @@ license = "Apache-2.0 OR MIT"
|
||||
name = "tauri"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tauri-apps/tauri"
|
||||
version = "1.0.5"
|
||||
version = "1.0.9"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
no-default-features = true
|
||||
@@ -55,10 +55,10 @@ url = { version = "2.2" }
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
once_cell = "1.12"
|
||||
tauri-runtime = { version = "0.10.2", path = "../tauri-runtime" }
|
||||
tauri-runtime = { version = "0.10.3", path = "../tauri-runtime" }
|
||||
tauri-macros = { version = "1.0.4", path = "../tauri-macros" }
|
||||
tauri-utils = { version = "1.0.3", features = [ "resources" ], path = "../tauri-utils" }
|
||||
tauri-runtime-wry = { version = "0.10.2", path = "../tauri-runtime-wry", optional = true }
|
||||
tauri-runtime-wry = { version = "0.10.3", path = "../tauri-runtime-wry", optional = true }
|
||||
rand = "0.8"
|
||||
semver = { version = "1.0", features = [ "serde" ] }
|
||||
serde_repr = "0.1"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
fs::{self, metadata},
|
||||
fs::{self, metadata, symlink_metadata},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use tempfile::{self, tempdir};
|
||||
@@ -31,8 +31,36 @@ pub fn is_dir<P: AsRef<Path>>(path: P) -> crate::api::Result<bool> {
|
||||
metadata(path).map(|md| md.is_dir()).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn is_symlink<P: AsRef<Path>>(path: P) -> crate::api::Result<bool> {
|
||||
// TODO: remove the different implementation once we raise tauri's MSRV to at least 1.58
|
||||
#[cfg(windows)]
|
||||
let ret = symlink_metadata(path)
|
||||
.map(|md| md.is_symlink())
|
||||
.map_err(Into::into);
|
||||
|
||||
#[cfg(not(windows))]
|
||||
let ret = symlink_metadata(path)
|
||||
.map(|md| md.file_type().is_symlink())
|
||||
.map_err(Into::into);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Reads a directory. Can perform recursive operations.
|
||||
pub fn read_dir<P: AsRef<Path>>(path: P, recursive: bool) -> crate::api::Result<Vec<DiskEntry>> {
|
||||
read_dir_with_options(path, recursive, ReadDirOptions { scope: None })
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct ReadDirOptions<'a> {
|
||||
pub scope: Option<&'a crate::FsScope>,
|
||||
}
|
||||
|
||||
pub(crate) fn read_dir_with_options<P: AsRef<Path>>(
|
||||
path: P,
|
||||
recursive: bool,
|
||||
options: ReadDirOptions<'_>,
|
||||
) -> crate::api::Result<Vec<DiskEntry>> {
|
||||
let mut files_and_dirs: Vec<DiskEntry> = vec![];
|
||||
for entry in fs::read_dir(path)? {
|
||||
let path = entry?.path();
|
||||
@@ -42,11 +70,16 @@ pub fn read_dir<P: AsRef<Path>>(path: P, recursive: bool) -> crate::api::Result<
|
||||
files_and_dirs.push(DiskEntry {
|
||||
path: path.clone(),
|
||||
children: if flag {
|
||||
Some(if recursive {
|
||||
read_dir(&path_as_string, true)?
|
||||
} else {
|
||||
vec![]
|
||||
})
|
||||
Some(
|
||||
if recursive
|
||||
&& (!is_symlink(&path_as_string)?
|
||||
|| options.scope.map(|s| s.is_allowed(&path)).unwrap_or(true))
|
||||
{
|
||||
read_dir_with_options(&path_as_string, true, options)?
|
||||
} else {
|
||||
vec![]
|
||||
},
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
|
||||
@@ -74,6 +74,7 @@ pub fn read_binary<P: AsRef<Path>>(file: P) -> crate::api::Result<Vec<u8>> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[cfg(not(windows))]
|
||||
use crate::api::Error;
|
||||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
|
||||
@@ -902,7 +902,7 @@ impl<R: Runtime> Builder<R> {
|
||||
#[cfg(any(windows, target_os = "linux"))]
|
||||
runtime_any_thread: false,
|
||||
setup: Box::new(|_| Ok(())),
|
||||
invoke_handler: Box::new(|_| ()),
|
||||
invoke_handler: Box::new(|invoke| invoke.resolver.reject("not implemented")),
|
||||
invoke_responder: Arc::new(window_invoke_responder),
|
||||
invoke_initialization_script:
|
||||
"Object.defineProperty(window, '__TAURI_POST_MESSAGE__', { value: (message) => window.ipc.postMessage(JSON.stringify(message)) })".into(),
|
||||
|
||||
@@ -191,9 +191,15 @@ impl Cmd {
|
||||
path,
|
||||
dir,
|
||||
)?;
|
||||
dir::read_dir(&resolved_path, recursive)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))
|
||||
.map_err(Into::into)
|
||||
dir::read_dir_with_options(
|
||||
&resolved_path,
|
||||
recursive,
|
||||
dir::ReadDirOptions {
|
||||
scope: Some(&context.window.state::<Scopes>().fs),
|
||||
},
|
||||
)
|
||||
.with_context(|| format!("path: {}", resolved_path.display()))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[module_command_handler(fs_copy_file)]
|
||||
|
||||
@@ -25,10 +25,9 @@ use tauri_utils::{
|
||||
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
|
||||
};
|
||||
|
||||
use crate::hooks::IpcJavascript;
|
||||
#[cfg(feature = "isolation")]
|
||||
use crate::hooks::IsolationJavascript;
|
||||
use crate::pattern::{format_real_schema, PatternJavascript};
|
||||
use crate::pattern::PatternJavascript;
|
||||
use crate::{
|
||||
app::{AppHandle, GlobalWindowEvent, GlobalWindowEventListener},
|
||||
event::{assert_event_name_is_valid, Event, EventHandler, Listeners},
|
||||
@@ -54,6 +53,7 @@ use crate::{
|
||||
app::{GlobalMenuEventListener, WindowMenuEvent},
|
||||
window::WebResourceRequestHandler,
|
||||
};
|
||||
use crate::{hooks::IpcJavascript, pattern::format_real_schema};
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "windows"))]
|
||||
use crate::api::path::{resolve_path, BaseDirectory};
|
||||
@@ -136,7 +136,7 @@ fn set_csp<R: Runtime>(
|
||||
let default_src = csp
|
||||
.entry("default-src".into())
|
||||
.or_insert_with(Default::default);
|
||||
default_src.push(format_real_schema(schema));
|
||||
default_src.push(crate::pattern::format_real_schema(schema));
|
||||
}
|
||||
|
||||
Csp::DirectiveMap(csp).to_string()
|
||||
@@ -225,7 +225,7 @@ pub struct InnerWindowManager<R: Runtime> {
|
||||
/// The script that initializes the invoke system.
|
||||
invoke_initialization_script: String,
|
||||
/// Application pattern.
|
||||
pattern: Pattern,
|
||||
pub(crate) pattern: Pattern,
|
||||
}
|
||||
|
||||
impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
|
||||
@@ -357,9 +357,12 @@ impl<R: Runtime> WindowManager<R> {
|
||||
/// Get the base URL to use for webview requests.
|
||||
///
|
||||
/// In dev mode, this will be based on the `devPath` configuration value.
|
||||
fn get_url(&self) -> Cow<'_, Url> {
|
||||
pub(crate) fn get_url(&self) -> Cow<'_, Url> {
|
||||
match self.base_path() {
|
||||
AppUrl::Url(WindowUrl::External(url)) => Cow::Borrowed(url),
|
||||
#[cfg(windows)]
|
||||
_ => Cow::Owned(Url::parse("https://tauri.localhost").unwrap()),
|
||||
#[cfg(not(windows))]
|
||||
_ => Cow::Owned(Url::parse("tauri://localhost").unwrap()),
|
||||
}
|
||||
}
|
||||
@@ -467,7 +470,7 @@ impl<R: Runtime> WindowManager<R> {
|
||||
});
|
||||
}
|
||||
|
||||
let window_url = Url::parse(&pending.url).unwrap();
|
||||
let window_url = pending.current_url.lock().unwrap().clone();
|
||||
let window_origin =
|
||||
if cfg!(windows) && window_url.scheme() != "http" && window_url.scheme() != "https" {
|
||||
format!("https://{}.localhost", window_url.scheme())
|
||||
@@ -1001,7 +1004,16 @@ mod test {
|
||||
);
|
||||
|
||||
#[cfg(custom_protocol)]
|
||||
assert_eq!(manager.get_url().to_string(), "tauri://localhost");
|
||||
{
|
||||
assert_eq!(
|
||||
manager.get_url().to_string(),
|
||||
if cfg!(windows) {
|
||||
"https://tauri.localhost/"
|
||||
} else {
|
||||
"tauri://localhost"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(dev)]
|
||||
assert_eq!(manager.get_url().to_string(), "http://localhost:4000/");
|
||||
@@ -1052,27 +1064,21 @@ impl<R: Runtime> WindowManager<R> {
|
||||
return Err(crate::Error::WindowLabelAlreadyExists(pending.label));
|
||||
}
|
||||
#[allow(unused_mut)] // mut url only for the data-url parsing
|
||||
let (is_local, mut url) = match &pending.webview_attributes.url {
|
||||
let mut url = match &pending.webview_attributes.url {
|
||||
WindowUrl::App(path) => {
|
||||
let url = self.get_url();
|
||||
(
|
||||
true,
|
||||
// ignore "index.html" just to simplify the url
|
||||
if path.to_str() != Some("index.html") {
|
||||
url
|
||||
.join(&*path.to_string_lossy())
|
||||
.map_err(crate::Error::InvalidUrl)
|
||||
// this will never fail
|
||||
.unwrap()
|
||||
} else {
|
||||
url.into_owned()
|
||||
},
|
||||
)
|
||||
}
|
||||
WindowUrl::External(url) => {
|
||||
let config_url = self.get_url();
|
||||
(config_url.make_relative(url).is_some(), url.clone())
|
||||
// ignore "index.html" just to simplify the url
|
||||
if path.to_str() != Some("index.html") {
|
||||
url
|
||||
.join(&*path.to_string_lossy())
|
||||
.map_err(crate::Error::InvalidUrl)
|
||||
// this will never fail
|
||||
.unwrap()
|
||||
} else {
|
||||
url.into_owned()
|
||||
}
|
||||
}
|
||||
WindowUrl::External(url) => url.clone(),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
@@ -1099,7 +1105,7 @@ impl<R: Runtime> WindowManager<R> {
|
||||
}
|
||||
}
|
||||
|
||||
pending.url = url.to_string();
|
||||
*pending.current_url.lock().unwrap() = url;
|
||||
|
||||
if !pending.window_builder.has_icon() {
|
||||
if let Some(default_window_icon) = self.inner.default_window_icon.clone() {
|
||||
@@ -1115,17 +1121,15 @@ impl<R: Runtime> WindowManager<R> {
|
||||
}
|
||||
}
|
||||
|
||||
if is_local {
|
||||
let label = pending.label.clone();
|
||||
pending = self.prepare_pending_window(
|
||||
pending,
|
||||
&label,
|
||||
window_labels,
|
||||
app_handle.clone(),
|
||||
web_resource_request_handler,
|
||||
)?;
|
||||
pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle));
|
||||
}
|
||||
let label = pending.label.clone();
|
||||
pending = self.prepare_pending_window(
|
||||
pending,
|
||||
&label,
|
||||
window_labels,
|
||||
app_handle.clone(),
|
||||
web_resource_request_handler,
|
||||
)?;
|
||||
pending.ipc_handler = Some(self.prepare_ipc_handler(app_handle));
|
||||
|
||||
// in `Windows`, we need to force a data_directory
|
||||
// but we do respect user-specification
|
||||
@@ -1150,6 +1154,28 @@ impl<R: Runtime> WindowManager<R> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "isolation")]
|
||||
let pattern = self.pattern().clone();
|
||||
let current_url_ = pending.current_url.clone();
|
||||
let navigation_handler = pending.navigation_handler.take();
|
||||
pending.navigation_handler = Some(Box::new(move |url| {
|
||||
// always allow navigation events for the isolation iframe and do not emit them for consumers
|
||||
#[cfg(feature = "isolation")]
|
||||
if let Pattern::Isolation { schema, .. } = &pattern {
|
||||
if url.scheme() == schema
|
||||
&& url.domain() == Some(crate::pattern::ISOLATION_IFRAME_SRC_DOMAIN)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*current_url_.lock().unwrap() = url.clone();
|
||||
if let Some(handler) = &navigation_handler {
|
||||
handler(url)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}));
|
||||
|
||||
Ok(pending)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,11 @@ use serialize_to_javascript::{default_template, Template};
|
||||
|
||||
use tauri_utils::assets::{Assets, EmbeddedAssets};
|
||||
|
||||
/// The domain of the isolation iframe source.
|
||||
pub const ISOLATION_IFRAME_SRC_DOMAIN: &str = "localhost";
|
||||
|
||||
/// An application pattern.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum Pattern<A: Assets = EmbeddedAssets> {
|
||||
/// The brownfield pattern.
|
||||
Brownfield(PhantomData<A>),
|
||||
@@ -35,6 +38,26 @@ pub enum Pattern<A: Assets = EmbeddedAssets> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<A: Assets> Clone for Pattern<A> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Brownfield(a) => Self::Brownfield(*a),
|
||||
#[cfg(feature = "isolation")]
|
||||
Self::Isolation {
|
||||
assets,
|
||||
schema,
|
||||
key,
|
||||
crypto_keys,
|
||||
} => Self::Isolation {
|
||||
assets: assets.clone(),
|
||||
schema: schema.clone(),
|
||||
key: key.clone(),
|
||||
crypto_keys: crypto_keys.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The shape of the JavaScript Pattern config
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "lowercase", tag = "pattern")]
|
||||
@@ -87,8 +110,8 @@ pub(crate) struct PatternJavascript {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn format_real_schema(schema: &str) -> String {
|
||||
if cfg!(windows) {
|
||||
format!("https://{}.localhost", schema)
|
||||
format!("https://{schema}.{ISOLATION_IFRAME_SRC_DOMAIN}")
|
||||
} else {
|
||||
format!("{}://localhost", schema)
|
||||
format!("{schema}://{ISOLATION_IFRAME_SRC_DOMAIN}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
path::{Path, PathBuf, MAIN_SEPARATOR},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
@@ -64,15 +64,19 @@ impl fmt::Debug for Scope {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_pattern<P: AsRef<Path>>(list: &mut HashSet<Pattern>, pattern: P) -> crate::Result<()> {
|
||||
fn push_pattern<P: AsRef<Path>, F: Fn(&str) -> Result<Pattern, glob::PatternError>>(
|
||||
list: &mut HashSet<Pattern>,
|
||||
pattern: P,
|
||||
f: F,
|
||||
) -> crate::Result<()> {
|
||||
let path: PathBuf = pattern.as_ref().components().collect();
|
||||
list.insert(Pattern::new(&path.to_string_lossy())?);
|
||||
list.insert(f(&path.to_string_lossy())?);
|
||||
#[cfg(windows)]
|
||||
{
|
||||
if let Ok(p) = std::fs::canonicalize(&path) {
|
||||
list.insert(Pattern::new(&p.to_string_lossy())?);
|
||||
list.insert(f(&p.to_string_lossy())?);
|
||||
} else {
|
||||
list.insert(Pattern::new(&format!("\\\\?\\{}", path.display()))?);
|
||||
list.insert(f(&format!("\\\\?\\{}", path.display()))?);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -89,7 +93,7 @@ impl Scope {
|
||||
let mut alllowed_patterns = HashSet::new();
|
||||
for path in scope.allowed_paths() {
|
||||
if let Ok(path) = parse_path(config, package_info, env, path) {
|
||||
push_pattern(&mut alllowed_patterns, path)?;
|
||||
push_pattern(&mut alllowed_patterns, path, Pattern::new)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +101,7 @@ impl Scope {
|
||||
if let Some(forbidden_paths) = scope.forbidden_paths() {
|
||||
for path in forbidden_paths {
|
||||
if let Ok(path) = parse_path(config, package_info, env, path) {
|
||||
push_pattern(&mut forbidden_patterns, path)?;
|
||||
push_pattern(&mut forbidden_patterns, path, Pattern::new)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -137,18 +141,20 @@ impl Scope {
|
||||
/// Extend the allowed patterns with the given directory.
|
||||
///
|
||||
/// After this function has been called, the frontend will be able to use the Tauri API to read
|
||||
/// the directory and all of its files and subdirectories.
|
||||
/// the directory and all of its files. If `recursive` is `true`, subdirectories will be accessible too.
|
||||
pub fn allow_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) -> crate::Result<()> {
|
||||
let path = path.as_ref().to_path_buf();
|
||||
let path = path.as_ref();
|
||||
{
|
||||
let mut list = self.alllowed_patterns.lock().unwrap();
|
||||
|
||||
// allow the directory to be read
|
||||
push_pattern(&mut list, &path)?;
|
||||
push_pattern(&mut list, &path, escaped_pattern)?;
|
||||
// allow its files and subdirectories to be read
|
||||
push_pattern(&mut list, path.join(if recursive { "**" } else { "*" }))?;
|
||||
push_pattern(&mut list, &path, |p| {
|
||||
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
||||
})?;
|
||||
}
|
||||
self.trigger(Event::PathAllowed(path));
|
||||
self.trigger(Event::PathAllowed(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -157,7 +163,11 @@ impl Scope {
|
||||
/// After this function has been called, the frontend will be able to use the Tauri API to read the contents of this file.
|
||||
pub fn allow_file<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
|
||||
let path = path.as_ref();
|
||||
push_pattern(&mut self.alllowed_patterns.lock().unwrap(), &path)?;
|
||||
push_pattern(
|
||||
&mut self.alllowed_patterns.lock().unwrap(),
|
||||
&path,
|
||||
escaped_pattern,
|
||||
)?;
|
||||
self.trigger(Event::PathAllowed(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
@@ -166,16 +176,18 @@ impl Scope {
|
||||
///
|
||||
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
|
||||
pub fn forbid_directory<P: AsRef<Path>>(&self, path: P, recursive: bool) -> crate::Result<()> {
|
||||
let path = path.as_ref().to_path_buf();
|
||||
let path = path.as_ref();
|
||||
{
|
||||
let mut list = self.forbidden_patterns.lock().unwrap();
|
||||
|
||||
// allow the directory to be read
|
||||
push_pattern(&mut list, &path)?;
|
||||
push_pattern(&mut list, &path, escaped_pattern)?;
|
||||
// allow its files and subdirectories to be read
|
||||
push_pattern(&mut list, path.join(if recursive { "**" } else { "*" }))?;
|
||||
push_pattern(&mut list, &path, |p| {
|
||||
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
||||
})?;
|
||||
}
|
||||
self.trigger(Event::PathForbidden(path));
|
||||
self.trigger(Event::PathForbidden(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -184,7 +196,11 @@ impl Scope {
|
||||
/// **Note:** this takes precedence over allowed paths, so its access gets denied **always**.
|
||||
pub fn forbid_file<P: AsRef<Path>>(&self, path: P) -> crate::Result<()> {
|
||||
let path = path.as_ref();
|
||||
push_pattern(&mut self.forbidden_patterns.lock().unwrap(), &path)?;
|
||||
push_pattern(
|
||||
&mut self.forbidden_patterns.lock().unwrap(),
|
||||
&path,
|
||||
escaped_pattern,
|
||||
)?;
|
||||
self.trigger(Event::PathForbidden(path.to_path_buf()));
|
||||
Ok(())
|
||||
}
|
||||
@@ -200,13 +216,22 @@ impl Scope {
|
||||
|
||||
if let Ok(path) = path {
|
||||
let path: PathBuf = path.components().collect();
|
||||
let options = glob::MatchOptions {
|
||||
// this is needed so `/dir/*` doesn't match files within subdirectories such as `/dir/subdir/file.txt`
|
||||
// see: https://github.com/tauri-apps/tauri/security/advisories/GHSA-6mv3-wm7j-h4w5
|
||||
require_literal_separator: true,
|
||||
// dotfiles are not supposed to be exposed by default
|
||||
#[cfg(unix)]
|
||||
require_literal_leading_dot: true,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let forbidden = self
|
||||
.forbidden_patterns
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|p| p.matches_path(&path));
|
||||
.any(|p| p.matches_path_with(&path, options));
|
||||
|
||||
if forbidden {
|
||||
false
|
||||
@@ -216,7 +241,7 @@ impl Scope {
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.any(|p| p.matches_path(&path));
|
||||
.any(|p| p.matches_path_with(&path, options));
|
||||
allowed
|
||||
}
|
||||
} else {
|
||||
@@ -224,3 +249,126 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn escaped_pattern(p: &str) -> Result<Pattern, glob::PatternError> {
|
||||
Pattern::new(&glob::Pattern::escape(p))
|
||||
}
|
||||
|
||||
fn escaped_pattern_with(p: &str, append: &str) -> Result<Pattern, glob::PatternError> {
|
||||
Pattern::new(&format!(
|
||||
"{}{}{}",
|
||||
glob::Pattern::escape(p),
|
||||
MAIN_SEPARATOR,
|
||||
append
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Scope;
|
||||
|
||||
fn new_scope() -> Scope {
|
||||
Scope {
|
||||
alllowed_patterns: Default::default(),
|
||||
forbidden_patterns: Default::default(),
|
||||
event_listeners: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn path_is_escaped() {
|
||||
let scope = new_scope();
|
||||
#[cfg(unix)]
|
||||
{
|
||||
scope.allow_directory("/home/tauri/**", false).unwrap();
|
||||
assert!(scope.is_allowed("/home/tauri/**"));
|
||||
assert!(scope.is_allowed("/home/tauri/**/file"));
|
||||
assert!(!scope.is_allowed("/home/tauri/anyfile"));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
scope.allow_directory("C:\\home\\tauri\\**", false).unwrap();
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**\\file"));
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
||||
}
|
||||
|
||||
let scope = new_scope();
|
||||
#[cfg(unix)]
|
||||
{
|
||||
scope.allow_file("/home/tauri/**").unwrap();
|
||||
assert!(scope.is_allowed("/home/tauri/**"));
|
||||
assert!(!scope.is_allowed("/home/tauri/**/file"));
|
||||
assert!(!scope.is_allowed("/home/tauri/anyfile"));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
scope.allow_file("C:\\home\\tauri\\**").unwrap();
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**"));
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\**\\file"));
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
||||
}
|
||||
|
||||
let scope = new_scope();
|
||||
#[cfg(unix)]
|
||||
{
|
||||
scope.allow_directory("/home/tauri", true).unwrap();
|
||||
scope.forbid_directory("/home/tauri/**", false).unwrap();
|
||||
assert!(!scope.is_allowed("/home/tauri/**"));
|
||||
assert!(!scope.is_allowed("/home/tauri/**/file"));
|
||||
assert!(scope.is_allowed("/home/tauri/**/inner/file"));
|
||||
assert!(scope.is_allowed("/home/tauri/inner/folder/anyfile"));
|
||||
assert!(scope.is_allowed("/home/tauri/anyfile"));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
scope.allow_directory("C:\\home\\tauri", true).unwrap();
|
||||
scope
|
||||
.forbid_directory("C:\\home\\tauri\\**", false)
|
||||
.unwrap();
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\**"));
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\**\\file"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**\\inner\\file"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\inner\\folder\\anyfile"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
||||
}
|
||||
|
||||
let scope = new_scope();
|
||||
#[cfg(unix)]
|
||||
{
|
||||
scope.allow_directory("/home/tauri", true).unwrap();
|
||||
scope.forbid_file("/home/tauri/**").unwrap();
|
||||
assert!(!scope.is_allowed("/home/tauri/**"));
|
||||
assert!(scope.is_allowed("/home/tauri/**/file"));
|
||||
assert!(scope.is_allowed("/home/tauri/**/inner/file"));
|
||||
assert!(scope.is_allowed("/home/tauri/anyfile"));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
scope.allow_directory("C:\\home\\tauri", true).unwrap();
|
||||
scope.forbid_file("C:\\home\\tauri\\**").unwrap();
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\**"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**\\file"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**\\inner\\file"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
||||
}
|
||||
|
||||
let scope = new_scope();
|
||||
#[cfg(unix)]
|
||||
{
|
||||
scope.allow_directory("/home/tauri", false).unwrap();
|
||||
assert!(scope.is_allowed("/home/tauri/**"));
|
||||
assert!(!scope.is_allowed("/home/tauri/**/file"));
|
||||
assert!(!scope.is_allowed("/home/tauri/**/inner/file"));
|
||||
assert!(scope.is_allowed("/home/tauri/anyfile"));
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
scope.allow_directory("C:\\home\\tauri", false).unwrap();
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\**"));
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\**\\file"));
|
||||
assert!(!scope.is_allowed("C:\\home\\tauri\\**\\inner\\file"));
|
||||
assert!(scope.is_allowed("C:\\home\\tauri\\anyfile"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ impl<T: UserEvent> RuntimeHandle<T> for MockRuntimeHandle {
|
||||
) -> Result<DetachedWindow<T, Self::Runtime>> {
|
||||
Ok(DetachedWindow {
|
||||
label: pending.label,
|
||||
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
|
||||
dispatcher: MockDispatcher {
|
||||
context: self.context.clone(),
|
||||
},
|
||||
@@ -616,6 +617,7 @@ impl<T: UserEvent> Runtime<T> for MockRuntime {
|
||||
fn create_window(&self, pending: PendingWindow<T, Self>) -> Result<DetachedWindow<T, Self>> {
|
||||
Ok(DetachedWindow {
|
||||
label: pending.label,
|
||||
current_url: Arc::new(Mutex::new("tauri://localhost".parse().unwrap())),
|
||||
dispatcher: MockDispatcher {
|
||||
context: self.context.clone(),
|
||||
},
|
||||
|
||||
@@ -33,6 +33,7 @@ use crate::{
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
use url::Url;
|
||||
#[cfg(windows)]
|
||||
use windows::Win32::Foundation::HWND;
|
||||
|
||||
@@ -483,7 +484,7 @@ impl<'a, R: Runtime> WindowBuilder<'a, R> {
|
||||
#[derive(Debug)]
|
||||
pub struct Window<R: Runtime> {
|
||||
/// The webview window created by the runtime.
|
||||
window: DetachedWindow<EventLoopMessage, R>,
|
||||
pub(crate) window: DetachedWindow<EventLoopMessage, R>,
|
||||
/// The manager to associate this webview window with.
|
||||
manager: WindowManager<R>,
|
||||
pub(crate) app_handle: AppHandle<R>,
|
||||
@@ -1157,9 +1158,27 @@ impl<R: Runtime> Window<R> {
|
||||
|
||||
/// Webview APIs.
|
||||
impl<R: Runtime> Window<R> {
|
||||
/// Returns the current url of the webview.
|
||||
pub fn url(&self) -> Url {
|
||||
self.window.current_url.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
/// Handles this window receiving an [`InvokeMessage`].
|
||||
pub fn on_message(self, payload: InvokePayload) -> crate::Result<()> {
|
||||
let manager = self.manager.clone();
|
||||
let current_url = self.url();
|
||||
let config_url = manager.get_url();
|
||||
#[allow(unused_mut)]
|
||||
let mut is_local = config_url.make_relative(¤t_url).is_some();
|
||||
#[cfg(feature = "isolation")]
|
||||
if let crate::Pattern::Isolation { schema, .. } = &self.manager.inner.pattern {
|
||||
if current_url.scheme() == schema
|
||||
&& current_url.domain() == Some(crate::pattern::ISOLATION_IFRAME_SRC_DOMAIN)
|
||||
{
|
||||
is_local = true;
|
||||
}
|
||||
}
|
||||
|
||||
match payload.cmd.as_str() {
|
||||
"__initialized" => {
|
||||
let payload: PageLoadPayload = serde_json::from_value(payload.inner)?;
|
||||
@@ -1173,8 +1192,15 @@ impl<R: Runtime> Window<R> {
|
||||
payload.inner,
|
||||
);
|
||||
let resolver = InvokeResolver::new(self, payload.callback, payload.error);
|
||||
|
||||
let invoke = Invoke { message, resolver };
|
||||
|
||||
if !is_local {
|
||||
invoke
|
||||
.resolver
|
||||
.reject("Remote URLs are not allowed to access the IPC");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(module) = &payload.tauri_module {
|
||||
crate::endpoints::handle(
|
||||
module.to_string(),
|
||||
|
||||
16
examples/api/src-tauri/Cargo.lock
generated
16
examples/api/src-tauri/Cargo.lock
generated
@@ -3132,7 +3132,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.0.2"
|
||||
version = "1.0.8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"attohttpc",
|
||||
@@ -3194,7 +3194,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -3209,7 +3209,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"brotli",
|
||||
@@ -3233,7 +3233,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "1.0.2"
|
||||
version = "1.0.4"
|
||||
dependencies = [
|
||||
"heck 0.4.0",
|
||||
"proc-macro2",
|
||||
@@ -3245,7 +3245,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@@ -3256,6 +3256,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror",
|
||||
"url",
|
||||
"uuid 1.1.2",
|
||||
"webview2-com",
|
||||
"windows 0.37.0",
|
||||
@@ -3263,7 +3264,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "0.10.1"
|
||||
version = "0.10.2"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"gtk",
|
||||
@@ -3272,6 +3273,7 @@ dependencies = [
|
||||
"raw-window-handle",
|
||||
"tauri-runtime",
|
||||
"tauri-utils",
|
||||
"url",
|
||||
"uuid 1.1.2",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
@@ -3281,7 +3283,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "1.0.2"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"brotli",
|
||||
|
||||
@@ -2,7 +2,7 @@ workspace = { }
|
||||
|
||||
[package]
|
||||
name = "tauri-bundler"
|
||||
version = "1.0.5"
|
||||
version = "1.0.7"
|
||||
authors = [
|
||||
"George Burton <burtonageo@gmail.com>",
|
||||
"Tauri Programme within The Commons Conservancy"
|
||||
|
||||
2
tooling/cli/Cargo.lock
generated
2
tooling/cli/Cargo.lock
generated
@@ -2766,7 +2766,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-bundler"
|
||||
version = "1.0.5"
|
||||
version = "1.0.7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ar",
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
"version": "1.0.5",
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"tauri": "1.0.5",
|
||||
"tauri": "1.0.8",
|
||||
"tauri-build": "1.0.4"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user