Paraxial.io 2.12.0 Scans Erlang/OTP for Known Vulnerabilities

Paraxial.io, 2026-10-19

Paraxial.io for Elixir version 2.12.0 adds Erlang/OTP vulnerability scanning to mix paraxial.scan. The scan now reports the known security advisories that affect the Erlang/OTP version your project is running on, alongside the existing hex dependency, license, and Sobelow results. Here is everything you need to use it.


Upgrading

{:paraxial, "~> 2.12"}

What the scan reports

Every scan prints the OTP version it checked, how many advisories it checked against, and where that advisory data came from:

[Paraxial] Erlang/OTP 28.5, checked against 70 advisories (bundled data 2026-10-09)

Each matching advisory becomes a finding, worst severity first:

[Paraxial] otp_advisory
  affected_applications: ssl
  cve_id: CVE-2026-55953
  dependency: erlang_otp
  first_patched_version: 28.5.0.4
  ghsa_id: GHSA-c6cw-pr89-w882
  severity: critical
  summary: (D)TLS-1.2  Server Cert Verification Bypass via Unoffered Anonymous Cipher Suite
  title: Erlang/OTP CVE-2026-55953
  url: https://github.com/erlang/otp/security/advisories/GHSA-c6cw-pr89-w882
  version: 28.0.1

first_patched_version is the lowest fixed release on your own major branch, so it is an upgrade you can take without a major version jump. When a branch was already end-of-life when the advisory was published and will never get a fix, the finding says so and points at the lowest fixed version above it.


How to ignore advisories

OTP findings use the reserved dependency name erlang_otp in .paraxial-ignore-deps, with the same two formats as a hex dependency. To turn the OTP check off entirely:

erlang_otp

To ignore one advisory, by CVE or GHSA id:

erlang_otp CVE-2026-55953

Advisory ids in your hex :ignore_advisories config are honored here too. Ignore rules are sent with the scan so the Paraxial.io server, which re-runs the match against its own fresher data, does not resurrect an advisory you chose to ignore.


Before you upgrade a CI pipeline

If you run mix paraxial.scan --add-exit-code, OTP advisories count as findings like everything else. A project on an older OTP patch release that passed yesterday can fail today. That is the intended behavior, but run the scan locally first to see what your runtime is carrying.


When the check is skipped

The agent needs the full OTP version, which lives in the OTP_VERSION file of a complete OTP installation. That file is absent inside a mix release and in target systems built by OTP tools. The advisory check runs at scan time, where a full OTP install always exists, so this should not come up in practice, but when the file is missing the check is skipped rather than failing the scan:

[Paraxial] OTP_VERSION file not found, Erlang/OTP advisory check skipped

Why this was added

A Paraxial.io scan already runs mix deps.audit and mix hex.audit against your dependency tree. Both answer the same question: is any package in mix.lock known to be vulnerable? Neither can say anything about the runtime those packages execute on.

Erlang/OTP ships security fixes in patch releases, and those fixes never appear in mix.lock. CVE-2025-32433, the unauthenticated remote code execution in Erlang’s SSH daemon, was a 10/10 critical fixed in OTP 27.3.3, 26.2.5.11, and 25.3.2.20. A project pinned to OTP 27.3.2 had a perfectly clean dependency audit the entire time. The runtime was the vulnerable component, and nothing in the standard Elixir tooling was looking at it.

GitHub publishes machine-readable security advisories for erlang/otp. There are 70 of them going back to December 2024: 4 critical, 33 high, 28 medium, 5 low, concentrated in inets, ssh, ssl, and erts. Version 2.12.0 matches your installed OTP version against that data on every scan.


Finding the real OTP version

System.otp_release() returns the major version only, "28". That is not enough to match against an advisory fixed in 28.5.0.7. The full version is in the OTP_VERSION file, so that is what the agent reads:

$ cat $(elixir -e 'IO.puts(:code.root_dir())')/releases/28/OTP_VERSION
28.5

Four-segment versions like 27.3.4.18 are handled, as is the ** suffix that marks an installation patched with otp_patch_apply. In that case the finding carries a note that the reported version may be inaccurate.


The advisory data, and keeping it current

A snapshot of the advisory data ships inside the hex package as priv/otp_advisories.json. This is the same approach mix_audit takes with its own database, and it is what lets the check work with no API key and no network. Results are deterministic: the same agent version on the same OTP version produces the same findings, which is what you want from a CI gate.

The obvious cost of a bundled database is that it ages. Two things address that. First, when an API key is present, the agent asks paraxial.io for its advisory cache. That request is fired before any scanning starts and is read after the scanners finish, so in the normal case it has already completed and adds nothing to scan time. It is capped at one second, and every failure path, including a slow network, an error response, or a server cache that is colder than the bundled snapshot, falls back to the data in the package. Stale advisories beat no advisory check, so the check never fails the scan.

Second, when the bundled data is what got used and it is more than 90 days old, the scan says so rather than quietly reporting you as clean against a 2024 database:

[Paraxial] Erlang/OTP advisory data is 94 days old, upgrade the paraxial dep or
check connectivity to paraxial.io for current advisories

Paraxial.io is the only security platform built specifically for Elixir and Phoenix, helping developers and agents ship secure applications. Contact us today to get started.

Subscribe to stay up to date on new posts.