Skip to content

Commit ed04cc3

Browse files
authored
feat(core): enhance IPC permission error message (#10664)
* feat(core): enhance IPC permission error message - include more information about current URL and allowed origins - enhance formatting of the error message * plugin not found & command not found * lint
1 parent 7796a8f commit ed04cc3

File tree

2 files changed

+231
-26
lines changed

2 files changed

+231
-26
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch:enhance
3+
---
4+
5+
Include more information in the IPC permission error message.

core/tauri/src/ipc/authority.rs

Lines changed: 226 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ impl RuntimeAuthority {
343343
webview: &str,
344344
origin: &Origin,
345345
) -> String {
346-
fn print_references(resolved: Vec<&ResolvedCommand>) -> String {
346+
fn print_references(resolved: &[ResolvedCommand]) -> String {
347347
resolved
348348
.iter()
349349
.map(|r| {
@@ -356,6 +356,53 @@ impl RuntimeAuthority {
356356
.join(" || ")
357357
}
358358

359+
fn print_allowed_on(resolved: &[ResolvedCommand]) -> String {
360+
if resolved.is_empty() {
361+
"command not allowed on any window/webview/URL context".to_string()
362+
} else {
363+
let mut s = "allowed on: ".to_string();
364+
365+
let last_index = resolved.len() - 1;
366+
for (index, cmd) in resolved.iter().enumerate() {
367+
let windows = cmd
368+
.windows
369+
.iter()
370+
.map(|w| format!("\"{}\"", w.as_str()))
371+
.collect::<Vec<_>>()
372+
.join(", ");
373+
let webviews = cmd
374+
.webviews
375+
.iter()
376+
.map(|w| format!("\"{}\"", w.as_str()))
377+
.collect::<Vec<_>>()
378+
.join(", ");
379+
380+
s.push('[');
381+
382+
if !windows.is_empty() {
383+
s.push_str(&format!("windows: {windows}, "));
384+
}
385+
386+
if !webviews.is_empty() {
387+
s.push_str(&format!("webviews: {webviews}, "));
388+
}
389+
390+
match &cmd.context {
391+
ExecutionContext::Local => s.push_str("URL: local"),
392+
ExecutionContext::Remote { url } => s.push_str(&format!("URL: {}", url.as_str())),
393+
}
394+
395+
s.push(']');
396+
397+
if index != last_index {
398+
s.push_str(", ");
399+
}
400+
}
401+
402+
s
403+
}
404+
}
405+
359406
fn has_permissions_allowing_command(
360407
manifest: &crate::utils::acl::manifest::Manifest,
361408
set: &crate::utils::acl::PermissionSet,
@@ -393,35 +440,34 @@ impl RuntimeAuthority {
393440
format!("{key}.{command_name}")
394441
};
395442

396-
if let Some(resolved) = self.denied_commands.get(&command).map(|r| {
397-
r.iter()
398-
.filter(|cmd| origin.matches(&cmd.context))
399-
.collect()
400-
}) {
443+
if let Some(resolved) = self.denied_commands.get(&command) {
401444
format!(
402-
"{command_pretty_name} denied on origin {origin}, referenced by: {}",
445+
"{command_pretty_name} explicitly denied on origin {origin}\n\nreferenced by: {}",
403446
print_references(resolved)
404447
)
405448
} else {
406449
let command_matches = self.allowed_commands.get(&command);
407450

408-
if let Some(resolved) = self.allowed_commands.get(&command).map(|r| {
409-
r.iter()
451+
if let Some(resolved) = self.allowed_commands.get(&command) {
452+
let resolved_matching_origin = resolved
453+
.iter()
410454
.filter(|cmd| origin.matches(&cmd.context))
411-
.collect::<Vec<&ResolvedCommand>>()
412-
}) {
413-
if resolved
455+
.collect::<Vec<&ResolvedCommand>>();
456+
if resolved_matching_origin
414457
.iter()
415458
.any(|cmd| cmd.webviews.iter().any(|w| w.matches(webview)))
416-
|| resolved
459+
|| resolved_matching_origin
417460
.iter()
418461
.any(|cmd| cmd.windows.iter().any(|w| w.matches(window)))
419462
{
420463
"allowed".to_string()
421464
} else {
422-
format!("{command_pretty_name} not allowed on window {window}, webview {webview}, allowed windows: {}, allowed webviews: {}, referenced by {}",
423-
resolved.iter().flat_map(|cmd| cmd.windows.iter().map(|w| w.as_str())).collect::<Vec<_>>().join(", "),
424-
resolved.iter().flat_map(|cmd| cmd.webviews.iter().map(|w| w.as_str())).collect::<Vec<_>>().join(", "),
465+
format!("{command_pretty_name} not allowed on window \"{window}\", webview \"{webview}\", URL: {}\n\n{}\n\nreferenced by: {}",
466+
match origin {
467+
Origin::Local => "local",
468+
Origin::Remote { url } => url.as_str()
469+
},
470+
print_allowed_on(resolved),
425471
print_references(resolved)
426472
)
427473
}
@@ -451,20 +497,25 @@ impl RuntimeAuthority {
451497

452498
permissions_referencing_command.sort();
453499

454-
format!(
455-
"Permissions associated with this command: {}",
456-
permissions_referencing_command
457-
.iter()
458-
.map(|p| if key == APP_ACL_KEY {
500+
let associated_permissions = permissions_referencing_command
501+
.iter()
502+
.map(|p| {
503+
if key == APP_ACL_KEY {
459504
p.to_string()
460505
} else {
461506
format!("{key}:{p}")
462-
})
463-
.collect::<Vec<_>>()
464-
.join(", ")
465-
)
507+
}
508+
})
509+
.collect::<Vec<_>>()
510+
.join(", ");
511+
512+
if associated_permissions.is_empty() {
513+
"Command not found".to_string()
514+
} else {
515+
format!("Permissions associated with this command: {associated_permissions}")
516+
}
466517
} else {
467-
"Plugin did not define its manifest".to_string()
518+
"Plugin not found".to_string()
468519
};
469520

470521
if let Some(resolved_cmds) = command_matches {
@@ -985,4 +1036,153 @@ mod tests {
9851036
.resolve_access(command, window, webview, &Origin::Local)
9861037
.is_none());
9871038
}
1039+
1040+
#[cfg(debug_assertions)]
1041+
#[test]
1042+
fn resolve_access_message() {
1043+
use tauri_utils::acl::manifest::Manifest;
1044+
1045+
let plugin_name = "myplugin";
1046+
let command_allowed_on_window = "my-command-window";
1047+
let command_allowed_on_webview_window = "my-command-webview-window";
1048+
let window = "main-*";
1049+
let webview = "webview-*";
1050+
let remote_url = "http://localhost:8080";
1051+
1052+
let referenced_by = tauri_utils::acl::resolved::ResolvedCommandReference {
1053+
capability: "maincap".to_string(),
1054+
permission: "allow-command".to_string(),
1055+
};
1056+
1057+
let resolved_window_cmd = ResolvedCommand {
1058+
windows: vec![Pattern::new(window).unwrap()],
1059+
referenced_by: referenced_by.clone(),
1060+
..Default::default()
1061+
};
1062+
let resolved_webview_window_cmd = ResolvedCommand {
1063+
windows: vec![Pattern::new(window).unwrap()],
1064+
webviews: vec![Pattern::new(webview).unwrap()],
1065+
referenced_by: referenced_by.clone(),
1066+
..Default::default()
1067+
};
1068+
let resolved_webview_window_remote_cmd = ResolvedCommand {
1069+
windows: vec![Pattern::new(window).unwrap()],
1070+
webviews: vec![Pattern::new(webview).unwrap()],
1071+
referenced_by: referenced_by.clone(),
1072+
context: ExecutionContext::Remote {
1073+
url: remote_url.parse().unwrap(),
1074+
},
1075+
..Default::default()
1076+
};
1077+
1078+
let allowed_commands = [
1079+
(
1080+
format!("plugin:{plugin_name}|{command_allowed_on_window}"),
1081+
vec![resolved_window_cmd],
1082+
),
1083+
(
1084+
format!("plugin:{plugin_name}|{command_allowed_on_webview_window}"),
1085+
vec![
1086+
resolved_webview_window_cmd,
1087+
resolved_webview_window_remote_cmd,
1088+
],
1089+
),
1090+
]
1091+
.into_iter()
1092+
.collect();
1093+
1094+
let authority = RuntimeAuthority::new(
1095+
[(
1096+
plugin_name.to_string(),
1097+
Manifest {
1098+
default_permission: None,
1099+
permissions: Default::default(),
1100+
permission_sets: Default::default(),
1101+
global_scope_schema: None,
1102+
},
1103+
)]
1104+
.into_iter()
1105+
.collect(),
1106+
Resolved {
1107+
allowed_commands,
1108+
..Default::default()
1109+
},
1110+
);
1111+
1112+
// unknown plugin
1113+
assert_eq!(
1114+
authority.resolve_access_message(
1115+
"unknown-plugin",
1116+
command_allowed_on_window,
1117+
window,
1118+
webview,
1119+
&Origin::Local
1120+
),
1121+
"unknown-plugin.my-command-window not allowed. Plugin not found"
1122+
);
1123+
1124+
// unknown command
1125+
assert_eq!(
1126+
authority.resolve_access_message(
1127+
plugin_name,
1128+
"unknown-command",
1129+
window,
1130+
webview,
1131+
&Origin::Local
1132+
),
1133+
"myplugin.unknown-command not allowed. Command not found"
1134+
);
1135+
1136+
// window/webview do not match
1137+
assert_eq!(
1138+
authority.resolve_access_message(
1139+
plugin_name,
1140+
command_allowed_on_window,
1141+
"other-window",
1142+
"any-webview",
1143+
&Origin::Local
1144+
),
1145+
"myplugin.my-command-window not allowed on window \"other-window\", webview \"any-webview\", URL: local\n\nallowed on: [windows: \"main-*\", URL: local]\n\nreferenced by: capability: maincap, permission: allow-command"
1146+
);
1147+
1148+
// window matches, but not origin
1149+
assert_eq!(
1150+
authority.resolve_access_message(
1151+
plugin_name,
1152+
command_allowed_on_window,
1153+
window,
1154+
"any-webview",
1155+
&Origin::Remote {
1156+
url: "http://localhst".parse().unwrap()
1157+
}
1158+
),
1159+
"myplugin.my-command-window not allowed on window \"main-*\", webview \"any-webview\", URL: http://localhst/\n\nallowed on: [windows: \"main-*\", URL: local]\n\nreferenced by: capability: maincap, permission: allow-command"
1160+
);
1161+
1162+
// window/webview do not match
1163+
assert_eq!(
1164+
authority.resolve_access_message(
1165+
plugin_name,
1166+
command_allowed_on_webview_window,
1167+
"other-window",
1168+
"other-webview",
1169+
&Origin::Local
1170+
),
1171+
"myplugin.my-command-webview-window not allowed on window \"other-window\", webview \"other-webview\", URL: local\n\nallowed on: [windows: \"main-*\", webviews: \"webview-*\", URL: local], [windows: \"main-*\", webviews: \"webview-*\", URL: http://localhost:8080]\n\nreferenced by: capability: maincap, permission: allow-command || capability: maincap, permission: allow-command"
1172+
);
1173+
1174+
// window/webview matches, but not origin
1175+
assert_eq!(
1176+
authority.resolve_access_message(
1177+
plugin_name,
1178+
command_allowed_on_webview_window,
1179+
window,
1180+
webview,
1181+
&Origin::Remote {
1182+
url: "http://localhost:123".parse().unwrap()
1183+
}
1184+
),
1185+
"myplugin.my-command-webview-window not allowed on window \"main-*\", webview \"webview-*\", URL: http://localhost:123/\n\nallowed on: [windows: \"main-*\", webviews: \"webview-*\", URL: local], [windows: \"main-*\", webviews: \"webview-*\", URL: http://localhost:8080]\n\nreferenced by: capability: maincap, permission: allow-command || capability: maincap, permission: allow-command"
1186+
);
1187+
}
9881188
}

0 commit comments

Comments
 (0)