.claude/skills/tauri-2/tauri-impl/tauri-impl-security/SKILL.md
Use when hardening Tauri 2 app security, configuring CSP, reviewing permissions, or implementing isolation patterns. Prevents overly permissive CSP, disabled prototype freeze, and unscoped file/shell/http permissions in production. Covers CSP configuration, Tauri protocols, freezePrototype, isolation pattern, scope-based access control, and dangerous permissions. Keywords: tauri security, CSP, Content Security Policy, freezePrototype, isolation pattern, scope, permissions audit.
npx skillsauth add OpenAEC-Foundation/OpenAEC-Workspace-Composer tauri-impl-securityInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
| Layer | Mechanism | Configuration |
|-------|-----------|---------------|
| Content Security Policy | Restricts resource loading in webview | app.security.csp in tauri.conf.json |
| Permissions | Define which IPC commands are allowed/denied | src-tauri/permissions/*.toml |
| Capabilities | Bind permissions to specific windows | src-tauri/capabilities/*.json |
| Scopes | Restrict what data commands can access | Within permission definitions |
| Isolation Pattern | Separate IPC from frontend context | app.security.pattern |
| Prototype Freeze | Prevent prototype pollution | app.security.freezePrototype |
| Protocol | Purpose | CSP Directive |
|----------|---------|---------------|
| tauri: | Internal protocol for serving frontend assets | default-src |
| asset: / https://asset.localhost | Access bundled resources and filesystem assets | img-src, media-src |
| ipc: / http://ipc.localhost | IPC communication between frontend and Rust | connect-src |
NEVER set csp to null in production -- this disables all content security restrictions, exposing the app to XSS and injection attacks.
NEVER use "windows": ["*"] with broad permissions -- this grants all windows identical access. ALWAYS use specific window labels.
NEVER set dangerousDisableAssetCspModification: true unless you fully understand that it removes automatic CSP nonce injection for asset loading.
NEVER grant shell:default without scope restrictions -- this allows arbitrary command execution. ALWAYS define explicit command scopes.
ALWAYS enable freezePrototype: true in production -- it prevents JavaScript prototype pollution attacks.
ALWAYS add capabilities for every plugin you install -- plugins do NOT automatically receive permissions.
ALWAYS define deny rules for sensitive paths (e.g., $HOME/.ssh/*) when granting filesystem access.
{
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: https://asset.localhost data:; font-src 'self' data:; connect-src ipc: http://ipc.localhost https://api.example.com; media-src 'self' asset: https://asset.localhost"
}
}
}
Common CSP directives for Tauri:
| Directive | Recommended Value | Purpose |
|-----------|-------------------|---------|
| default-src | 'self' | Fallback for all resource types |
| script-src | 'self' | JavaScript sources |
| style-src | 'self' 'unsafe-inline' | Stylesheets (inline needed for most frameworks) |
| img-src | 'self' asset: https://asset.localhost data: | Image sources including asset protocol |
| font-src | 'self' data: | Font files |
| connect-src | ipc: http://ipc.localhost | IPC + any external APIs |
| media-src | 'self' asset: https://asset.localhost | Audio/video sources |
{
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'",
"freezePrototype": true,
"dangerousDisableAssetCspModification": false,
"assetProtocol": {
"enable": true,
"scope": ["$APPDATA/**", "$RESOURCE/**"]
},
"pattern": {
"use": "brownfield"
}
}
}
}
| Property | Description |
|----------|-------------|
| freezePrototype | Freezes Object.prototype to prevent pollution attacks |
| dangerousDisableAssetCspModification | Disables automatic CSP nonce injection for the asset protocol |
| assetProtocol.enable | Enable the asset: protocol for file access |
| assetProtocol.scope | Glob patterns defining allowed asset paths |
| pattern.use | "brownfield" (default) or "isolation" |
Permissions follow the naming convention:
<plugin>:default -- Default permission set<plugin>:allow-<command> -- Allow a specific command<plugin>:deny-<command> -- Deny a specific commandDefining custom permissions (src-tauri/permissions/my-commands.toml):
[[permission]]
identifier = "allow-read-file"
description = "Enables the read_file command"
commands.allow = ["read_file"]
[[permission]]
identifier = "deny-write-file"
description = "Blocks the write_file command"
commands.deny = ["write_file"]
Permission with scopes:
[[permission]]
identifier = "scope-home"
description = "Access to files in $HOME but not .ssh"
[[scope.allow]]
path = "$HOME/*"
[[scope.deny]]
path = "$HOME/.ssh/*"
Permission sets (bundle multiple):
[[set]]
identifier = "allow-home-read-extended"
description = "Read access + directory creation in $HOME"
permissions = [
"fs:read-files",
"fs:scope-home",
"fs:allow-mkdir"
]
Capabilities tie permissions to specific windows. Files in src-tauri/capabilities/ are automatically enabled.
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "main-capability",
"description": "Capability for the main window",
"windows": ["main"],
"permissions": [
"core:path:default",
"core:event:default",
"core:window:default",
"core:app:default",
"core:resources:default",
"core:menu:default",
"core:tray:default"
]
}
Platform-specific capabilities:
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop-capability",
"windows": ["main"],
"platforms": ["linux", "macOS", "windows"],
"permissions": ["global-shortcut:allow-register"]
}
Remote URL access:
{
"$schema": "../gen/schemas/remote-schema.json",
"identifier": "remote-capability",
"windows": ["main"],
"remote": {
"urls": ["https://*.tauri.app"]
},
"permissions": ["nfc:allow-scan"]
}
File system scopes:
{
"permissions": [
{
"identifier": "fs:allow-read-file",
"allow": [{ "path": "$APPDATA/**" }],
"deny": [{ "path": "$APPDATA/secrets/**" }]
}
]
}
HTTP URL scopes:
{
"permissions": [
{
"identifier": "http:default",
"allow": [{ "url": "https://api.example.com/*" }],
"deny": [{ "url": "https://api.example.com/admin/*" }]
}
]
}
Shell command scopes:
{
"permissions": [
{
"identifier": "shell:allow-execute",
"allow": [{
"name": "exec-sh",
"cmd": "sh",
"args": ["-c", { "validator": "\\S+" }],
"sidecar": false
}]
}
]
}
use tauri::ipc::{CommandScope, GlobalScope};
#[tauri::command]
async fn scoped_command<R: tauri::Runtime>(
command_scope: CommandScope<'_, ScopeEntry>,
global_scope: GlobalScope<'_, ScopeEntry>,
) -> Result<(), String> {
let allowed = command_scope.allows();
let denied = command_scope.denies();
// Validate access against scopes
Ok(())
}
On Windows, production frontend files load from http://tauri.localhost instead of https://tauri.localhost. This resets IndexedDB, LocalStorage, and Cookies unless:
dangerousUseHttpScheme was enabled in v1app.windows[].useHttpsScheme is set to true in v2Review these permissions carefully before granting:
| Permission | Risk | Mitigation |
|------------|------|------------|
| shell:allow-execute | Arbitrary command execution | Define explicit command scopes with validators |
| fs:allow-write-file with broad scope | Data destruction/exfiltration | Restrict to $APPDATA or specific directories |
| http:default without URL scope | Unrestricted network access | Define allowed URL patterns |
| clipboard-manager:allow-read-text | Read sensitive clipboard data | Only grant when functionally required |
| "windows": ["*"] | All windows get same permissions | Use specific window labels |
| dangerousDisableAssetCspModification | Removes CSP nonce protection | Almost never needed |
The isolation pattern creates a separate execution context for the IPC bridge, preventing the frontend from directly accessing Tauri internals:
{
"app": {
"security": {
"pattern": {
"use": "isolation",
"options": {
"dir": "../isolation-app"
}
}
}
}
}
Use "brownfield" (default) for standard apps. Use "isolation" for apps loading untrusted third-party content.
These require no plugin installation but still need explicit capability grants:
core:path:default
core:event:default
core:window:default
core:app:default
core:resources:default
core:menu:default
core:tray:default
core:window:allow-set-title
core:window:allow-close
core:window:allow-minimize
core:window:allow-maximize
development
Use when integrating Vite with a backend framework, rendering Vite assets from server-side templates, or setting up dev/production HTML serving. Prevents incorrect manifest.json traversal and missing CSS chunk resolution in production. Covers build.manifest configuration, .vite/manifest.json structure, ManifestChunk properties, dev mode HTML setup, production rendering, CSS/JS chunk resolution, and modulepreload polyfill. Keywords: backend integration, manifest.json, ManifestChunk, Django, Laravel, Rails, modulepreload.
development
Use when encountering dev server startup failures, HMR issues, proxy errors, CORS blocks, or module not found errors during development. Prevents misconfiguring server.hmr behind reverse proxies and forgetting appType: 'custom' in middleware mode. Covers HMR full-reload debugging, proxy configuration, CORS setup, HTTPS certificates, server.fs.strict violations, port conflicts, WebSocket failures, file watcher issues, and middleware mode. Keywords: dev server, HMR, proxy, CORS, HTTPS, WebSocket, port conflict, server.fs.strict, middleware mode, file watcher.
development
Use when encountering pre-bundling errors, dependency resolution failures, stale cache issues, or slow development server startup. Prevents excluding CJS dependencies from pre-bundling (which breaks runtime module resolution) and misconfiguring optimizeDeps. Covers CJS/ESM conversion failures, missing dependency auto-discovery, optimizeDeps configuration, monorepo linked dependencies, cache invalidation, browser cache staleness, and large dependency tree performance. Keywords: pre-bundling, optimizeDeps, CJS, ESM, cache, dependency resolution, monorepo, node_modules/.vite.
development
Use when encountering Vite build failures, chunk size warnings, or version-specific build errors. Prevents the common mistake of using deprecated rollupOptions in v8 or misconfiguring build targets and minifiers. Covers Rolldown/Rollup bundling failures, CSS minification errors, sourcemap problems, library mode build failures, BundleError handling, and asset processing errors. Keywords: build error, Rolldown, chunk size, sourcemap, library mode, minify, BundleError, rollupOptions, build.target.