Packages
mob_dev
0.5.4
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.37
0.3.35
0.3.34
0.3.33
0.3.28
0.3.26
0.3.23
0.3.21
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.18
0.2.17
0.2.15
0.2.14
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
Development tooling for the Mob mobile framework
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/mob.security_scan.ex
defmodule Mix.Tasks.Mob.SecurityScan do
@shortdoc "Comprehensive security scan of a Mob app's dependencies, bundled runtime, and source"
@moduledoc """
Audits the project for known vulnerabilities and unsafe code across
every surface a Mob app actually ships:
* Hex dependency CVEs (`mix_audit`, `osv-scanner` over `mix.lock`)
* Android Gradle dependency CVEs (`osv-scanner`)
* iOS Swift Package dependency CVEs (`osv-scanner`)
* Bundled-runtime CVEs — OpenSSL/SQLite/OTP/Elixir baked into
Mob's pre-built OTP tarballs (manifest + fingerprint verification +
OpenSSL/SQLite/Erlef advisory feeds)
* C source static analysis (semgrep, flawfinder)
* Kotlin static analysis (detekt)
* Swift static analysis (`xcodebuild analyze`)
Layers run sequentially. A missing external scanner is a soft warning,
not a failure — the layer reports `tool missing` and the rest of the
scan continues.
## Usage
mix mob.security_scan # full scan, pretty terminal output
mix mob.security_scan --json # machine-readable JSON to stdout
mix mob.security_scan --skip hex,gradle # skip named layers
mix mob.security_scan --strict # exit 1 if any high+ finding
mix mob.security_scan --write-report PATH # also write a markdown report
## External tools
Recommended one-time install on macOS:
brew install osv-scanner semgrep flawfinder detekt
Each layer prints which tool produced its findings so the report
is fully sourced.
## Why "security_scan" not "audit"
`mix mob.audit_otp` already exists and does something else — it
reports which OTP libs your bundled app doesn't use so they can be
stripped to shrink the binary. That's a *binary-size* audit. This
task is the *security* counterpart, deliberately named differently.
"""
use Mix.Task
alias MobDev.SecurityScan
alias MobDev.SecurityScan.{Formatter, Report}
@switches [
json: :boolean,
strict: :boolean,
skip: :string,
write_report: :string,
project_root: :string
]
@impl Mix.Task
def run(args) do
{opts, _, _} = OptionParser.parse(args, strict: @switches)
skip = parse_skip(opts[:skip])
project_root = opts[:project_root] || File.cwd!()
run_opts = [
project_root: project_root,
skip: skip,
on_layer_start: &on_layer_start(&1, opts),
on_layer_done: &on_layer_done(&1, opts)
]
report = SecurityScan.run(run_opts)
cond do
opts[:json] ->
IO.puts(Formatter.json(report))
true ->
IO.write(Formatter.terminal(report))
end
if path = opts[:write_report] do
File.write!(path, Formatter.markdown(report))
Mix.shell().info("wrote markdown report to #{path}")
end
Report.maybe_exit_strict(report, opts[:strict])
end
defp parse_skip(nil), do: []
defp parse_skip(value) do
value
|> String.split(",", trim: true)
|> Enum.map(&(&1 |> String.trim() |> String.to_atom()))
end
defp on_layer_start(_name, opts) do
if opts[:json], do: :ok, else: :ok
end
defp on_layer_done(_layer, opts) do
if opts[:json], do: :ok, else: :ok
end
end