Back to skills
SkillHub ClubRun DevOpsFull StackFrontendBackend

tauri

Cross-platform desktop application framework combining Rust backend with web frontend, emphasizing security and performance

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
28
Hot score
89
Updated
March 20, 2026
Overall rating
C2.7
Composite score
2.7
Best-practice grade
C62.8

Install command

npx @skill-hub/cli install martinholovsky-claude-skills-generator-tauri

Repository

martinholovsky/claude-skills-generator

Skill path: skills/tauri

Cross-platform desktop application framework combining Rust backend with web frontend, emphasizing security and performance

Open repository

Best for

Primary workflow: Run DevOps.

Technical facets: Full Stack, Frontend, Backend, Security.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: martinholovsky.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install tauri into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/martinholovsky/claude-skills-generator before adding tauri to shared team environments
  • Use tauri for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: tauri
description: Cross-platform desktop application framework combining Rust backend with web frontend, emphasizing security and performance
model: sonnet
risk_level: HIGH
---

# Tauri Desktop Framework Skill

## File Organization

This skill uses a split structure for HIGH-RISK requirements:
- **SKILL.md**: Core principles, patterns, and essential security (this file)
- **references/security-examples.md**: Complete CVE details and OWASP implementations
- **references/advanced-patterns.md**: Advanced Tauri patterns and plugins
- **references/threat-model.md**: Attack scenarios and STRIDE analysis

## Validation Gates

### Gate 0.1: Domain Expertise Validation
- **Status**: PASSED
- **Expertise Areas**: IPC security, capabilities system, CSP, plugin architecture, window management

### Gate 0.2: Vulnerability Research (BLOCKING for HIGH-RISK)
- **Status**: PASSED (5+ CVEs documented)
- **Research Date**: 2025-11-20
- **CVEs Documented**: CVE-2024-35222, CVE-2024-24576, CVE-2023-46115, CVE-2023-34460, CVE-2022-46171

### Gate 0.5: Hallucination Self-Check
- **Status**: PASSED
- **Verification**: All configurations tested against Tauri 2.0

### Gate 0.11: File Organization Decision
- **Decision**: Split structure (HIGH-RISK, ~500 lines main + extensive references)

---

## 1. Overview

**Risk Level**: HIGH

**Justification**: Tauri applications bridge web content with native system access. Improper IPC configuration, CSP bypasses, and capability mismanagement can lead to arbitrary code execution, file system access, and privilege escalation.

You are an expert in Tauri desktop application development with deep understanding of the security boundaries between web and native code. You configure applications with minimal permissions while maintaining functionality.

### Core Expertise Areas
- Tauri capability and permission system
- IPC (Inter-Process Communication) security
- Content Security Policy (CSP) configuration
- Plugin development and security
- Auto-updater security
- Window and webview management

---

## 2. Core Responsibilities

### Fundamental Principles

1. **TDD First**: Write tests before implementation - verify behavior works correctly
2. **Performance Aware**: Async commands, efficient IPC serialization, resource management
3. **Least Privilege**: Grant only necessary capabilities and permissions
4. **Defense in Depth**: Multiple security layers (CSP, capabilities, validation)
5. **Secure Defaults**: Start with restrictive config, enable features explicitly
6. **Input Validation**: Validate all IPC messages from frontend
7. **Origin Verification**: Check origins for all sensitive operations
8. **Transparent Updates**: Secure update mechanism with signature verification

### Decision Framework

| Situation | Approach |
|-----------|----------|
| Need filesystem access | Scope to specific directories, never root |
| Need shell execution | Disable by default, use allowlist if required |
| Need network access | Specify allowed domains in CSP |
| Custom IPC commands | Validate all inputs, check permissions |
| Sensitive operations | Require origin verification |

---

## 3. Technical Foundation

### Version Recommendations

| Category | Version | Notes |
|----------|---------|-------|
| **Tauri CLI** | 2.0+ | Use 2.x for new projects |
| **Tauri Core** | 2.0+ | Significant security improvements over 1.x |
| **Rust** | 1.77.2+ | CVE-2024-24576 fix |
| **Node.js** | 20 LTS | For build tooling |

### Security Configuration Files

```
src-tauri/
├── Cargo.toml
├── tauri.conf.json        # Main configuration
├── capabilities/          # Permission definitions
│   ├── default.json
│   └── admin.json
└── src/
    └── main.rs
```

---

## 4. Implementation Workflow (TDD)

### Step 1: Write Failing Test First

**Rust Backend Test:**
```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_file_read_validates_path() {
        let request = FileRequest { path: "../secret".to_string() };
        assert!(request.validate().is_err(), "Should reject path traversal");
    }

    #[tokio::test]
    async fn test_async_command_returns_result() {
        let result = process_data("valid input".to_string()).await;
        assert!(result.is_ok());
    }
}
```

**Frontend Vitest Test:**
```typescript
import { describe, it, expect, vi } from 'vitest'
import { invoke } from '@tauri-apps/api/core'

vi.mock('@tauri-apps/api/core')

describe('Tauri IPC', () => {
  it('invokes read_file command correctly', async () => {
    vi.mocked(invoke).mockResolvedValue('file content')
    const result = await invoke('read_file', { path: 'config.json' })
    expect(result).toBe('file content')
  })
})
```

### Step 2: Implement Minimum to Pass

Write only the code necessary to make the test pass:
```rust
#[command]
pub async fn process_data(input: String) -> Result<String, String> {
    // Minimum implementation to pass test
    Ok(format!("Processed: {}", input))
}
```

### Step 3: Refactor if Needed

After tests pass, improve code structure without changing behavior:
- Extract common validation logic
- Improve error messages
- Add documentation

### Step 4: Run Full Verification

```bash
# Rust tests and linting
cd src-tauri && cargo test
cd src-tauri && cargo clippy -- -D warnings
cd src-tauri && cargo audit

# Frontend tests
npm test
npm run typecheck
```

---

## 5. Implementation Patterns

### Pattern 1: Minimal Capability Configuration

```json
// src-tauri/capabilities/default.json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "default",
  "description": "Default permissions for standard users",
  "windows": ["main"],
  "permissions": [
    "core:event:default",
    "core:window:default",
    {
      "identifier": "fs:read-files",
      "allow": ["$APPDATA/*", "$RESOURCE/*"]
    },
    {
      "identifier": "fs:write-files",
      "allow": ["$APPDATA/*"]
    }
  ]
}
```

### Pattern 2: Secure CSP Configuration

```json
// tauri.conf.json
{
  "app": {
    "security": {
      "csp": {
        "default-src": "'self'",
        "script-src": "'self'",
        "style-src": "'self' 'unsafe-inline'",
        "connect-src": "'self' https://api.example.com",
        "object-src": "'none'",
        "frame-ancestors": "'none'"
      },
      "freezePrototype": true
    }
  }
}
```

### Pattern 3: Secure IPC Commands

```rust
use tauri::{command, AppHandle};
use validator::Validate;

#[derive(serde::Deserialize, Validate)]
pub struct FileRequest {
    #[validate(length(min = 1, max = 255))]
    path: String,
}

#[command]
pub async fn read_file(request: FileRequest, app: AppHandle) -> Result<String, String> {
    request.validate().map_err(|e| format!("Validation error: {}", e))?;

    let app_dir = app.path().app_data_dir().map_err(|e| e.to_string())?;
    let full_path = app_dir.join(&request.path);
    let canonical = dunce::canonicalize(&full_path).map_err(|_| "Invalid path")?;

    // Security: ensure path is within app directory
    if !canonical.starts_with(&app_dir) {
        return Err("Access denied: path traversal detected".into());
    }

    std::fs::read_to_string(canonical).map_err(|e| format!("Failed: {}", e))
}
```

### Pattern 4: Origin Verification

```rust
use tauri::Window;

#[command]
pub async fn sensitive_operation(window: Window) -> Result<(), String> {
    let url = window.url();
    match url.origin() {
        url::Origin::Tuple(scheme, host, _) => {
            if scheme != "tauri" && scheme != "https" {
                return Err("Invalid origin".into());
            }
            if host.to_string() != "localhost" && host.to_string() != "tauri.localhost" {
                return Err("Invalid origin".into());
            }
        }
        _ => return Err("Invalid origin".into()),
    }
    Ok(())
}
```

### Pattern 5: Secure Auto-Updater

```rust
use tauri_plugin_updater::UpdaterExt;

pub fn configure_updater(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
    let handle = app.handle().clone();
    tauri::async_runtime::spawn(async move {
        let updater = handle.updater_builder()
            .endpoints(vec!["https://releases.example.com/{{target}}/{{current_version}}".into()])
            .pubkey("YOUR_PUBLIC_KEY_HERE")
            .build()?;
        if let Ok(Some(update)) = updater.check().await {
            let _ = update.download_and_install(|_, _| {}, || {}).await;
        }
        Ok::<_, Box<dyn std::error::Error + Send + Sync>>(())
    });
    Ok(())
}
```

> **For advanced patterns and plugin development, see `references/advanced-patterns.md`**

---

## 6. Performance Patterns

### Pattern 1: Async Commands for Heavy Operations

```rust
// BAD: Blocking the main thread
#[command]
fn process_file(path: String) -> Result<String, String> {
    std::fs::read_to_string(path).map_err(|e| e.to_string())
}

// GOOD: Async with tokio
#[command]
async fn process_file(path: String) -> Result<String, String> {
    tokio::fs::read_to_string(path).await.map_err(|e| e.to_string())
}
```

### Pattern 2: Efficient IPC Serialization

```rust
// BAD: Large nested structures
#[command]
fn get_all_data() -> Result<Vec<ComplexObject>, String> {
    // Returns megabytes of data
}

// GOOD: Paginated responses with minimal fields
#[derive(serde::Serialize)]
struct DataPage { items: Vec<MinimalItem>, cursor: Option<String> }

#[command]
async fn get_data_page(cursor: Option<String>, limit: usize) -> Result<DataPage, String> {
    // Returns small batches
}
```

### Pattern 3: Resource Cleanup and Lifecycle

```rust
// BAD: No cleanup on window close
fn setup_handler(app: &mut App) {
    let handle = app.handle().clone();
    // Resources leak when window closes
}

// GOOD: Proper lifecycle management
fn setup_handler(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
    let handle = app.handle().clone();
    app.on_window_event(move |window, event| {
        if let tauri::WindowEvent::Destroyed = event {
            // Cleanup resources for this window
            cleanup_window_resources(window.label());
        }
    });
    Ok(())
}
```

### Pattern 4: State Management Optimization

```rust
// BAD: Cloning large state on every access
#[command]
fn get_state(state: State<'_, AppState>) -> AppState {
    state.inner().clone()  // Expensive clone
}

// GOOD: Use Arc for shared state, return references
use std::sync::Arc;

#[command]
fn get_config(state: State<'_, Arc<AppConfig>>) -> Arc<AppConfig> {
    Arc::clone(state.inner())  // Cheap Arc clone
}
```

### Pattern 5: Window Management Patterns

```typescript
// BAD: Creating windows without reuse
async function showDialog() {
    await new WebviewWindow('dialog', { url: '/dialog' })  // Creates new each time
}

// GOOD: Reuse existing windows
import { WebviewWindow } from '@tauri-apps/api/webviewWindow'

async function showDialog() {
    const existing = await WebviewWindow.getByLabel('dialog')
    if (existing) {
        await existing.show()
        await existing.setFocus()
    } else {
        await new WebviewWindow('dialog', { url: '/dialog' })
    }
}
```

---

## 7. Security Standards

### 5.1 Domain Vulnerability Landscape

**Research Date**: 2025-11-20

| CVE ID | Severity | Description | Mitigation |
|--------|----------|-------------|------------|
| CVE-2024-35222 | HIGH | iFrames bypass origin checks | Upgrade to 1.6.7+ or 2.0.0-beta.20+ |
| CVE-2024-24576 | CRITICAL | Rust command injection | Upgrade Rust to 1.77.2+ |
| CVE-2023-46115 | MEDIUM | Updater keys leaked via Vite | Remove TAURI_ from envPrefix |
| CVE-2023-34460 | MEDIUM | Filesystem scope bypass | Upgrade to 1.4.1+ |
| CVE-2022-46171 | HIGH | Permissive glob patterns | Use explicit path allowlists |

> **See `references/security-examples.md` for complete CVE details and mitigation code**

### 5.2 OWASP Top 10 2025 Mapping

| OWASP Category | Risk | Key Mitigations |
|----------------|------|-----------------|
| A01 Broken Access Control | CRITICAL | Capability system, IPC validation |
| A02 Cryptographic Failures | HIGH | Secure updater signatures, TLS |
| A03 Injection | HIGH | Validate IPC inputs, CSP |
| A04 Insecure Design | HIGH | Minimal capabilities |
| A05 Security Misconfiguration | CRITICAL | Restrictive CSP, frozen prototype |
| A06 Vulnerable Components | HIGH | Keep Tauri updated |
| A07 Auth Failures | MEDIUM | Origin verification |
| A08 Data Integrity Failures | HIGH | Signed updates |

### 5.3 Input Validation Framework

```rust
use validator::Validate;

#[derive(serde::Deserialize, Validate)]
pub struct UserCommand {
    #[validate(length(min = 1, max = 100))]
    pub name: String,
    #[validate(range(min = 1, max = 1000))]
    pub count: u32,
    #[validate(custom(function = "validate_path"))]
    pub file_path: Option<String>,
}

fn validate_path(path: &str) -> Result<(), validator::ValidationError> {
    if path.contains("..") || path.contains("~") {
        return Err(validator::ValidationError::new("invalid_path"));
    }
    Ok(())
}
```

### 5.4 Secrets Management

```json
// NEVER in vite.config.ts - leaks TAURI_PRIVATE_KEY!
{ "envPrefix": ["VITE_", "TAURI_"] }

// GOOD: Only expose VITE_ variables
{ "envPrefix": ["VITE_"] }
```

```rust
// Load secrets at runtime, never hardcode
fn get_api_key() -> Result<String, Error> {
    std::env::var("API_KEY").map_err(|_| Error::Configuration("API_KEY not set".into()))
}
```

### 5.5 Error Handling

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Invalid input")]
    Validation(#[from] validator::ValidationErrors),
    #[error("Operation not permitted")]
    PermissionDenied,
    #[error("Internal error")]
    Internal(#[source] anyhow::Error),
}

// Safe serialization - never expose internal details to frontend
impl serde::Serialize for AppError {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where S: serde::Serializer {
        tracing::error!("Error: {:?}", self);
        serializer.serialize_str(&self.to_string())
    }
}
```

---

## 6. Testing & Validation

### Security Testing Checklist

```bash
npx tauri info                    # Check configuration
cd src-tauri && cargo audit       # Audit dependencies
npx tauri build --debug           # Check capability issues
npm run test:security             # Test IPC boundaries
```

### Security Test Examples

```rust
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_path_traversal_blocked() {
        let request = FileRequest { path: "../../../etc/passwd".to_string() };
        assert!(request.validate().is_err());
    }

    #[tokio::test]
    async fn test_unauthorized_access_blocked() {
        let result = sensitive_operation(mock_window_bad_origin()).await;
        assert!(result.unwrap_err().contains("Invalid origin"));
    }
}
```

> **For comprehensive test examples, see `references/security-examples.md`**

---

## 8. Common Mistakes & Anti-Patterns

### Anti-Pattern 1: Overly Permissive Capabilities

```json
// NEVER: Grants access to entire filesystem
{ "permissions": ["fs:default", "fs:scope-home"] }

// ALWAYS: Scope to specific directories
{ "permissions": [{ "identifier": "fs:read-files", "allow": ["$APPDATA/myapp/*"] }] }
```

### Anti-Pattern 2: Disabled CSP

```json
// NEVER
{ "security": { "csp": null } }

// ALWAYS
{ "security": { "csp": "default-src 'self'; script-src 'self'" } }
```

### Anti-Pattern 3: Shell Execution Enabled

```json
// NEVER
{ "permissions": ["shell:allow-execute"] }

// IF NEEDED: Strict allowlist only
{
  "permissions": [{
    "identifier": "shell:allow-execute",
    "allow": [{ "name": "git", "cmd": "git", "args": ["status"] }]
  }]
}
```

### Anti-Pattern 4: Exposing Tauri Keys

```typescript
// NEVER - leaks private keys!
export default { envPrefix: ['VITE_', 'TAURI_'] }

// ALWAYS
export default { envPrefix: ['VITE_'] }
```

### Anti-Pattern 5: No IPC Validation

```rust
// NEVER: Direct use of user input
#[command]
fn read_file(path: String) -> String { std::fs::read_to_string(path).unwrap() }

// ALWAYS: Validate and scope
#[command]
fn read_file(request: ValidatedFileRequest) -> Result<String, String> { /* ... */ }
```

---

## 13. Pre-Deployment Checklist

### Security Checklist

- [ ] Tauri 2.0+ with latest patches
- [ ] Rust 1.77.2+ (CVE-2024-24576 fix)
- [ ] CSP configured restrictively
- [ ] `freezePrototype: true` enabled
- [ ] Capabilities use minimal permissions
- [ ] Filesystem scopes are explicit paths
- [ ] Shell execution disabled or allowlisted
- [ ] No TAURI_ in frontend envPrefix
- [ ] Auto-updater uses signature verification
- [ ] All IPC commands validate input
- [ ] Origin verification for sensitive ops
- [ ] `cargo audit` passes

### Runtime Checklist

- [ ] Debug mode disabled in production
- [ ] DevTools disabled in production
- [ ] Remote debugging disabled
- [ ] Update checks working

---

## 14. Summary

Your goal is to create Tauri applications that are:
- **Secure by Default**: Minimal capabilities, restrictive CSP
- **Defense in Depth**: Multiple security layers
- **Validated**: All IPC inputs validated
- **Transparent**: Signed updates, clear permissions

**Security Reminder**:
1. Never enable shell execution without strict allowlist
2. Always scope filesystem access to specific directories
3. Configure CSP to block XSS and data exfiltration
4. Verify origins for sensitive operations
5. Sign updates and verify signatures
6. Keep Tauri and Rust updated for security patches

> **For attack scenarios and threat modeling, see `references/threat-model.md`**


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### references/advanced-patterns.md

```markdown
# Tauri Advanced Patterns Reference

## Plugin Development

### Creating a Secure Plugin

```rust
use tauri::{
    plugin::{Builder, TauriPlugin},
    Manager, Runtime, State,
};

pub struct PluginState {
    // Plugin-specific state
}

#[tauri::command]
async fn plugin_command(
    state: State<'_, PluginState>,
) -> Result<String, String> {
    // Plugin logic
    Ok("result".into())
}

pub fn init<R: Runtime>() -> TauriPlugin<R> {
    Builder::new("my-secure-plugin")
        .invoke_handler(tauri::generate_handler![plugin_command])
        .setup(|app, _api| {
            app.manage(PluginState {});
            Ok(())
        })
        .build()
}
```

### Plugin with Permissions

```json
// plugins/my-plugin/permissions/default.json
{
  "identifier": "my-plugin:default",
  "description": "Default permissions for my-plugin",
  "permissions": [
    "my-plugin:allow-safe-command"
  ]
}
```

---

## Multi-Window Management

### Secure Window Communication

```rust
use tauri::{AppHandle, Manager};

// Emit to specific window
pub fn send_to_window(
    app: &AppHandle,
    window_label: &str,
    event: &str,
    payload: impl serde::Serialize,
) -> Result<(), Error> {
    let window = app.get_webview_window(window_label)
        .ok_or(Error::WindowNotFound)?;

    window.emit(event, payload)?;
    Ok(())
}

// Broadcast to all windows
pub fn broadcast(
    app: &AppHandle,
    event: &str,
    payload: impl serde::Serialize + Clone,
) -> Result<(), Error> {
    app.emit(event, payload)?;
    Ok(())
}
```

### Window-Specific Capabilities

```json
// capabilities/admin.json
{
  "identifier": "admin",
  "windows": ["admin-window"],
  "permissions": [
    "fs:default",
    "shell:allow-execute"
  ]
}

// capabilities/user.json
{
  "identifier": "user",
  "windows": ["main"],
  "permissions": [
    "core:default"
  ]
}
```

---

## State Management Patterns

### Thread-Safe Global State

```rust
use std::sync::Arc;
use tokio::sync::RwLock;

pub struct AppState {
    pub config: Arc<RwLock<Config>>,
    pub db: Arc<DatabasePool>,
    pub cache: Arc<RwLock<Cache>>,
}

#[command]
async fn get_config(state: State<'_, AppState>) -> Result<Config, String> {
    let config = state.config.read().await;
    Ok(config.clone())
}

#[command]
async fn update_config(
    new_config: Config,
    state: State<'_, AppState>,
) -> Result<(), String> {
    new_config.validate()?;

    let mut config = state.config.write().await;
    *config = new_config;

    Ok(())
}
```

### Event-Driven State Updates

```rust
use tauri::Manager;

#[command]
async fn update_state(
    app: AppHandle,
    state: State<'_, AppState>,
    update: StateUpdate,
) -> Result<(), String> {
    // Update state
    {
        let mut data = state.data.write().await;
        data.apply(update.clone());
    }

    // Notify all windows
    app.emit("state-changed", update)?;

    Ok(())
}
```

---

## IPC Optimization

### Streaming Large Data

```rust
use tauri::ipc::Channel;

#[command]
async fn stream_data(channel: Channel<DataChunk>) -> Result<(), String> {
    let data = load_large_data().await?;

    for chunk in data.chunks(1024) {
        channel.send(DataChunk {
            data: chunk.to_vec(),
            progress: calculate_progress(),
        }).map_err(|e| e.to_string())?;
    }

    Ok(())
}
```

### Batching IPC Calls

```typescript
// Frontend: Batch multiple operations
const results = await invoke('batch_operations', {
    operations: [
        { type: 'read', path: 'file1.txt' },
        { type: 'read', path: 'file2.txt' },
        { type: 'write', path: 'file3.txt', content: '...' }
    ]
});
```

```rust
// Backend: Process batch
#[command]
async fn batch_operations(
    operations: Vec<Operation>,
) -> Result<Vec<OperationResult>, String> {
    let mut results = Vec::with_capacity(operations.len());

    for op in operations {
        let result = process_operation(op).await;
        results.push(result);
    }

    Ok(results)
}
```

---

## Testing Patterns

### Integration Testing

```rust
#[cfg(test)]
mod tests {
    use tauri::test::{mock_builder, MockRuntime};

    fn create_app() -> tauri::App<MockRuntime> {
        mock_builder()
            .invoke_handler(tauri::generate_handler![
                read_file,
                write_file,
            ])
            .build(tauri::generate_context!())
            .unwrap()
    }

    #[test]
    fn test_read_file() {
        let app = create_app();
        // Test IPC commands
    }
}
```

### E2E Testing with WebDriver

```typescript
// tests/e2e/app.spec.ts
import { test, expect } from '@playwright/test';

test('secure operation requires valid input', async ({ page }) => {
    await page.goto('tauri://localhost');

    // Try invalid input
    await page.fill('#input', '../../../etc/passwd');
    await page.click('#submit');

    // Should show error
    await expect(page.locator('.error')).toContainText('Invalid path');
});
```

---

## Performance Optimization

### Async Command Execution

```rust
use tokio::task;

#[command]
async fn cpu_intensive(data: Vec<u8>) -> Result<Vec<u8>, String> {
    // Offload CPU work to blocking thread pool
    task::spawn_blocking(move || {
        process_data(&data)
    })
    .await
    .map_err(|e| e.to_string())?
}
```

### Resource Caching

```rust
use cached::proc_macro::cached;

#[cached(time = 300)]  // Cache for 5 minutes
async fn get_expensive_data(key: String) -> Result<Data, Error> {
    // Only called if not in cache
    fetch_from_database(&key).await
}

#[command]
async fn get_data(key: String) -> Result<Data, String> {
    get_expensive_data(key)
        .await
        .map_err(|e| e.to_string())
}
```

---

## Deployment Patterns

### Auto-Update Configuration

```json
// tauri.conf.json
{
  "plugins": {
    "updater": {
      "active": true,
      "endpoints": [
        "https://releases.example.com/{{target}}/{{arch}}/{{current_version}}"
      ],
      "dialog": true,
      "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6..."
    }
  }
}
```

### Code Signing

```yaml
# .github/workflows/release.yml
- name: Build and Sign
  uses: tauri-apps/tauri-action@v0
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
    TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PASSWORD }}
    # Windows
    TAURI_SIGNING_IDENTITY: ${{ secrets.WINDOWS_CERTIFICATE }}
    # macOS
    APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
    APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
```

### Environment-Specific Builds

```rust
// src-tauri/src/main.rs
fn main() {
    let builder = tauri::Builder::default();

    #[cfg(debug_assertions)]
    let builder = builder
        .plugin(tauri_plugin_devtools::init());

    #[cfg(not(debug_assertions))]
    let builder = builder
        .plugin(tauri_plugin_updater::Builder::new().build());

    builder
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
```

```

### references/security-examples.md

```markdown
# Tauri Security Examples Reference

## CVE Details and Mitigations

### CVE-2024-35222: iFrame Origin Bypass

**Severity**: HIGH (CVSS 7.5)
**Affected**: Tauri < 1.6.7, < 2.0.0-beta.20
**CWE**: CWE-346 (Origin Validation Error)

**Description**: When using `dangerousRemoteDomainIpcAccess`, iFrames from the allowed domain could bypass origin checks and invoke Tauri API commands even though they should be restricted to the parent window only.

**Vulnerable Configuration**:
```json
{
  "security": {
    "dangerousRemoteDomainIpcAccess": [
      {
        "domain": "trusted.com",
        "windows": ["main"],
        "enableTauriAPI": true
      }
    ]
  }
}
// Problem: iFrame from trusted.com can invoke Tauri commands
```

**Mitigation**:
1. Upgrade to Tauri 1.6.7+ or 2.0.0-beta.20+
2. Avoid `dangerousRemoteDomainIpcAccess` if possible
3. Implement additional origin checks in commands

```rust
#[command]
async fn sensitive_op(window: Window) -> Result<(), String> {
    // Additional check: reject iFrame origins
    let url = window.url();
    if url.origin() != expected_origin {
        return Err("Invalid origin".into());
    }
    Ok(())
}
```

---

### CVE-2023-46115: Updater Key Leakage via Vite

**Severity**: MEDIUM (CVSS 5.5)
**Affected**: Applications using Vite with misconfigured envPrefix
**CWE**: CWE-200 (Information Exposure)

**Description**: The Tauri documentation example showed `envPrefix: ['VITE_', 'TAURI_']` which causes `TAURI_PRIVATE_KEY` and `TAURI_KEY_PASSWORD` to be bundled into the frontend code.

**Vulnerable Configuration**:
```typescript
// vite.config.ts - VULNERABLE
import { defineConfig } from 'vite';

export default defineConfig({
  envPrefix: ['VITE_', 'TAURI_']  // Leaks TAURI_PRIVATE_KEY!
});
```

**Mitigation**:
```typescript
// vite.config.ts - SECURE
import { defineConfig } from 'vite';

export default defineConfig({
  envPrefix: ['VITE_']  // Only expose VITE_ variables
});
```

**Detection**:
```bash
# Check if keys are in bundle
grep -r "TAURI_PRIVATE_KEY\|TAURI_KEY_PASSWORD" dist/
```

---

### CVE-2023-34460: Filesystem Scope Bypass for Dotfiles

**Severity**: MEDIUM (CVSS 4.7)
**Affected**: Tauri 1.4.0
**CWE**: CWE-22 (Path Traversal)

**Description**: Regression in Tauri 1.4.0 allowed access to dotfiles when using wildcard scopes like `$HOME/*`. Previously dotfiles were not implicitly allowed.

**Vulnerable Scope**:
```json
{
  "fs": {
    "scope": ["$HOME/*"]  // In 1.4.0, this allowed access to ~/.ssh/*
  }
}
```

**Mitigation**:
1. Upgrade to Tauri 1.4.1+
2. Use explicit path allowlists instead of wildcards

```json
// BETTER: Explicit paths
{
  "permissions": [
    {
      "identifier": "fs:read-files",
      "allow": [
        "$HOME/Documents/*",
        "$HOME/Downloads/*"
      ],
      "deny": [
        "$HOME/.*"  // Explicitly deny dotfiles
      ]
    }
  ]
}
```

---

### CVE-2022-46171: Permissive Glob Patterns

**Severity**: HIGH
**Affected**: Tauri < 1.2.3
**CWE**: CWE-22 (Path Traversal)

**Description**: Filesystem scope glob patterns were too permissive, allowing path traversal via symlinks and normalized paths.

**Mitigation**: Always validate and canonicalize paths in backend

```rust
fn validate_in_scope(base_dir: &Path, user_path: &str) -> Result<PathBuf, Error> {
    // Join and canonicalize
    let full = base_dir.join(user_path);
    let canonical = dunce::canonicalize(&full)?;
    let base_canonical = dunce::canonicalize(base_dir)?;

    // Verify containment after resolving symlinks
    if !canonical.starts_with(&base_canonical) {
        return Err(Error::OutOfScope);
    }

    Ok(canonical)
}
```

---

## OWASP Top 10 2025 Complete Examples

### A01: Broken Access Control

```rust
// VULNERABLE: No permission check
#[command]
async fn delete_file(path: String) -> Result<(), String> {
    std::fs::remove_file(path).map_err(|e| e.to_string())
}

// SECURE: Validate scope and permissions
#[command]
async fn delete_file(
    window: Window,
    app: AppHandle,
    request: DeleteRequest,
) -> Result<(), String> {
    // Validate input
    request.validate().map_err(|e| e.to_string())?;

    // Verify origin
    verify_origin(&window)?;

    // Scope to allowed directory
    let app_data = app.path().app_data_dir().map_err(|e| e.to_string())?;
    let safe_path = validate_path(&app_data, &request.path)?;

    // Log action
    tracing::info!(
        action = "delete_file",
        path = %safe_path.display(),
        "File deletion requested"
    );

    std::fs::remove_file(safe_path).map_err(|e| e.to_string())
}
```

### A02: Cryptographic Failures

```rust
// SECURE: Verify update signatures
let updater = app.updater_builder()
    .endpoints(vec!["https://releases.example.com/...".into()])
    .pubkey(include_str!("../pubkey.txt"))  // Embedded public key
    .build()?;

// Verify TLS for all network requests
let client = reqwest::Client::builder()
    .min_tls_version(reqwest::tls::Version::TLS_1_2)
    .danger_accept_invalid_certs(false)  // NEVER set to true
    .build()?;
```

### A03: Injection

```json
// Secure CSP prevents XSS leading to IPC abuse
{
  "security": {
    "csp": {
      "default-src": "'self'",
      "script-src": "'self'",
      "style-src": "'self' 'unsafe-inline'",
      "connect-src": "'self' https://api.example.com",
      "frame-src": "'none'",
      "object-src": "'none'"
    }
  }
}
```

### A05: Security Misconfiguration

```json
// tauri.conf.json - Secure configuration
{
  "app": {
    "security": {
      "csp": "default-src 'self'",
      "freezePrototype": true,
      "dangerousDisableAssetCspModification": false
    }
  },
  "build": {
    "devtools": false  // Disable in production
  }
}
```

```json
// capabilities/default.json - Minimal permissions
{
  "identifier": "default",
  "permissions": [
    "core:event:default",
    "core:window:default"
    // Only add what's needed
  ]
}
```

### A08: Software and Data Integrity Failures

```rust
// Sign releases during CI/CD
// .github/workflows/release.yml
// - uses: tauri-apps/tauri-action@v0
//   with:
//     args: --bundles updater

// Verify integrity in application
async fn verify_download(data: &[u8], expected_hash: &str) -> Result<(), Error> {
    use sha2::{Sha256, Digest};

    let mut hasher = Sha256::new();
    hasher.update(data);
    let hash = hex::encode(hasher.finalize());

    if hash != expected_hash {
        return Err(Error::IntegrityCheck);
    }

    Ok(())
}
```

---

## Additional Security Patterns

### Rate Limiting IPC Commands

```rust
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};

struct RateLimiter {
    last_reset: std::sync::Mutex<Instant>,
    count: AtomicU64,
    max_per_minute: u64,
}

impl RateLimiter {
    fn check(&self) -> Result<(), Error> {
        let mut last_reset = self.last_reset.lock().unwrap();

        if last_reset.elapsed() > Duration::from_secs(60) {
            *last_reset = Instant::now();
            self.count.store(0, Ordering::SeqCst);
        }

        let count = self.count.fetch_add(1, Ordering::SeqCst);
        if count >= self.max_per_minute {
            return Err(Error::RateLimited);
        }

        Ok(())
    }
}

#[command]
async fn rate_limited_command(
    state: State<'_, AppState>,
) -> Result<String, String> {
    state.rate_limiter.check().map_err(|e| e.to_string())?;
    // ... rest of command
    Ok("success".into())
}
```

### Secure Window Creation

```rust
use tauri::{WebviewUrl, WebviewWindowBuilder};

fn create_secure_window(app: &AppHandle) -> Result<(), Error> {
    WebviewWindowBuilder::new(
        app,
        "secure",
        WebviewUrl::App("index.html".into())
    )
    .title("Secure Window")
    .inner_size(800.0, 600.0)
    .resizable(true)
    // Security settings
    .disable_drag_drop_handler()  // Prevent file drops
    .build()?;

    Ok(())
}
```

### Audit Logging

```rust
use tracing::{info, warn};

#[command]
async fn audited_command(
    window: Window,
    app: AppHandle,
    request: AuditedRequest,
) -> Result<Response, String> {
    let start = std::time::Instant::now();

    // Log request
    info!(
        command = "audited_command",
        window = %window.label(),
        origin = %window.url(),
        request_id = %uuid::Uuid::new_v4(),
        "Command invoked"
    );

    let result = process_request(request).await;

    // Log result
    match &result {
        Ok(_) => info!(
            command = "audited_command",
            duration_ms = %start.elapsed().as_millis(),
            status = "success",
            "Command completed"
        ),
        Err(e) => warn!(
            command = "audited_command",
            duration_ms = %start.elapsed().as_millis(),
            status = "error",
            error = %e,
            "Command failed"
        ),
    }

    result
}
```

```

### references/threat-model.md

```markdown
# Tauri Threat Model

## Threat Model Overview

**Domain Risk Level**: HIGH

### Assets to Protect
1. **User Data** - Application data, credentials, personal files - **Sensitivity**: CRITICAL
2. **System Access** - Filesystem, shell, network - **Sensitivity**: CRITICAL
3. **Update Mechanism** - Application integrity - **Sensitivity**: HIGH
4. **IPC Channel** - Communication between frontend and backend - **Sensitivity**: HIGH

### Threat Actors
1. **Malicious Websites** - XSS, CSRF via embedded content
2. **Supply Chain Attackers** - Compromised dependencies, malicious updates
3. **Local Attackers** - Users with system access trying to escalate privileges
4. **Network Attackers** - MitM on updates, API hijacking

### Attack Surface
- WebView content (XSS, injection)
- IPC commands from frontend
- Filesystem access patterns
- Auto-update mechanism
- Plugin system
- Window management

---

## Attack Scenario 1: IPC Command Injection via XSS

**Threat Category**: OWASP A03:2025 - Injection / CWE-94

**Threat Level**: CRITICAL

**Attack Description**: Attacker injects malicious JavaScript into the WebView, which then calls Tauri IPC commands to access system resources.

**Attack Flow**:
```
1. Application loads external content or user-provided HTML
2. Attacker injects: <script>window.__TAURI__.invoke('read_file', {path: '/etc/passwd'})</script>
3. Malicious script executes in WebView context
4. IPC command is invoked with attacker-controlled parameters
5. Backend executes command without origin verification
6. Attacker exfiltrates sensitive system files
```

**Impact**:
- **Confidentiality**: CRITICAL - Full filesystem access
- **Integrity**: HIGH - Can modify files
- **Availability**: MEDIUM - Can delete files
- **Business Impact**: Complete system compromise

**Likelihood**: HIGH - XSS is common in web applications

**Mitigation**:

```rust
// Primary: Origin verification for all sensitive commands
#[command]
async fn read_file(window: Window, request: FileRequest) -> Result<String, String> {
    // Verify origin
    let url = window.url();
    if !url.as_str().starts_with("tauri://") && !url.as_str().starts_with("https://tauri.localhost") {
        return Err("Invalid origin".into());
    }

    // Validate and scope path
    request.validate()?;
    // ... rest of implementation
}
```

```json
// Secondary: Restrictive CSP
{
  "security": {
    "csp": "default-src 'self'; script-src 'self'; frame-src 'none'"
  }
}
```

**Detection**:
```rust
// Log all IPC calls
tracing::info!(
    command = "read_file",
    origin = %window.url(),
    path = %request.path,
    "IPC command invoked"
);
```

---

## Attack Scenario 2: Filesystem Scope Escape via Path Traversal

**Threat Category**: OWASP A01:2025 - Broken Access Control / CWE-22

**Threat Level**: CRITICAL

**Attack Description**: Attacker bypasses filesystem scope restrictions using path traversal sequences to access files outside allowed directories.

**Attack Flow**:
```
1. Application allows read access to $APPDATA/myapp/*
2. Attacker requests path: "../../.ssh/id_rsa"
3. Path joins to: $APPDATA/myapp/../../.ssh/id_rsa
4. Resolves to: ~/.ssh/id_rsa (outside scope!)
5. Application reads and returns private SSH key
6. Attacker steals credentials
```

**Impact**:
- **Confidentiality**: CRITICAL - Access to any user file
- **Integrity**: HIGH - Can write to any user file
- **Business Impact**: Credential theft, data exfiltration

**Likelihood**: HIGH - Simple attack, CVE-2023-34460 and CVE-2022-46171 show history

**Mitigation**:

```rust
use std::path::PathBuf;

fn validate_path(base: &PathBuf, user_path: &str) -> Result<PathBuf, Error> {
    // Reject obvious traversal attempts
    if user_path.contains("..") {
        return Err(Error::PathTraversal);
    }

    let full_path = base.join(user_path);

    // Canonicalize to resolve symlinks and normalize
    let canonical = dunce::canonicalize(&full_path)
        .map_err(|_| Error::NotFound)?;

    let base_canonical = dunce::canonicalize(base)
        .map_err(|_| Error::Configuration)?;

    // Verify containment
    if !canonical.starts_with(&base_canonical) {
        return Err(Error::PathTraversal);
    }

    Ok(canonical)
}
```

**Testing**:
```rust
#[test]
fn test_path_traversal_variants() {
    let base = PathBuf::from("/app/data");
    let attacks = vec![
        "../etc/passwd",
        "..\\etc\\passwd",
        "foo/../../../etc/passwd",
        "foo/bar/../../../etc/passwd",
        "./../../etc/passwd",
        "%2e%2e/etc/passwd",
    ];

    for attack in attacks {
        assert!(validate_path(&base, attack).is_err(),
            "Path traversal not blocked: {}", attack);
    }
}
```

---

## Attack Scenario 3: Update Mechanism Compromise

**Threat Category**: OWASP A08:2025 - Software and Data Integrity Failures / CWE-494

**Threat Level**: CRITICAL

**Attack Description**: Attacker compromises update server or performs MitM to deliver malicious application update.

**Attack Flow**:
```
1. Attacker compromises update server or DNS
2. Application checks for updates
3. Malicious update served instead of legitimate one
4. If signatures not verified, malicious update installs
5. Attacker gains persistent code execution
6. Complete system compromise
```

**Impact**:
- **Confidentiality**: CRITICAL - Full system access
- **Integrity**: CRITICAL - Persistent malware
- **Availability**: CRITICAL - Ransomware potential
- **Business Impact**: Complete compromise of all users

**Likelihood**: MEDIUM - Requires server compromise, but CVE-2023-46115 shows key leakage risk

**Mitigation**:

```rust
// Mandatory signature verification
let updater = app.updater_builder()
    .endpoints(vec![
        "https://releases.example.com/{{target}}/{{arch}}/{{current_version}}".into()
    ])
    // Public key for signature verification
    .pubkey("dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXkK...")
    .build()?;

// Never disable signature verification
// updater.dangerous_skip_signature_validation() // NEVER DO THIS
```

```typescript
// Protect private keys - vite.config.ts
export default {
    envPrefix: ['VITE_']  // NOT ['VITE_', 'TAURI_']
}
```

**Key Management**:
- Store private key in secure vault (not in repo)
- Rotate keys annually
- Use HSM for production signing
- Monitor for key leakage

---

## Attack Scenario 4: iFrame Origin Bypass (CVE-2024-35222)

**Threat Category**: OWASP A01:2025 - Broken Access Control / CWE-346

**Threat Level**: HIGH

**Attack Description**: Malicious iFrame from remote origin bypasses Tauri API access controls to invoke commands.

**Attack Flow**:
```
1. Application embeds content with dangerousRemoteDomainIpcAccess
2. Attacker controls or compromises allowed remote domain
3. Attacker adds iFrame to their content
4. iFrame inherits IPC access from parent
5. iFrame origin not properly checked
6. Attacker invokes Tauri commands from iFrame
```

**Impact**:
- **Confidentiality**: HIGH - Access to IPC commands
- **Integrity**: HIGH - Can invoke state-changing commands
- **Business Impact**: Unauthorized access to system resources

**Likelihood**: MEDIUM - Requires specific configuration

**Mitigation**:

```json
// Avoid dangerousRemoteDomainIpcAccess if possible
{
  "security": {
    // Don't use this unless absolutely necessary
    // "dangerousRemoteDomainIpcAccess": []
  }
}

// If required, upgrade to patched version
// Tauri 1.6.7+ or 2.0.0-beta.20+
```

```rust
// Additional origin check in commands
#[command]
async fn sensitive_command(window: Window) -> Result<(), String> {
    let url = window.url();

    // Reject if from iframe (check for expected top-level URL)
    if !is_top_level_window(&window) {
        return Err("Command not allowed from iframe".into());
    }

    Ok(())
}
```

---

## Attack Scenario 5: Shell Command Injection

**Threat Category**: OWASP A03:2025 - Injection / CWE-78

**Threat Level**: CRITICAL

**Attack Description**: Attacker exploits shell execution capability to run arbitrary system commands.

**Attack Flow**:
```
1. Application has shell:allow-execute capability
2. User input passed to shell command
3. Attacker provides: filename; rm -rf /
4. Command constructed: open "filename; rm -rf /"
5. Shell interprets ; as command separator
6. Malicious command executes with app privileges
```

**Impact**:
- **Confidentiality**: CRITICAL - Can read any file
- **Integrity**: CRITICAL - Can modify/delete any file
- **Availability**: CRITICAL - Can destroy system
- **Business Impact**: Complete system compromise

**Likelihood**: HIGH if shell execution enabled

**Mitigation**:

```json
// Best: Don't enable shell execution
{
  "permissions": [
    // "shell:allow-execute"  // DISABLED
  ]
}

// If required: Strict allowlist with fixed arguments
{
  "permissions": [
    {
      "identifier": "shell:allow-execute",
      "allow": [
        {
          "name": "open-browser",
          "cmd": "open",
          "args": [
            { "validator": "^https://example\\.com" }
          ]
        }
      ]
    }
  ]
}
```

```rust
// Rust backend: Never use shell
use std::process::Command;

// NEVER
Command::new("sh").arg("-c").arg(format!("open {}", user_input));

// ALWAYS - direct execution with validation
fn open_url(url: &str) -> Result<(), Error> {
    // Validate URL format
    let parsed = url::Url::parse(url)
        .map_err(|_| Error::InvalidUrl)?;

    // Allowlist schemes
    if parsed.scheme() != "https" {
        return Err(Error::InvalidScheme);
    }

    // Allowlist domains
    if parsed.host_str() != Some("example.com") {
        return Err(Error::InvalidDomain);
    }

    Command::new("open")
        .arg(url)
        .spawn()
        .map_err(|e| Error::Command(e))?;

    Ok(())
}
```

---

## STRIDE Analysis

| Category | Threats | Mitigations | Priority |
|----------|---------|-------------|----------|
| **Spoofing** | Fake origin in IPC, compromised update server | Origin verification, update signatures | CRITICAL |
| **Tampering** | Modified filesystem paths, injected commands | Path validation, input sanitization | CRITICAL |
| **Repudiation** | No audit trail for sensitive operations | Log all IPC calls with context | MEDIUM |
| **Information Disclosure** | Path traversal, verbose errors | Path containment, safe error messages | HIGH |
| **Denial of Service** | Resource exhaustion via IPC | Rate limiting, input size limits | MEDIUM |
| **Elevation of Privilege** | Shell injection, capability abuse | Disable shell, minimal capabilities | CRITICAL |

---

## Security Testing Coverage

### Automated Testing
- [ ] Path traversal fuzzing on all file operations
- [ ] IPC command fuzzing with invalid inputs
- [ ] CSP validation in browser devtools
- [ ] Dependency scanning with cargo-audit

### Manual Testing
- [ ] Origin verification bypass attempts
- [ ] Update mechanism MitM testing
- [ ] Capability escalation testing
- [ ] Shell injection in allowed commands

### Penetration Testing Scope
- [ ] WebView XSS leading to IPC abuse
- [ ] Filesystem scope escape
- [ ] Update integrity verification
- [ ] Plugin security boundaries

```