Have a look at our new Handbook: "Transitioning from Monolith to Microservices"!  Discover →

    3 Nov 2022 · Software Engineering

    Elixir Code Security: Prioritize Security in Your CI With 4 Tools

    6 min read
    Contents

    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:

    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?

    1. 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, []}
     # ...
          ]
        }
      ]
    }
    1. 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

    1. 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

    1. 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

    1. 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

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    mm
    Writen by:
    Software Developer from sunny Lisbon that loves all things from OTP and Elixir but also has coded with Java, Scala, Clojure, Kotlin, TS, and JS in the past. Currently working at Supabase developing highly concurrent software for log ingestion and event broadcasting.
    Avatar for Filipe Cabaco
    Reviewed by:
    I picked up most of my soft/hardware troubleshooting skills in the US Army. A decade of Java development drove me to operations, scaling infrastructure to cope with the thundering herd. Engineering coach and CTO of Teleclinic.