# gVisor Sandboxed Step A GitHub Action that runs commands in a gVisor sandbox. ```yaml - uses: geomys/sandboxed-step@v1.2.0 with: run: | go get -u -t ./... go test ./... ```

The Geomys logo, an ink outline of a quaint Italian town on the side of a mountain.

## Motivation Surprisingly enough, GitHub Actions with read-only permissions still receive a cache write token, allowing [cache poisoning](https://adnanthekhan.com/2024/05/06/the-monsters-in-your-build-cache-github-actions-cache-poisoning/), so they are not safe to run untrusted code. This has led to [supply chain compromises](https://words.filippo.io/compromise-survey/). Moreover, there is no isolation between steps in a workflow, since they all run on the same VM with root access. The only alternative is separating workflows with `workflow_run`, but that has its own limitations and overhead. This can lead to a false sense of security when running untrusted code in a workflow or step with read-only permissions (despite defaulting workflows to read-only permissions being a top-level feature). This Action runs commands in an isolated gVisor sandbox, allowing e.g. running CI against the latest versions of dependencies without risking being affected by supply chain attacks. ## Usage The commands run in a gVisor sandbox with - a root filesystem similar to ubuntu-24.04 ([`ghcr.io/catthehacker/ubuntu:runner-24.04`](https://github.com/catthehacker/docker_images)) - overlayed access to GITHUB_WORKSPACE (changes do not persist by default) - GITHUB_WORKSPACE working directory - host network access - allow-listed environment variables - same user as the GitHub Actions runner, with sudo access - read-only access to tools installed by setup-* actions (via RUNNER_TOOL_CACHE) Changes to the workspace inside the sandbox can be made to persist on the host by setting `persist-workspace-changes: 'true'`. This is unlikely to be safe, as following steps will need to treat the workspace as untrusted. > [!NOTE] > This action will detect and fail if the `actions/checkout` Action has > persisted authentication tokens in GITHUB_WORKSPACE (the default behavior). > > To use this action, you must disable credential persistence in your checkout > step: > > ```yaml > - uses: actions/checkout@v4 > with: > persist-credentials: false > ``` > > Alternatively, you can set `allow-checkout-credentials: true` to bypass this > check, but this is **NOT RECOMMENDED** as it will expose the GitHub token to > the sandbox. All tags use GitHub's Immutable Releases, so they can't be changed even if this repository is compromised. ### Inputs - `run` (required): Commands to run in the sandbox - `env` (optional): Additional environment variables to set in the sandbox (one per line, KEY=VALUE format) - `persist-workspace-changes` (optional, default: `false`): Allow changes to persist on the host - `disable-network` (optional, default: `false`): Disable network access in the sandbox - `allow-checkout-credentials` (optional, default: `false`): Allow persisted checkout credentials - `rootfs-image` (optional): Docker image to use as the root filesystem ### Example for sandboxed Go tests with latest dependencies ```yaml name: Go tests on: push: pull_request: schedule: # daily at 09:42 UTC - cron: '42 9 * * *' workflow_dispatch: permissions: contents: read jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: go: - { go-version: stable } - { go-version: oldstable } - { go-version-file: go.mod } deps: - locked - latest steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-go@v6 with: go-version: ${{ matrix.go.go-version }} go-version-file: ${{ matrix.go.go-version-file }} - uses: geomys/sandboxed-step@v1.1.1 with: run: | if [ "${{ matrix.deps }}" = "latest" ]; then go get -u -t ./... fi go test ./... staticcheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-go@v6 with: go-version: stable - uses: geomys/sandboxed-step@v1.1.1 with: run: go run honnef.co/go/tools/cmd/staticcheck@latest ./... govulncheck: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: persist-credentials: false - uses: actions/setup-go@v6 with: go-version: stable - uses: geomys/sandboxed-step@v1.1.1 with: run: go run golang.org/x/vuln/cmd/govulncheck@latest ./... ```