MobDev.SecurityScan.Layers.BundledRuntime (mob_dev v0.7.30)

Copy Markdown View Source

Audits the OpenSSL, ERTS, Elixir, exqlite, and SQLite versions baked into Mob's pre-built OTP tarballs and into the project's deps/exqlite/c_src/sqlite3.c.

Why this layer exists

Generic dependency scanners (mix_audit, osv-scanner, Aqua/Snyk/Trivy) all assume dynamic linking — they look at lockfiles and don't see versions baked into static archives. Mob ships libcrypto.a (OpenSSL), libbeam.a (ERTS), the entire SQLite amalgamation, and a frozen Elixir stdlib inside the OTP tarball that gets copied into every app binary. Nothing in your mix.lock reveals these versions.

This layer looks inside.

What it does

  1. Fingerprint — locate cached tarballs at otp-*-{hash}/ in the runtime cache ($MOB_CACHE_DIR, default ~/.mob/cache), extract real versions of OpenSSL, ERTS, Elixir, and the bundled exqlite BEAMs.

  2. Drift detection — compare fingerprints against the BundledVersions manifest. Any mismatch is a :high finding — it means the manifest is lying about what shipped, which is the exact failure mode the manifest exists to prevent.

  3. SQLite — read deps/exqlite/c_src/sqlite3.c from the scanned project, extract SQLITE_VERSION, and report every CVE on www.sqlite.org/cves.html whose fixing release is newer (severity from the page, :medium when it gives none; rows SQLite marks "not a bug" are skipped). SQLite isn't in the OTP tarball; it's compiled per-app via exqlite, so this runs with or without cached tarballs.

  4. Upstream advisories — for the tarballs of the active runtime (the manifest's active_hash, i.e. what this mob_dev deploys), match the fingerprinted versions against the Erlang/OTP GitHub security advisories, OpenSSL's per-branch vulnerability page, and endoflife.date's OpenSSL branch EOL dates. Each affected advisory is a finding with the upstream severity; an OpenSSL branch past EOL is :high. See MobDev.SecurityScan.BundledRuntime.Advisories for the matching rules and MobDev.SecurityScan.BundledRuntime.AdvisoryFeed for caching. Offline, a cached copy is used (flagged when stale); with no copy at all the layer warns and lists the advisory pages to check by hand. Elixir remains a manual-check note.

Stale tarballs of other hashes are fingerprinted for drift but not matched against advisories: they are not what ships.

Hex-ecosystem CVEs (including exqlite the BEAM package) are handled by hex_deps, not duplicated here.

Summary

Functions

Whether an exqlite found in a cached OTP dir is the deploy-installed copy of the scanned project's own exqlite.

Functions

deploy_installed_exqlite?(arg1, actual, project_exqlite)

@spec deploy_installed_exqlite?(String.t() | nil, String.t() | nil, String.t() | nil) ::
  boolean()

Whether an exqlite found in a cached OTP dir is the deploy-installed copy of the scanned project's own exqlite.

iOS tarballs don't ship exqlite (manifest nil for that platform); the iOS native build installs the project's exqlite into the cached OTP dir, and the :hex_deps layer audits that version through mix.lock. So the copy is exempt from drift only when the manifest expects none AND its version equals the project's locked exqlite. Anything else in the cache (a project without exqlite, or another project's version) is not covered by :hex_deps, still gets packaged, and stays a drift finding.