Security is becoming an ever bigger concern as we see rising attacks and leaks based on source code weakness like SQL Injection or those that rely on vulnerabilities of dependencies like the one that affected log4j.
With the rapid growth of Elixir adoption, the community has to find ways to harden applications and increase confidence in the ecosystem.
As a team using Elixir, how can we ensure that each release does not increase risk? In this article, we will automate our Semaphore CI pipeline with static code analysis using Credo and Sobelow to audit our code, followed by checking our dependencies using Hex Audit and Mix audit.
All the code used can be found in this repository and you can check out the Semaphore pipeline.
Credo – not only for code quality
Let’s start with one of the more famous tools. Credo has long been one of the main tools used to push for higher quality code in Elixir, but it also gives some important warnings for more secure code.
Take a look at the following code:
defmodule Demo.Demo do
@moduledoc false
def bad_atom(string), do: String.to_atom(string)
def leaky(executable, arguments), do: System.cmd(executable, arguments)
def bad_exec, do: :os.cmd("ls")
end
Here we have 3 potential vulnerabilities:
bad_atom
can blow up the Beam VM by dynamically creating too many tuples and exceeding Beam VM limits.leaky
can expose/override/clear important environment variables from your system since this process would have the same env as the parent.bad_exec
can execute unsafe code by sending shell commands to your OS.
With mix credo
we will get the warnings we need to prevent us from deploying dangerous code like this.
Plus, Credo makes it easy to extend and add new custom rules that might be important to increase security for your use case.
How do we set up Credo?
- In your
mix.exs
file, you need to add the following dependency:
{:credo, "~> 1.6", runtime: false, only: :dev},
2. To capture these errors you will need to adapt your .credo.exs file, as shown below:
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
# ...
checks: [
# ...
#
# Controversial and experimental checks (opt-in, replace `false` with `[]`)
#
# ...
{Credo.Check.Warning.UnsafeToAtom, []},
{Credo.Check.Warning.LeakyEnvironment, []}
# ...
]
}
]
}
- You can automate Credo in your CI with the following command:
- name: Analyze code
task:
# ...
jobs:
# ...
- name: credo
commands:
- mix credo -a
You should end up with a failing job that looks like this:

Sobelow – static analysis focused on security
Letβs keep the focus on static code analysis and dive a bit deeper. Credo is great but lacks some base Elixir warnings and doesnβt detect common mistakes from libraries like Phoenix and Ecto.
That’s where Sobelow comes in. This tool will comb your code to find common pitfalls.
Take a look at the following configuration file:
import Config
config :demo, Demo.Repo,
database: "demo",
username: "postgres",
password: "UG90YXRv",
hostname: "localhost"
If you committed this code to your repository, you could easily leak a secret. Sobelow would catch this, as it supports warnings from Config files to Phoenix controller responses. Sobelow is able to find this type of issue by running mix sobelow
.
You can review supported checkers in the Documentation.
How to set up Sobelow
- In your
mix.exs
file, you need to add the following dependency:
{:sobelow, "~> 0.8", only: :dev}
2. You can automate the install in your CI with the following command:
- name: Set up
task:
jobs:
- name: compile and build plts
commands:
# ...
- mix escript.install --force hex sobelow
# ...
3. And also automate running Sobelow in your CI, as shown below:
- name: Analyze code
task:
# ...
jobs:
# ...
- name: sobelow
commands:
- mix sobelow --exit medium
You should end up with a failing job that looks like this:

Important: note the command mix sobelow --exit medium
. Sobelow will return an error exit code with a medium or higher vulnerability found.
mix hex.audit – avoid unsupported packages
Now that we’ve improved our code, we need to check the code of others. mix hex.audit already has some tooling to check which dependencies have been retired by the maintainer.
Check out the following mix.exs
dependencies:
defp deps do
[
{:paginator, "~> 0.6.0"}
]
end
Paginator is a great project that you might find in blog posts, so you might have copied and pasted it from an article, but forgotten to check for the latest version.
By using it, we can reduce the risk of introducing vulnerable code. With mix hex.audit we are also able to detect retired libraries.
How to setup mix hex.audit
- You can automate running hex audit on your CI, as shown below:
- name: Analyze code
task:
# ...
jobs:
# ...
- name: retired packages
commands:
- mix hex.audit
You should end up with a failing job that looks like this:

Mix Audit – check dependencies for CVEs
CVEs are a great tool to check which dependencies have proven vulnerabilities and now the GitHub Advisory Database has Erlang / Elixir CVEs, which means we have a great database at our disposal.
See the following mix.exs
dependencies:
defp deps do
[
{:sweet_xml, "~> 0.6.0"}
]
end
Same as the example above, you might have copied a bad version of sweet_xml
with a known CVE.
You are opening the door for a potential attack vector by not using another version and mix hex.audit
wouldn’t find it since it wasn’t retired.
How to setup mix deps.audit
- In your
mix.exs
file, you need to add the following dependency:
{:mix_audit, "~> 2.0", only: [:dev, :test], runtime: false}
2. You can automate the install in your CI with the following command:
- name: Set up
task:
jobs:
- name: compile and build plts
commands:
# ...
- mix escript.install --force hex mix_audit
# ...
3. You can automate running Mix Audit on your CI, as shown below:
- name: Analyze code
task:
# ...
jobs:
# ...
- name: audit
commands:
- mix deps.audit
You should end up with a failing job that looks like this:

Conclusion
With these four tools, we are able to improve both our code security and dependency checking, getting warnings about attack vectors in an automated and actionable way.
Some of the tools seem to overlap but in reality, they work best in conjunction:
- Credo makes it easy to add new rules if you find more patterns
- Sobelow checks how you write code and use dependencies
- Hex Audit verifies errors in retired packages
- Mix Audit actively warns you about CVEs in your application