Setting Up in CI/CD
SAFE runs as a step in your CI pipeline. When vulnerabilities are found it exits with a non-zero code, blocking the build. This page covers how to integrate SAFE into common CI systems.
Approaches
There are three ways to add SAFE to a pipeline:
- Binary download — download the SAFE binary as part of the job. No changes to your project files.
- Mix plugin (mix_safe) — add
mix_safeto yourmix.exsdependencies. The plugin manages the SAFE binary automatically and integrates with the Mix workflow. See mix_safe. - Rebar3 plugin (rebar_safe) — add
rebar_safeto yourrebar.configplugins. The plugin manages the SAFE binary automatically and integrates with the Rebar3 workflow. See rebar_safe.
Prerequisites
Before running SAFE in CI, make sure:
.safe/config.jsonis committed to your repository. Generate it locally withsafe setupand commit the result.- The
SAFE_LICENSEsecret is configured in your CI system.
GitHub Actions
- mix_safe
- rebar_safe
- Binary download
name: SAFE Security Scan
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
safe:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: '26'
elixir-version: '1.16'
- name: Restore plugin binary cache
uses: actions/cache@v4
with:
path: _build/safe
key: safe-binary-${{ hashFiles('safe.lock') }}
restore-keys: safe-binary-
- name: Restore analysis state cache
uses: actions/cache@v4
with:
path: ~/.safe
key: safe-state-${{ github.ref }}-${{ github.sha }}
restore-keys: |
safe-state-${{ github.ref }}-
safe-state-
- name: Install dependencies
run: mix deps.get
- name: Compile
run: mix compile
- name: Run SAFE analysis
run: mix safe analyse
env:
SAFE_LICENSE: ${{ secrets.SAFE_LICENSE }}
name: SAFE Security Scan
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
safe:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: '26'
rebar3-version: '3.24.0'
- name: Restore plugin binary cache
uses: actions/cache@v4
with:
path: _build/safe
key: safe-binary-${{ github.ref }}
restore-keys: safe-binary-
- name: Restore analysis state cache
uses: actions/cache@v4
with:
path: ~/.safe
key: safe-state-${{ github.ref }}-${{ github.sha }}
restore-keys: |
safe-state-${{ github.ref }}-
safe-state-
- name: Compile
run: rebar3 compile
- name: Run SAFE analysis
run: rebar3 safe analyse
env:
SAFE_LICENSE: ${{ secrets.SAFE_LICENSE }}
name: SAFE Security Scan
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
safe:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Restore analysis state cache
uses: actions/cache@v4
with:
path: ~/.safe
key: safe-state-${{ github.ref }}-${{ github.sha }}
restore-keys: |
safe-state-${{ github.ref }}-
safe-state-
- name: Download SAFE
run: |
mkdir -p ~/.safe/install
curl -fsSL -o ~/.safe/safe.tar.gz https://safe-releases.s3.eu-central-1.amazonaws.com/1.5.0/safe-1.5.0-linux-x86_64.tar.gz
tar -xzf ~/.safe/safe.tar.gz -C ~/.safe/install
echo "$HOME/.safe/install" >> $GITHUB_PATH
- name: Run SAFE analysis
run: safe start --config-path .safe/config.json
env:
SAFE_LICENSE: ${{ secrets.SAFE_LICENSE }}
GitLab CI
GitLab CI cache paths must be relative to the project directory. SAFE's analysis state lives at ~/.safe, which is outside that scope, so incremental state caching is not available on GitLab. Each run performs a full analysis.
- mix_safe
- rebar_safe
- Binary download
safe:
stage: test
image: elixir:1.16
variables:
SAFE_LICENSE: $SAFE_LICENSE
before_script:
- mix deps.get
- mix compile
script:
- mix safe analyse
safe:
stage: test
image: erlang:26
variables:
SAFE_LICENSE: $SAFE_LICENSE
before_script:
- curl -fsSL https://s3.amazonaws.com/rebar3/rebar3 -o /usr/local/bin/rebar3 && chmod +x /usr/local/bin/rebar3
- rebar3 compile
script:
- rebar3 safe analyse
safe:
stage: test
image: ubuntu:22.04
variables:
SAFE_LICENSE: $SAFE_LICENSE
before_script:
- apt-get update -qq && apt-get install -y -qq curl
script:
- mkdir -p ~/.safe/install
- curl -fsSL -o ~/.safe/safe.tar.gz https://safe-releases.s3.eu-central-1.amazonaws.com/1.5.0/safe-1.5.0-linux-x86_64.tar.gz
- tar -xzf ~/.safe/safe.tar.gz -C ~/.safe/install
- export PATH=$HOME/.safe/install:$PATH
- safe start --config-path .safe/config.json
CircleCI
CircleCI requires environment variables to be configured in the project settings or as a context. Set SAFE_LICENSE there rather than hard-coding it in the config.
- mix_safe
- rebar_safe
- Binary download
version: 2.1
jobs:
safe:
docker:
- image: cimg/elixir:1.17
steps:
- checkout
- restore_cache:
keys:
- safe-binary-{{ checksum "safe.lock" }}
- safe-binary-
- restore_cache:
keys:
- safe-state-{{ .Branch }}-{{ .Revision }}
- safe-state-{{ .Branch }}-
- safe-state-
- run:
name: Install dependencies
command: mix deps.get
- run:
name: Compile
command: mix compile
- run:
name: Run SAFE analysis
command: mix safe analyse
- save_cache:
key: safe-binary-{{ checksum "safe.lock" }}
paths:
- _build/safe
- save_cache:
key: safe-state-{{ .Branch }}-{{ .Revision }}
paths:
- ~/.safe
workflows:
version: 2
security:
jobs:
- safe
version: 2.1
jobs:
safe:
docker:
- image: cimg/elixir:1.17
steps:
- checkout
- restore_cache:
keys:
- safe-state-{{ .Branch }}-{{ .Revision }}
- safe-state-{{ .Branch }}-
- safe-state-
- run:
name: Compile
command: rebar3 compile
- run:
name: Run SAFE analysis
command: rebar3 safe analyse
- save_cache:
key: safe-state-{{ .Branch }}-{{ .Revision }}
paths:
- ~/.safe
workflows:
version: 2
security:
jobs:
- safe
version: 2.1
jobs:
safe:
docker:
- image: cimg/base:stable
steps:
- checkout
- restore_cache:
keys:
- safe-state-{{ .Branch }}-{{ .Revision }}
- safe-state-{{ .Branch }}-
- safe-state-
- run:
name: Download SAFE
command: |
mkdir -p ~/.safe/install
curl -fsSL -o ~/.safe/safe.tar.gz https://safe-releases.s3.eu-central-1.amazonaws.com/1.5.0/safe-1.5.0-linux-x86_64.tar.gz
tar -xzf ~/.safe/safe.tar.gz -C ~/.safe/install
echo 'export PATH=$HOME/.safe/install:$PATH' >> $BASH_ENV
- run:
name: Run SAFE analysis
command: safe start --config-path .safe/config.json
- save_cache:
key: safe-state-{{ .Branch }}-{{ .Revision }}
paths:
- ~/.safe
workflows:
version: 2
security:
jobs:
- safe
Caching
Caching reduces scan time significantly on repeat runs. There are two directories worth caching:
| Directory | Purpose | Cache key recommendation |
|---|---|---|
~/.safe | SAFE analysis state. Stores the results of previous runs so only changed files are re-analysed. | Branch + commit SHA, falling back to branch |
_build/safe | Plugin binary cache (mix_safe / rebar_safe). Avoids re-downloading the SAFE binary on every run. | Contents of safe.lock (mix_safe) or branch (rebar_safe) |
Always restore the ~/.safe cache before the compile step, and save it after the SAFE step. This ensures the analysis state reflects the compiled BEAM files from the current run.
Exit Codes
| Code | Meaning |
|---|---|
0 | Analysis completed successfully — no vulnerabilities found. |
1 | An error occurred (bad configuration, missing license, etc.). |
2 | Analysis completed — vulnerabilities were found. |