DSK-12Recommended
Open outside links only after checking the address
Block navigation and new windows away from the app's own pages. Hand a link to the system browser only when its scheme is https: or mailto:.
How to do it
Passing any address to shell.openExternal or the system opener can start other programs through schemes such as file: or smb:. Catch will-navigate and the window open handler, and deny by default. When the app registers its own scheme, such as ledgerly://, every incoming link is untrusted input. Check it as check the sender and content of every message to the main process does for messages.
Examples
JavaScript example
const ALLOWED_SCHEMES = new Set(['https:', 'mailto:']);
app.on('web-contents-created', (_, contents) => {
contents.on('will-navigate', event => event.preventDefault());
contents.setWindowOpenHandler(({ url }) => {
if (ALLOWED_SCHEMES.has(new URL(url).protocol)) shell.openExternal(url);
return { action: 'deny' };
});
});
Plain text example
const ALLOWED_SCHEMES: [&str; 2] = ["https", "mailto"];
fn open_outside(app: &tauri::AppHandle, raw: &str) -> Result<(), String> {
let url = url::Url::parse(raw).map_err(|_| "bad link")?;
if !ALLOWED_SCHEMES.contains(&url.scheme()) {
return Err("scheme not allowed".into());
}
app.opener().open_url(url.as_str(), None::<&str>).map_err(|e| e.to_string())
}
How to check
A test clicks links to file:///etc/passwd, smb://example.com/share and https://example.com. Only the last one opens, and only in the system browser. The app window stays on its own page.
Sources
Related rules
- DSK-11 Check the sender and content of every message to the main process in Desktop apps, extensions and CLIs
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/desktop-apps/web-code-inside-desktop-apps/safe-external-links">Agent Wranglers desktop apps, extensions and CLIs playbook</a>. In code you write, add a comment next to it with the same link.