5 unstable releases

0.3.0 May 7, 2024
0.2.1 Apr 14, 2024
0.2.0 Apr 8, 2024
0.1.1 Mar 25, 2024
0.1.0 Mar 14, 2024

#50 in Build Utils

Download history 95/week @ 2024-03-11 14/week @ 2024-03-18 138/week @ 2024-03-25 40/week @ 2024-04-01 153/week @ 2024-04-08 143/week @ 2024-04-15 142/week @ 2024-05-06

291 downloads per month

AGPL-3.0

25KB
465 lines

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:
    cargo install build-wrap
    
  2. Create a .cargo/config.toml file in your home directory with the following contents:
    [target.'cfg(all())']
    linker = "build-wrap"
    

Environment variables that build-wrap reads

  • 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.:

    BUILD_WRAP_ALLOW=1 cargo build -vv
    
  • BUILD_WRAP_CMD: Command used to execute a build script. Linux default:

    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
    

    Note that bwrap is Bubblewrap.

    macOS default:

    sandbox-exec -p
    (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
    {}                                                   # Build script path
    

    Note that (version\ 1)\ ... (deny\ network*) expands to a single string (see How BUILD_WRAP_CMD is expanded below).

  • BUILD_WRAP_LD: Linker to use. Default: cc

Note that the above environment variables are read when the build script is linked. So, for example, changing BUILD_WRAP_CMD will not change the command used to execute already linked build scripts.

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.

  • 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 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 so, replace the build script B with its "wrapped" version B', described next.

Given a build script B, its "wrapped" version B' contains a copy of B and does the following when invoked:

  1. Create a temporary file with the contents of B. (Recall: B' contains a copy of B).
  2. Make the temporary file executable.
  3. Expand BUILD_WRAP_CMD in the manner described above.
  4. 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.

Dependencies

~2–11MB
~123K SLoC