https://github.com/trailofbits/build-wrap
Help protect against malicious build scripts
https://github.com/trailofbits/build-wrap
build-scripts rust sandboxing
Last synced: about 1 month ago
JSON representation
Help protect against malicious build scripts
- Host: GitHub
- URL: https://github.com/trailofbits/build-wrap
- Owner: trailofbits
- License: agpl-3.0
- Created: 2024-03-09T12:38:21.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2026-04-22T12:49:58.000Z (about 1 month ago)
- Last Synced: 2026-04-22T13:25:45.757Z (about 1 month ago)
- Topics: build-scripts, rust, sandboxing
- Language: Rust
- Homepage: https://crates.io/crates/build-wrap
- Size: 229 KB
- Stars: 27
- Watchers: 10
- Forks: 4
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
- Codeowners: CODEOWNERS
Awesome Lists containing this project
- awesome-software-supply-chain-security - trailofbits/build-wrap: Linker replacement to sandbox build scripts using Bubblewrap (Linux) or sandbox-exec (macOS), protecting against malicious build script execution
README
# build-wrap
A linker replacement to help protect against malicious build scripts
`build-wrap` "re-links" a build script so that it is executed under another command. By default, the command is [Bubblewrap] (Linux) or [`sandbox-exec`] (macOS), though this is configurable. See [Environment variables that `build-wrap` reads] and [How `build-wrap` works] for more information.
## Installation
Installing `build-wrap` requires two steps:
1. Install `build-wrap` with Cargo:
```sh
cargo install build-wrap
```
2. Create a `.cargo/config.toml` file in your home directory with the following contents:
```toml
[target.'cfg(all())']
linker = "build-wrap"
```
### Ubuntu 24.04
Ubuntu's default AppArmor profiles [changed with version 24.04]. The changes [affect Bubblewrap], which in turn affect `build-wrap`. Thus, installing `build-wrap` on Ubuntu 24.04 requires some additional steps:
```sh
sudo apt install apparmor-profiles
sudo cp /usr/share/apparmor/extra-profiles/bwrap-userns-restrict /etc/apparmor.d
sudo systemctl reload apparmor
```
Note that following these additional steps, Bubblewrap still runs unprivileged. More information on AppArmor profiles can be found on [Ubuntu Server] and the [Ubuntu Community Wiki].
## Environment variables that `build-wrap` reads
Note that the below environment variables are read **when a build script is linked**. So, for example, changing `BUILD_WRAP_CMD` will not change the command used to execute already linked build scripts.
- `BUILD_WRAP_ALLOW`: When set to a value other than `0`, `build-wrap` uses the following weakened strategy. If running a build script under `BUILD_WRAP_CMD` fails, report the failure and rerun the build script normally.
Note that to see the reported failures, you must invoke Cargo with the `-vv` (["very verbose"]) flag, e.g.:
```sh
BUILD_WRAP_ALLOW=1 cargo build -vv
```
To disable sandboxing entirely for specific directories or packages, use [`$HOME/.config/build-wrap/config.toml`] (see below).
- `BUILD_WRAP_CMD`: Command used to execute a build script. Linux default:
- With comments:
```sh
bwrap
--ro-bind / / # Allow read-only access everywhere
--dev-bind /dev /dev # Allow device access
--bind {OUT_DIR} {OUT_DIR} # Allow write access to `OUT_DIR`
--bind /tmp /tmp # Allow write access to /tmp
--unshare-net # Deny network access
{} # Build script path
```
- On one line (for copying-and-pasting):
```sh
bwrap --ro-bind / / --dev-bind /dev /dev --bind {OUT_DIR} {OUT_DIR} --bind /tmp /tmp --unshare-net {}
```
Note that `bwrap` is [Bubblewrap].
macOS default:
```sh
sandbox-exec -f {BUILD_WRAP_PROFILE_PATH} {}
```
See [Environment variables that `build-wrap` treats as set] regarding `BUILD_WRAP_PROFILE_PATH`.
- `BUILD_WRAP_LD`: Linker to use. Default: `cc`
- `BUILD_WRAP_PROFILE`: macOS only. `build-wrap` expands `BUILD_WRAP_PROFILE` [as it would `BUILD_WRAP_CMD`], and writes the results to a temporary file. `BUILD_WRAP_PROFILE_PATH` then expands to the absolute path of that temporary file. Default:
```
(version 1)
(deny default)
(allow file-read*) ;; Allow read-only access everywhere
(allow file-write* (subpath "/dev")) ;; Allow write access to /dev
(allow file-write* (subpath "{OUT_DIR}")) ;; Allow write access to `OUT_DIR`
(allow file-write* (subpath "{TMPDIR}")) ;; Allow write access to `TMPDIR`
(allow file-write* (subpath "{PRIVATE_TMPDIR}")) ;; Allow write access to `PRIVATE_TMPDIR` (see below)
(allow process-exec) ;; Allow `exec`
(allow process-fork) ;; Allow `fork`
(allow sysctl-read) ;; Allow reading kernel state
(deny network*) ;; Deny network access
```
## `$HOME/.config/build-wrap/config.toml`
If a file at `$HOME/.config/build-wrap/config.toml` exists, `build-wrap` reads it to determine which directories and packages should be allowed to build without sandboxing. Like the environment variables above, this file is consulted when a build script is linked. Changing it does not affect already linked build scripts.
The file supports `[allow]` and `[ignore]` sections, which are treated as synonyms:
```toml
[allow]
directories = ["/home/user/project-a"]
packages = ["aws-lc-fips-sys"]
[ignore]
directories = ["/home/user/project-b"]
packages = ["svm-rs-builds"]
```
- `directories`: A list of directory paths. If `cargo build` is run from within a listed directory (or any subdirectory), `build-wrap` will not sandbox the build scripts. A leading `~` or `~/` is expanded to the current user's home directory.
- `packages`: A list of package names. Build scripts belonging to listed packages will not be sandboxed.
Both sections are merged, so entries from `[allow]` and `[ignore]` are combined.
For example, if you frequently build in a project that has dependencies requiring unrestricted build scripts:
```sh
mkdir -p "$HOME/.config/build-wrap"
cat > "$HOME/.config/build-wrap/config.toml" << 'EOF'
[allow]
packages = ["svm-rs-builds"]
EOF
```
## Environment variables that `build-wrap` treats as set
Note that we say "treats as set" because these are considered only when [`BUILD_WRAP_CMD` is expanded].
- `BUILD_WRAP_PROFILE_PATH`: Expands to the absolute path of a temporary file containing the expanded contents of `BUILD_WRAP_PROFILE`.
- `PRIVATE_TMPDIR`: If `TMPDIR` is set to a path in `/private` (as is typical on macOS), then `PRIVATE_TMPDIR` expands to that path. This is needed for some build scripts that use [`cc-rs`], though the exact reason it is needed is still unknown.
## How `BUILD_WRAP_CMD` is expanded
- `{}` is replaced with the path of a renamed copy of the original build script.
- `{VAR}` is replaced with the value of environment variable `VAR`.
- `{{` is replaced with `{`.
- `}}` is replaced with `}`.
- `\` followed by a whitespace character is replaced with that whitespace character.
- `\\` is replaced with `\`.
## How `build-wrap` works
When invoked, `build-wrap` does the following:
1. Link normally using `BUILD_WRAP_LD`.
2. Parse the arguments to determine whether the output file is a build script.
3. If not, stop; otherwise, proceed.
4. Let `B` be the build script's original name.
5. Rename the build script to a fresh, unused name `B'`.
6. At `B`, create a "wrapped" version of the build script whose behavior is described next.
The "wrapped" version of the build script does the following when invoked:
1. Expand `BUILD_WRAP_CMD` in the [manner described above], with `{}` expanding to `B'`.
2. Execute the expanded command.
## Goals
- Aside from configuration and dealing with an occasional warning, `build-wrap` should not require a user to adjust their normal workflow.
["very verbose"]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script
[Bubblewrap]: https://github.com/containers/bubblewrap
[Environment variables that `build-wrap` reads]: #environment-variables-that-build-wrap-reads
[Environment variables that `build-wrap` treats as set]: #environment-variables-that-build-wrap-treats-as-set
[How `build-wrap` works]: #how-build-wrap-works
[Ubuntu Community Wiki]: https://help.ubuntu.com/community/AppArmor
[Ubuntu Server]: https://documentation.ubuntu.com/server/how-to/security/apparmor/
[`$HOME/.config/build-wrap/config.toml`]: #homeconfigbuild-wrapconfigtoml
[`BUILD_WRAP_CMD` is expanded]: #how-build_wrap_cmd-is-expanded
[`cc-rs`]: https://github.com/rust-lang/cc-rs
[`sandbox-exec`]: https://keith.github.io/xcode-man-pages/sandbox-exec.1.html
[affect Bubblewrap]: https://github.com/containers/bubblewrap/issues/505#issuecomment-2093203129
[as it would `BUILD_WRAP_CMD`]: #how-build_wrap_cmd-is-expanded
[changed with version 24.04]: https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces
[manner described above]: #how-build_wrap_cmd-is-expanded