mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-10-10 22:38:44 +02:00
fix(fs): allow open, seek and fstat in the write permission sets (#3630)
* fix(fs): allow open, seek and fstat in the write permission sets The stream path of writeFile calls `open`, which only the nested command list of `allow-write-file` granted (plugins/fs/permissions/write-all.toml, write-files.toml), so `fs:allow-appdata-write-recursive` and the other write sets rejected streamed writes; seek/fstat were missing for FileHandles created under these sets. Adds open, seek and fstat to write-all and write-files. This widens the write sets slightly (open is already implied by allow-write-file); the generated reference does not list set members, so nothing to regenerate. * use dependencies instead
This commit is contained in:
1 parent
db25585357
commit
6a263bec1c
5 files changed
+147
-65
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
fs: patch
|
||||||
|
fs-js: patch
|
||||||
|
---
|
||||||
|
|
||||||
|
The `write-all` and `write-files` permissions (and so every `allow-*-write[-recursive]` set) are now sets of the `allow-*` command permissions, so they also allow the commands a command depends on. `writeFile` with a `ReadableStream` uses `open` and failed with "not allowed" under these sets.
|
||||||
+114
-38
@@ -77,35 +77,62 @@ const BASE_DIR_VARS: &[&str] = &[
|
|||||||
"APPCACHE",
|
"APPCACHE",
|
||||||
"APPLOG",
|
"APPLOG",
|
||||||
];
|
];
|
||||||
const COMMANDS: &[(&str, &[&str])] = &[
|
/// A command exposed by the plugin.
|
||||||
("mkdir", &[]),
|
struct Command {
|
||||||
("create", &[]),
|
name: &'static str,
|
||||||
("copy_file", &[]),
|
/// Other commands the JS API calls when using this command,
|
||||||
("remove", &[]),
|
/// e.g. `writeFile` with a stream uses `open` and `write`.
|
||||||
("rename", &[]),
|
///
|
||||||
("truncate", &[]),
|
/// The `allow-*` permission of this command also allows its dependencies,
|
||||||
("ftruncate", &[]),
|
/// and hand-written permissions that allow this command must allow them too.
|
||||||
("write", &[]),
|
dependencies: &'static [&'static str],
|
||||||
("write_file", &["open", "write"]),
|
}
|
||||||
("write_text_file", &[]),
|
|
||||||
("read_dir", &[]),
|
impl Command {
|
||||||
("read_file", &[]),
|
const fn new(name: &'static str) -> Self {
|
||||||
("read", &[]),
|
Self {
|
||||||
("open", &[]),
|
name,
|
||||||
("read_text_file", &[]),
|
dependencies: &[],
|
||||||
("read_text_file_lines", &["read_text_file_lines_next"]),
|
}
|
||||||
("read_text_file_lines_next", &[]),
|
}
|
||||||
("seek", &[]),
|
|
||||||
("stat", &[]),
|
const fn dependencies(self, dependencies: &'static [&'static str]) -> Self {
|
||||||
("lstat", &[]),
|
Self {
|
||||||
("fstat", &[]),
|
name: self.name,
|
||||||
("exists", &[]),
|
dependencies,
|
||||||
("watch", &[]),
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const COMMANDS: &[Command] = &[
|
||||||
|
Command::new("mkdir"),
|
||||||
|
Command::new("create"),
|
||||||
|
Command::new("copy_file"),
|
||||||
|
Command::new("remove"),
|
||||||
|
Command::new("rename"),
|
||||||
|
Command::new("truncate"),
|
||||||
|
Command::new("ftruncate"),
|
||||||
|
Command::new("write"),
|
||||||
|
Command::new("write_file").dependencies(&["open", "write"]),
|
||||||
|
Command::new("write_text_file"),
|
||||||
|
Command::new("read_dir"),
|
||||||
|
Command::new("read_file"),
|
||||||
|
Command::new("read"),
|
||||||
|
Command::new("open"),
|
||||||
|
Command::new("read_text_file"),
|
||||||
|
Command::new("read_text_file_lines").dependencies(&["read_text_file_lines_next"]),
|
||||||
|
Command::new("read_text_file_lines_next"),
|
||||||
|
Command::new("seek"),
|
||||||
|
Command::new("stat"),
|
||||||
|
Command::new("lstat"),
|
||||||
|
Command::new("fstat"),
|
||||||
|
Command::new("exists"),
|
||||||
|
Command::new("watch"),
|
||||||
// TODO: Remove this in v3
|
// TODO: Remove this in v3
|
||||||
("unwatch", &[]),
|
Command::new("unwatch"),
|
||||||
("size", &[]),
|
Command::new("size"),
|
||||||
("start_accessing_security_scoped_resource", &[]),
|
Command::new("start_accessing_security_scoped_resource"),
|
||||||
("stop_accessing_security_scoped_resource", &[]),
|
Command::new("stop_accessing_security_scoped_resource"),
|
||||||
];
|
];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -215,8 +242,8 @@ permissions = [
|
|||||||
&COMMANDS
|
&COMMANDS
|
||||||
.iter()
|
.iter()
|
||||||
// FIXME: https://docs.rs/crate/tauri-plugin-fs/2.1.0/builds/1571296
|
// FIXME: https://docs.rs/crate/tauri-plugin-fs/2.1.0/builds/1571296
|
||||||
.filter(|c| c.1.is_empty())
|
.filter(|c| c.dependencies.is_empty())
|
||||||
.map(|c| c.0)
|
.map(|c| c.name)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.global_api_script_path("./api-iife.js")
|
.global_api_script_path("./api-iife.js")
|
||||||
@@ -224,10 +251,16 @@ permissions = [
|
|||||||
.android_path("android")
|
.android_path("android")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// workaround to include nested permissions as `tauri_plugin` doesn't support it
|
validate_command_dependencies(Path::new("permissions"));
|
||||||
|
|
||||||
|
// workaround to include command dependencies as `tauri_plugin` doesn't support it
|
||||||
let permissions_dir = autogenerated.join("commands");
|
let permissions_dir = autogenerated.join("commands");
|
||||||
for (command, nested_commands) in COMMANDS {
|
for Command {
|
||||||
if nested_commands.is_empty() {
|
name: command,
|
||||||
|
dependencies,
|
||||||
|
} in COMMANDS
|
||||||
|
{
|
||||||
|
if dependencies.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,11 +277,10 @@ permissions = [
|
|||||||
.iter_mut()
|
.iter_mut()
|
||||||
.filter(|p| p.identifier.starts_with("allow"))
|
.filter(|p| p.identifier.starts_with("allow"))
|
||||||
{
|
{
|
||||||
for c in nested_commands.iter().map(|s| s.to_string()) {
|
p.commands.allow = std::iter::once(command)
|
||||||
if !p.commands.allow.contains(&c) {
|
.chain(dependencies.iter())
|
||||||
p.commands.allow.push(c);
|
.map(|c| c.to_string())
|
||||||
}
|
.collect();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let out = toml::to_string_pretty(&permission_file)
|
let out = toml::to_string_pretty(&permission_file)
|
||||||
@@ -267,3 +299,47 @@ permissions = [
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensures every hand-written permission that allows a command also allows its dependencies.
|
||||||
|
fn validate_command_dependencies(permissions_dir: &Path) {
|
||||||
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
|
for entry in std::fs::read_dir(permissions_dir).expect("failed to read permissions dir") {
|
||||||
|
let path = entry.expect("failed to read permissions dir entry").path();
|
||||||
|
if path.extension().and_then(|ext| ext.to_str()) != Some("toml") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
println!("cargo:rerun-if-changed={}", path.display());
|
||||||
|
|
||||||
|
let content = std::fs::read_to_string(&path)
|
||||||
|
.unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
|
||||||
|
let permission_file = toml::from_str::<PermissionFile>(&content)
|
||||||
|
.unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display()));
|
||||||
|
|
||||||
|
for permission in &permission_file.permission {
|
||||||
|
let allowed = &permission.commands.allow;
|
||||||
|
for command in COMMANDS
|
||||||
|
.iter()
|
||||||
|
.filter(|c| allowed.iter().any(|a| a == c.name))
|
||||||
|
{
|
||||||
|
for dependency in command.dependencies {
|
||||||
|
if !allowed.iter().any(|a| a == dependency) {
|
||||||
|
errors.push(format!(
|
||||||
|
"{}: `{}` allows `{}` but not its dependency `{dependency}`",
|
||||||
|
path.display(),
|
||||||
|
permission.identifier,
|
||||||
|
command.name,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.is_empty() {
|
||||||
|
panic!(
|
||||||
|
"permissions are missing command dependencies:\n{}",
|
||||||
|
errors.join("\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2035,16 +2035,16 @@
|
|||||||
"markdownDescription": "An empty permission you can use to modify the global scope.\n\n## Example\n\n```json\n{\n \"identifier\": \"read-documents\",\n \"windows\": [\"main\"],\n \"permissions\": [\n \"fs:allow-read\",\n {\n \"identifier\": \"fs:scope\",\n \"allow\": [\n \"$APPDATA/documents/**/*\"\n ],\n \"deny\": [\n \"$APPDATA/documents/secret.txt\"\n ]\n }\n ]\n}\n```\n"
|
"markdownDescription": "An empty permission you can use to modify the global scope.\n\n## Example\n\n```json\n{\n \"identifier\": \"read-documents\",\n \"windows\": [\"main\"],\n \"permissions\": [\n \"fs:allow-read\",\n {\n \"identifier\": \"fs:scope\",\n \"allow\": [\n \"$APPDATA/documents/**/*\"\n ],\n \"deny\": [\n \"$APPDATA/documents/secret.txt\"\n ]\n }\n ]\n}\n```\n"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "This enables all write related commands without any pre-configured accessible paths.",
|
"description": "This enables all write related commands without any pre-configured accessible paths.\n#### This permission set includes:\n\n- `allow-mkdir`\n- `allow-create`\n- `allow-copy-file`\n- `allow-remove`\n- `allow-rename`\n- `allow-truncate`\n- `allow-ftruncate`\n- `allow-write`\n- `allow-write-file`\n- `allow-write-text-file`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "write-all",
|
"const": "write-all",
|
||||||
"markdownDescription": "This enables all write related commands without any pre-configured accessible paths."
|
"markdownDescription": "This enables all write related commands without any pre-configured accessible paths.\n#### This permission set includes:\n\n- `allow-mkdir`\n- `allow-create`\n- `allow-copy-file`\n- `allow-remove`\n- `allow-rename`\n- `allow-truncate`\n- `allow-ftruncate`\n- `allow-write`\n- `allow-write-file`\n- `allow-write-text-file`"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "This enables all file write related commands without any pre-configured accessible paths.",
|
"description": "This enables all file write related commands without any pre-configured accessible paths.\n#### This permission set includes:\n\n- `allow-create`\n- `allow-copy-file`\n- `allow-remove`\n- `allow-rename`\n- `allow-truncate`\n- `allow-ftruncate`\n- `allow-write`\n- `allow-write-file`\n- `allow-write-text-file`",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"const": "write-files",
|
"const": "write-files",
|
||||||
"markdownDescription": "This enables all file write related commands without any pre-configured accessible paths."
|
"markdownDescription": "This enables all file write related commands without any pre-configured accessible paths.\n#### This permission set includes:\n\n- `allow-create`\n- `allow-copy-file`\n- `allow-remove`\n- `allow-rename`\n- `allow-truncate`\n- `allow-ftruncate`\n- `allow-write`\n- `allow-write-file`\n- `allow-write-text-file`"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
"$schema" = "schemas/schema.json"
|
"$schema" = "schemas/schema.json"
|
||||||
|
|
||||||
[[permission]]
|
[[set]]
|
||||||
identifier = "write-all"
|
identifier = "write-all"
|
||||||
description = "This enables all write related commands without any pre-configured accessible paths."
|
description = "This enables all write related commands without any pre-configured accessible paths."
|
||||||
commands.allow = [
|
permissions = [
|
||||||
"mkdir",
|
"allow-mkdir",
|
||||||
"create",
|
"allow-create",
|
||||||
"copy_file",
|
"allow-copy-file",
|
||||||
"remove",
|
"allow-remove",
|
||||||
"rename",
|
"allow-rename",
|
||||||
"truncate",
|
"allow-truncate",
|
||||||
"ftruncate",
|
"allow-ftruncate",
|
||||||
"write",
|
"allow-write",
|
||||||
"write_file",
|
"allow-write-file",
|
||||||
"write_text_file",
|
"allow-write-text-file",
|
||||||
]
|
]
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
"$schema" = "schemas/schema.json"
|
"$schema" = "schemas/schema.json"
|
||||||
|
|
||||||
[[permission]]
|
[[set]]
|
||||||
identifier = "write-files"
|
identifier = "write-files"
|
||||||
description = "This enables all file write related commands without any pre-configured accessible paths."
|
description = "This enables all file write related commands without any pre-configured accessible paths."
|
||||||
commands.allow = [
|
permissions = [
|
||||||
"create",
|
"allow-create",
|
||||||
"copy_file",
|
"allow-copy-file",
|
||||||
"remove",
|
"allow-remove",
|
||||||
"rename",
|
"allow-rename",
|
||||||
"truncate",
|
"allow-truncate",
|
||||||
"ftruncate",
|
"allow-ftruncate",
|
||||||
"write",
|
"allow-write",
|
||||||
"write_file",
|
"allow-write-file",
|
||||||
"write_text_file",
|
"allow-write-text-file",
|
||||||
]
|
]
|
||||||
Reference in new issue
Block a user