3 unstable releases

0.2.0 Sep 20, 2024
0.1.1 Sep 19, 2024
0.1.0 Sep 18, 2024

#98 in Cargo plugins

MIT/Apache

31KB
288 lines

cargo-task-wasm

A sandboxed local task runner for Rust

About

This project provides a new cargo task subcommand that can be used to run project-local tasks inside a secure WebAssembly sandbox. It looks for files in a tasks/ subdirectory of your project's root, and compiles those to Wasm Components. This is an attempt at formalizing cargo-xtask pattern into a first-class, secure workflow.

Roadmap

  • Sketch out a repository layout or whatever workflow example
  • Create a new cargo subcommand
  • Hook up wasmtime to the subcommand
  • Add support for manual paths in a [tasks] section in Cargo.toml
  • Figure out how to configure capabilities for the tasks
  • Add support for compiling cargo deps as part of subcommands
  • Store config in Cargo metadata section
  • Add support for using submodules
  • Add the remainder of the capabilities
  • Add support for installing tasks from crates.io
  • Support workspaces and [workspace.metadata]

Installation

The cargo task subcommand compiles Rust to Wasm Components targeting WASI 0.2. In order to do that a working WASI 0.2 toolchain needs to be present on the host system.

$ rustup +beta target add wasip2  # Install the WASI 0.2 target
$ cargo install cargo-task-wasm   # Install the `cargo task` subcommand

Usage

Usage: cargo task <TASK_NAME> [ARGS]...

Arguments:
  <TASK_NAME>  The name of the task to run
  [ARGS]...    Optional arguments to pass to the task

Options:
  -h, --help     Print help
  -V, --version  Print version

Examples:
  cargo task codegen     # run a task called `codegen`

Configuration

Capabilities

Tasks in cargo task follow the principle of least privilege. By default they only get access to the working directory, and can access any additional command line arguments passed to it. Additional permissions can be configured via a [package.metadata.tasks] section in Cargo.toml.

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[package.metadata.tasks]
env-filter = { inherit-env = ["FOO"] }  # inherit specific env vars
env-all = { inherit-env = true }        # inherit all env vars

The reason why this is stored in [package.metadata.tasks] rather than a top-level [tasks] section is because that is the canonical extension point Cargo recommends using for third-party extensions. Should a cargo tasks command ever become a first-class extension to Cargo, the package.metadata prefix can be dropped.

Dependencies

Tasks must specify their own dependencies via [package.metadata.task-dependencies] in Cargo.toml. These dependencies are separate from Cargo's existing [dev-dependencies] and [build-dependencies] because these dependencies must be able to be compiled to Rust's wasm32-wasip2 target. Not all dev or build deps may fit these requirements, which is why task dependencies are listed separately.

[package]
name = "example"
version = "0.1.0"
edition = "2021"

[package.metadata.task-dependencies]
wstd = "0.4.0"

Paths

Tasks are discovered in the local tasks/ directory of your project. This is a treated as standalone workspace where each file is treated as an individual task to be compiled and executed. This behaves not unlike the tests/ directory in Cargo projects. It is possible to use both submodules and dependencies with tasks like you would expect. A typical project structure will look like this:

example/
├── Cargo.toml
├── src
│   └── lib.rs
└── tasks
    ├── codegen.rs
    └── test.rs

This structure will give you access to the cargo task codegen and cargo task test subcommands.

Limitations

By default tasks only get access to the local project directory and any additional arguments passed via the CLI. Additional capabilities such as network or filesystem access can be configured via Cargo.toml. Sandboxing is provided by the Wasmtime runtime, and the available APIs are part of the wasi:cli/command world. Some limitations however still exist, and are good to be aware of:

  • Limited ecosystem support: At the time of writing WASI 0.2 is a fairly new compile target, and so ecoystem support is still in its infancy. Not all crates are expected to work, and may need to be updated first.
  • Limited stdlib support: For similar reasons: not all functionality in the stdlib will work yet. In particular network support for WASI 0.2 is still being implemented. This is expected to land in Rust 1.84 in the second half of 2024. If you want to access the network before then, you can try and use the wasi or wstd crates.
  • No threading support: At the time of writing support for threading in WASI 0.2 has not yet been implemented. Work on this is still ongoing upstream in the WASI subgroup. Consensus on a design seems to have formed, and implementation work has started - but this is unlikely to stabilize before the start of 2025.
  • No support for exec/fork: WASI 0.2 does not allow you to spawn or fork new processes. Providing access to this would be a sandbox escape, and so we don't provide access to it. This means it's not possible to shell out to call global tools, which may at times be impractical but is also a necessary limitation to guarantee security.

In the future we hope to provide a way to instrument Cargo or Rustc directly from inside the sandbox. However this will need to be carefully evaluated and designed to ensure the sandbox cannot be escaped.

See Also

Safety

This crate uses #![deny(unsafe_code)] to ensure everything is implemented in 100% Safe Rust.

Contributing

Want to join us? Check out our "Contributing" guide and take a look at some of these issues:

Acknowledgements

This project was built as a collaboration between Michael Woerister and Yosh Wuyts as part of the 2024 Microsoft Hackathon, targeting the Microsoft Secure Future Initiative. Special thanks to Pat Hickey for showing us how to configure Wasmtime as a Rust library.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~35–47MB
~870K SLoC