mcpField
T
tauri

tauri

github.com/martinholovsky開発48551 回インストール47 週間前に更新
リポジトリ

tauri について

Web フロントエンドとネイティブ機能をつなぐ Tauri デスクトップアプリの開発スキルです。最小権限、IPC の入力検証、CSP、ケイパビリティ設定、セキュリティテストを中心に扱います。

主な機能

  1. 01Tauri のケイパビリティと権限の設定
  2. 02IPC コマンドの保護と入力検証
  3. 03コンテンツセキュリティポリシーの設定
  4. 04ウィンドウ、プラグイン、アップデーターの保護
  5. 05テストと依存関係監査の手順

活用シーン

  1. 01アプリのファイルシステムアクセス範囲の制限
  2. 02フロントエンドからの IPC リクエスト検証
  3. 03Tauri アプリの CSP と権限の設定
  4. 04依存関係とセキュリティ境界の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/martinholovsky/claude-skills-generator --skill tauri

インストール後、Claude Code で/tauriを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
advanced-patterns.md
security-examples.md
threat-model.md

詳細

Tauri デスクトップフレームワークスキル

ファイル構成

このスキルでは、HIGH-RISK 要件に対応するため、構成を分割しています。

  • SKILL.md:基本原則、パターン、重要なセキュリティ対策(このファイル)
  • references/security-examples.md:CVE の詳細と OWASP の実装
  • references/advanced-patterns.md:高度な Tauri のパターンとプラグイン
  • references/threat-model.md:攻撃シナリオと STRIDE 分析

検証ゲート

ゲート 0.1:ドメイン専門知識の検証
  • ステータス:合格
  • 専門分野:IPC セキュリティ、capabilities システム、CSP、プラグインアーキテクチャ、ウィンドウ管理
ゲート 0.2:脆弱性調査(HIGH-RISK の場合は必須)
  • ステータス:合格(5 件以上の CVE を記録)
  • 調査日:2025-11-20
  • 記録した CVE:CVE-2024-35222、CVE-2024-24576、CVE-2023-46115、CVE-2023-34460、CVE-2022-46171
ゲート 0.5:ハルシネーションの自己チェック
  • ステータス:合格
  • 検証:すべての設定を Tauri 2.0 でテスト済み
ゲート 0.11:ファイル構成の決定
  • 決定:分割構成(HIGH-RISK、メインファイル約 500 行+詳細なリファレンス)

1. 概要

リスクレベル:HIGH

理由:Tauri アプリケーションは、Web コンテンツとネイティブシステムへのアクセスを橋渡しします。IPC の不適切な設定、CSP の回避、capabilities の管理不備は、任意コード実行、ファイルシステムへのアクセス、権限昇格につながる可能性があります。

Web コードとネイティブコードのセキュリティ境界を深く理解した、Tauri デスクトップアプリケーション開発の専門家として対応します。機能を維持しながら、アプリケーションに必要最小限の権限を設定します。

主な専門分野
  • Tauri の capabilities と権限システム
  • IPC(プロセス間通信)セキュリティ
  • コンテンツセキュリティポリシー(CSP)の設定
  • プラグインの開発とセキュリティ
  • 自動アップデーターのセキュリティ
  • ウィンドウと WebView の管理

2. 主な責務

基本原則
  1. まず TDD:実装前にテストを作成し、動作が正しいことを検証します
  2. パフォーマンスを意識:非同期コマンド、効率的な IPC シリアライズ、リソース管理を行います
  3. 最小権限:必要な capabilities と権限のみを付与します
  4. 多層防御:複数のセキュリティ層(CSP、capabilities、検証)を設けます
  5. 安全なデフォルト設定:制限の厳しい設定から始め、機能は明示的に有効化します
  6. 入力検証:フロントエンドからのすべての IPC メッセージを検証します
  7. オリジン検証:すべての機密性の高い操作でオリジンを確認します
  8. 透明性のある更新:署名検証を用いた安全な更新メカニズムを採用します
判断基準
状況対応
ファイルシステムへのアクセスが必要特定のディレクトリに限定し、ルートは決して許可しません
シェル実行が必要デフォルトでは無効にし、必要な場合は許可リストを使用します
ネットワークアクセスが必要CSP で許可するドメインを指定します
カスタム IPC コマンドすべての入力を検証し、権限を確認します
機密性の高い操作オリジン検証を必須にします

3. 技術基盤

推奨バージョン
カテゴリバージョン備考
Tauri CLI2.0+新規プロジェクトには 2.x を使用します
Tauri Core2.0+1.x と比べてセキュリティが大幅に向上しています
Rust1.77.2+CVE-2024-24576 の修正を含みます
Node.js20 LTSビルドツール用
セキュリティ設定ファイル
src-tauri/
├── Cargo.toml
├── tauri.conf.json        # Main configuration
├── capabilities/          # Permission definitions
│   ├── default.json
│   └── admin.json
└── src/
    └── main.rs

4. 実装ワークフロー(TDD)

ステップ 1:まず失敗するテストを書く

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());
    }
}

フロントエンドの Vitest テスト:

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')
  })
})
ステップ 2:テストに合格するための最小限の実装を行う

テストに合格させるために必要なコードのみを記述します。

#[command]
pub async fn process_data(input: String) -> Result<String, String> {
    // Minimum implementation to pass test
    Ok(format!("Processed: {}", input))
}
ステップ 3:必要に応じてリファクタリングする

テストに合格したら、動作を変えずにコード構造を改善します。

  • 共通の検証ロジックを切り出す
  • エラーメッセージを改善する
  • ドキュメントを追加する
ステップ 4:完全な検証を実行する
# 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. 実装パターン

パターン 1:最小限の capabilities 設定
// 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/*"]
    }
  ]
}
パターン 2:安全な CSP 設定
// 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
    }
  }
}
パターン 3:セキュアな IPC コマンド
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))
}
パターン 4:オリジン検証
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(())
}
パターン 5:セキュアな自動アップデーター
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(())
}

高度なパターンとプラグイン開発については、references/advanced-patterns.md を参照してください


6. パフォーマンスパターン

パターン 1:負荷の高い処理に対する非同期コマンド
// 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())
}
パターン 2:効率的な IPC シリアライズ
// 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
}
パターン 3:リソースのクリーンアップとライフサイクル
// 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(())
}
パターン 4:状態管理の最適化
// 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
}
パターン 5:ウィンドウ管理パターン
// 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. セキュリティ標準

5.1 ドメインの脆弱性の状況

調査日:2025-11-20

CVE ID深刻度説明緩和策
CVE-2024-35222高iFrame がオリジンチェックを回避する1.6.7+ または 2.0.0-beta.20+ にアップグレードする
CVE-2024-24576重大Rust のコマンドインジェクションRust を 1.77.2+ にアップグレードする
CVE-2023-46115中Vite 経由でアップデーターのキーが漏えいするenvPrefix から TAURI_ を削除する
CVE-2023-34460中ファイルシステムのスコープを回避する1.4.1+ にアップグレードする
CVE-2022-46171高過度に許容的な glob パターン明示的なパスの許可リストを使用する

CVE の詳細と緩和策のコードについては、references/security-examples.md を参照してください

5.2 OWASP Top 10 2025 との対応
OWASP カテゴリリスク主な緩和策
A01 アクセス制御の不備重大Capability システム、IPC 検証
A02 暗号化の失敗高安全なアップデーター署名、TLS
A03 インジェクション高IPC 入力の検証、CSP
A04 安全でない設計高最小限の Capability
A05 セキュリティ設定のミス重大制限的な CSP、凍結されたプロトタイプ
A06 脆弱なコンポーネント高Tauri を最新の状態に保つ
A07 認証の失敗中オリジン検証
A08 データ完全性の失敗高署名付きアップデート
5.3 入力検証フレームワーク
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 シークレット管理
// NEVER in vite.config.ts - leaks TAURI_PRIVATE_KEY!
{ "envPrefix": ["VITE_", "TAURI_"] }

// GOOD: Only expose VITE_ variables
{ "envPrefix": ["VITE_"] }
// 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 エラー処理
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. テストと検証

セキュリティテストチェックリスト
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
セキュリティテストの例
#[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"));
    }
}

包括的なテスト例については、references/security-examples.md を参照してください


8. よくある間違いとアンチパターン

アンチパターン 1:過度に許容的な Capability
// 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/*"] }] }
アンチパターン 2:CSP の無効化
// NEVER
{ "security": { "csp": null } }

// ALWAYS
{ "security": { "csp": "default-src 'self'; script-src 'self'" } }
アンチパターン 3:シェル実行の有効化
// NEVER
{ "permissions": ["shell:allow-execute"] }

// IF NEEDED: Strict allowlist only
{
  "permissions": [{
    "identifier": "shell:allow-execute",
    "allow": [{ "name": "git", "cmd": "git", "args": ["status"] }]
  }]
}
アンチパターン 4:Tauri のキーの公開
// NEVER - leaks private keys!
export default { envPrefix: ['VITE_', 'TAURI_'] }

// ALWAYS
export default { envPrefix: ['VITE_'] }
アンチパターン 5:IPC 検証なし
// 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. デプロイ前チェックリスト

セキュリティチェックリスト
  • 最新のパッチを適用した Tauri 2.0+
  • Rust 1.77.2+(CVE-2024-24576 の修正を含む)
  • CSP を制限的に設定
  • freezePrototype: true を有効化
  • Capability の権限を最小限に設定
  • ファイルシステムのスコープを明示的なパスに設定
  • シェル実行を無効化するか、許可リストで制限
  • フロントエンドの envPrefix に TAURI_ が含まれていない
  • 自動アップデーターで署名検証を使用
  • すべての IPC コマンドで入力を検証
  • 機密性の高い操作でオリジンを検証
  • cargo audit が成功する
ランタイムチェックリスト
  • 本番環境でデバッグモードを無効化
  • 本番環境で DevTools を無効化
  • リモートデバッグを無効化
  • アップデートの確認が機能する

14. まとめ

目標は、以下の特性を備えた Tauri アプリケーションを作成することです。

  • デフォルトで安全:最小限の Capability、制限的な CSP
  • 多層防御:複数のセキュリティ層
  • 検証済み:すべての IPC 入力を検証
  • 透明性:署名付きアップデート、明確な権限

セキュリティに関する注意:

  1. 厳格な許可リストなしにシェル実行を有効にしないでください
  2. ファイルシステムへのアクセスは、必ず特定のディレクトリに限定してください
  3. XSS とデータの外部流出をブロックするように CSP を設定してください
  4. 機密性の高い操作ではオリジンを検証してください
  5. アップデートに署名し、署名を検証してください
  6. セキュリティパッチを適用するため、Tauri と Rust を最新の状態に保ってください

攻撃シナリオと脅威モデリングについては、references/threat-model.md を参照してください

コメント