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.
Installing build-wrap
requires two steps:
- Install
build-wrap
with Cargo:cargo install build-wrap
- Create a
.cargo/config.toml
file in your home directory with the following contents:[target.'cfg(all())'] linker = "build-wrap"
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 than0
,build-wrap
uses the following weakened strategy. If running a build script underBUILD_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.:BUILD_WRAP_ALLOW=1 cargo build -vv
-
BUILD_WRAP_CMD
: Command used to execute a build script. Linux default:-
With comments:
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):
bwrap --ro-bind / / --dev-bind /dev /dev --bind {OUT_DIR} {OUT_DIR} --bind /tmp /tmp --unshare-net {}
Note that
bwrap
is Bubblewrap.macOS default:
sandbox-exec -f {BUILD_WRAP_PROFILE_PATH} {}
See Environment variables that
build-wrap
treats as set regardingBUILD_WRAP_PROFILE_PATH
. -
-
BUILD_WRAP_LD
: Linker to use. Default:cc
-
BUILD_WRAP_PROFILE
: macOS only.build-wrap
expandsBUILD_WRAP_PROFILE
as it wouldBUILD_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
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 ofBUILD_WRAP_PROFILE
. -
PRIVATE_TMPDIR
: IfTMPDIR
is set to a path in/private
(as is typical on macOS), thenPRIVATE_TMPDIR
expands to that path. This is needed for some build scripts that usecc-rs
, though the exact reason it is needed is still unknown.
{}
is replaced with the path of a copy of the original build script.{VAR}
is replaced with the value of environment variableVAR
.{{
is replaced with{
.}}
is replaced with}
.\
followed by a whitespace character is replaced with that whitespace character.\\
is replaced with\
.
When invoked, build-wrap
does the following:
- Link normally using
BUILD_WRAP_LD
. - Parse the arguments to determine whether the output file is a build script.
- If so, replace the build script
B
with its "wrapped" versionB'
, described next.
Given a build script B
, its "wrapped" version B'
contains a copy of B
and does the following when invoked:
- Create a temporary file with the contents of
B
. (Recall:B'
contains a copy ofB
). - Make the temporary file executable.
- Expand
BUILD_WRAP_CMD
in the manner described above. - Execute the expanded command.
- Aside from configuration and dealing with an occasional warning,
build-wrap
should not require a user to adjust their normal workflow.