Quick Start

Goal

We will fuzz a very minimal Tauri application. The repository features a minimal example called mini-app. This example app will be used to showcase how to setup fuzzing with tauri-fuzz.

Fuzzing a Tauri application

We are using mini-app that implements a simple Tauri command, that will later be fuzzed.

Tauri app structure
mini-app
- ...
- src/
- src-tauri/
    - src/
        - lib.rs
        - main.rs
        - tauri_commands/
            - file_access.rs
            - read_foo_file
            - ...
    - Cargo.toml

Make the Tauri Application Accessible to the Fuzzer

The Tauri app backend must be compiled as a crate such that the Tauri commands are exposed to the fuzzer.

For example we want to fuzz the Tauri commands called read_foo_file:

`mini-app/src-tauri/Cargo.toml`
[package]
name = "mini-app"
version = "0.0.0"
description = "A Tauri App"

# This section is automatic in Tauri v2
[lib]
crate-type = ["staticlib", "cdylib", "rlib"]
`mini-app/src-tauri/lib.rs`
/// define the module
pub mod tauri_commands;

/// publicly re-export the Taur command `read_foo_file`
pub use tauri_commands::file_access::read_foo_file
`mini-app/src-tauri/src-tauri/tauri_commands/file_access.rs`
#[tauri::command]
/// Mark the function as public
pub fn read_foo_file() -> String {
    let path        = get_foo_path();
    let mut content = String::new();
    let mut file    = File::open(path).unwrap();
    file.read_to_string(&mut content).unwrap();
    content
}

Fuzzing our Tauri app, quick guide

[Note] This section requires the CLI utility cargo-tauri-fuzz The project contains the crate crates/tauri-fuzz-cli that builds the binary cargo-tauri-fuzz. If any issue arises from using the CLI we recommend you follow the manual steps guide

1. Create fuzz directory

Execute cargo-tauri-fuzz init in mini-app/src-tauri.

Tauri app structure with fuzz directory
Project
- ...
- src/
  - ...
- src-tauri/
    - src/
        - lib.rs
        - main.rs
        - tauri_commands/
            - file_access.rs
            - read_foo_file
            - ...
    - fuzz/
        - build.rs
        - Cargo.toml
        - fuzz_targets/
            - _template_.rs
            - _template_full_.rs
        - fuzzer_config.toml
        - README.md
        - tauri.conf.json
    - Cargo.toml

2. Write your fuzz target

  • Copy mini-app/src-tauri/fuzz/fuzz_targets/_template_.rs as mini-app/src-tauri/fuzz/fuzz_targets/fuzz_read_foo.rs
  • Fill mini-app/src-tauri/fuzz/fuzz_targets/fuzz_read_foo.rs with relevant information
`mini-app/src-tauri/fuzz/fuzz_targets/fuzz_read_foo.rs`

Here we will fuzz the Tauri command read_foo against a policy that does not allow any file access.

// Copyright 2023-2024 CrabNebula Ltd., Alexandre Dang
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0

// This is a template to create a fuzz target
//
// Steps:
// 1. Copy this file and rename it
// 2. Change the target details below
// 3. Add the new fuzz target in [[bin]] table in Cargo.toml of your project
//
// Note: you may need to implement [FromRandomBytes] for your command argument types.

tauri_fuzz::fuzz_tauri_command! {
    // Name of the tauri command you want to fuzz
    command: "read_foo_file",
    // Pointer to the tauri command you want to fuzz
    path: {{crate_name_underscored}}::file_access::read_foo_file,
    // Parameters names and types to the tauri command
    parameters: {
        name: String,
    },
    // Policy chosen for the fuzzing
    // Here the policy will not allow any access to the filesystem
    policy: tauri_fuzz_policies::filesystem::no_file_access(),
}

3. Add the fuzz target as binary

Add fuzz_read_foo as a binary in mini-app/src-tauri/fuzz/Cargo.toml

`mini-app/src-tauri/fuzz/Cargo.toml`
[package]
name = "{{ crate_name }}-fuzz"
version = "0.0.0"
publish = false
edition = "2021"

[workspace]

[build-dependencies]
tauri-build = "2.0"

[dependencies]
{{ crate_name }} = { path = ".." }
tauri-fuzz-policies = { git = "ssh://git@github.com/crabnebula-dev/tauri-fuzz.git" }
tauri-fuzz = { git = "ssh://git@github.com/crabnebula-dev/tauri-fuzz.git", features = ["tauri"] }
tauri = { version = "2.0", features = ["test"]}
libafl = "0.13"

# Uncomment this block to add `fuzz_read_foo` as a fuzz target
# [[bin]]
# name = "fuzz_read_foo"
# path = "fuzz_targets/fuzz_read_foo.rs"
# doc = false

4. Start fuzzing

Start fuzzing by executing one of these commands.

From mini-app/src-tauri/ directory:

cargo-tauri-fuzz fuzz fuzz_read_foo

Or from mini-app/src-tauri/fuzz/ directory:

cargo r --bin fuzz_read_foo

5. Check your solutions

You should see this result repeatedly on your terminal:

Fuzzing results
[ERROR policies::engine] Policy was broken at function [open].
    Description: Access to [open] denied
    Rule: Rule::OnEntry
    Context: Function entry with parameters: [140736965046336, 524288]
The application panicked (crashed).
Message:  Intercepting call to [open].
Policy was broken at function [open].
Description: Access to [open] denied
Rule: Rule::OnEntry
Context: Function entry with parameters: [140736965046336, 524288]

This is the expected result. The Tauri command we fuzz, read_foo_file, tries to read foo.txt but got intercepted since we are fuzzing with a policy that does not allow any access to the filesystem.

More precisely the message specifies Policy was broken at function [open]. Indeed read_foo_file tried to use the libc function open that is used to access files and got intercepted.

The inputs used by the fuzzer which provokes a policy breach are stored in mini-app/src-tauri/fuzz/fuzz_solutions. Those inputs can be then investigated to understand why the policy breach happened.