Security is becoming an essential concern for companies as we're seeing a rise in attacks and leaks.
As a team using Elixir how can we ensure that each release does not increase risk? We're going to use many tools from the Elixir ecosystem to increase our confidence and automated using Semaphore CI.
All the code used is based on the Semaphore Demo Elixir Phoenix repository and we're adding our changes to ./semaphore.yml
Let's start with one of the more famous tools. Credo has long been one of the main tools to push for higher quality code in Elixir but it also has some important warnings for more secure code.
Check 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")
endHere we have 3 potential vulnerabilities:
bad_atomcan blow up the Beam VM by creating too many tuples dynamically and exceeding Beam VM limitsleakycan expose / override/ clear important environment variables from your system since this process would have the same env as the parentbad_execcan execute unsafe code by sending shell commands to your OS
With mix credo we will have the warnings we need to prevent us from deploying dangerous code.
Plus, Credo makes it easy to extend and add new custom rules that might deem important to increase security for your use case.
- In your
mix.exsfile, you need to add the following dependency
{:credo, "~> 1.6", runtime: false, only: :dev},- To capture these errors you will need to adapt your
.credo.exsfile:
%{
#
# 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, []}
# ...
]
}
]
}- Automate running Credo your CI:
- name: Analyze code
task:
# ...
jobs:
# ...
- name: credo
commands:
- mix credo -aYou should end up with a failing job looking like this:

Still focused on code static analysis, we can dive a bit deeper. Credo is great but lacks some base Elixir warnings and is unaware of common mistakes from common libraries like Phoenix and Ecto.
That's where Sobelow enters. This tool will comb your code to find common pitfalls.
Check 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 have leaked a secret. And this is the start for Sobelow since it supports warnings from Config files to Phoenix controller responses. Sobelow will be able to find this type of issue by running mix sobelow.
You can check supported checkers in the Documentation.
- In your
mix.exsfile, you need to add the following dependency
{:sobelow, "~> 0.8", only: :dev}- Automate install on your CI
- name: Set up
task:
jobs:
- name: compile and build plts
commands:
# ...
- mix escript.install --force hex sobelow
# ...- Automate running Sobelow on your CI
- name: Analyze code
task:
# ...
jobs:
# ...
- name: sobelow
commands:
- mix sobelow --exit mediumYou should end up with a failing job looking like this:

Important: Do note the command mix sobelow --exit medium. Sobelow will return an error exit code with a medium or higher vulnerability found
Now that we've improved our code we need to check the code of others. mix already has some tooling to check which dependencies are retired by choice of the maintainer.
Check the following mix.exs dependencies:
defp deps do
[
{:paginator, "~> 0.6.0"}
]
endPaginator's a great project that you might find in several blog posts so you might copy and paste it from the article but forgot to check the latest version.
By using it, we can increase the risk of using vulnerable code. With mix hex.audit we will be able to detect retired libraries.
- Automate running hex audit on your CI
- name: Analyze code
task:
# ...
jobs:
# ...
- name: retired packages
commands:
- mix hex.auditYou should end up with a failing job looking like this:

CVEs are a great tool to check what dependencies have proven vulnerabilities and now GitHub Advisory Database has Erlang / Elixir CVEs which means that have a great database at our disposal.
Check the following mix.exs dependencies:
defp deps do
[
{:sweet_xml, "~> 0.6.0"}
]
endSame 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.
- In your
mix.exsfile, you need to add the following dependency
{:mix_audit, "~> 2.0", only: [:dev, :test], runtime: false}- Automate install on your CI
- name: Set up
task:
jobs:
- name: compile and build plts
commands:
# ...
- mix escript.install --force hex mix_audit
# ...- Automate running Mix Audit on your CI
- name: Analyze code
task:
# ...
jobs:
# ...
- name: audit
commands:
- mix deps.auditYou should end up with a failing job looking like this:

With these four tools, we were able to improve both our code security and dependency checking, warning about attack vectors in an automated and actionable way that will allow your team to improve security.
Some of the tools seem to overlap but in reality, they work the best in conjunction:
- Credo will make it easy for you to add new rules if you find more patterns
- Sobelow checks for how you write your code and use your dependencies
- Hex Audit verifies errors on retired packages
- Mix Audit will ensure you actively avoid CVEs in your system