Sobelow
Sobelow is a security-focused static analysis tool for Elixir & the Phoenix framework. For security researchers, it is a useful tool for getting a quick view of points-of-interest. For project maintainers, it can be used to prevent the introduction of a number of common vulnerabilities.
Currently Sobelow detects some types of the following security issues:
- Insecure configuration
- Known-vulnerable Dependencies
- Cross-Site Scripting
- SQL injection
- Command injection
- Code execution
- Denial of Service
- Directory traversal
- Unsafe serialization
Potential vulnerabilities are flagged in different colors according to confidence in their insecurity. High confidence is red, medium confidence is yellow, and low confidence is green.
A finding is typically marked "low confidence" if it looks like a function could be used insecurely, but it cannot reliably be determined if the function accepts user-supplied input. i.e. If a finding is marked green, it may be critically insecure, but it will require greater manual validation.
Note: This project is in constant development, and additional vulnerabilities will be flagged as time goes on. If you encounter a bug, or would like to request additional features or security checks, please open an issue!
Table of Contents
- Installation
- Options
- Configuration Files
- False Positives
- Modules
- Usage Rules
- Umbrella Apps
- Development and Coverage
- Updates
Installation
To use Sobelow, you can add it to your application's dependencies.
def deps do
[
{:sobelow, "~> 0.14", only: [:dev, :test], runtime: false, warn_if_outdated: true}
]
end
You can also install Sobelow globally by executing the following from the command line:
$ mix escript.install hex sobelow
To install from the main branch, rather than the latest release,
the following command can be used:
$ mix escript.install github sobelow/sobelow
To Use
After installation, the simplest way to scan a Phoenix project is to run the following from the project root:
$ mix sobelow
Options
Note: Any path arguments should be absolute paths, or relative to the application root.
-
--rootor-r- Specify the application root directory. Accepts a path argument, e.g.../my_project. -
--verboseor-v- Print code snippets and additional finding details. -
--ignoreor-i- Ignore given finding types. Accepts a comma-separated list of module names, e.g.XSS.Raw,Traversal. -
--ignore-files- Ignore files. Accepts a comma-separated list of file names, e.g.config/prod.exs. -
--include-mix-tasks- Also scanlib/mix/tasks/. -
--include-scripts- Also scan.exsunderlib/, and source files underscripts/andpriv/. -
--detailsor-d- Get finding-type details. Accepts a single module name, e.g.Config.CSRF. -
--all-details- Get details of all finding-types. -
--private- Skip update checks. -
--summary- Print discovered, scanned, ignored, unreadable, unparseable, and pending file counts on stderr. JSON findings keep their existing fields. Non-strict scans warn when source or templates cannot be read or parsed; SARIF also includes these warnings as invocation notifications. -
--strict- Exit 2 on a source or template parse failure. -
--router- Specify router location. This only needs to be used if the router location is non-standard. Accepts a path argument, e.g.my/strange/router.ex. -
--no-router- Scan a project that has no Phoenix router, such as a plain Elixir library. Sobelow otherwise warns that it cannot find one. The router-dependent checks (Config.CSRF,Config.CSRFRoute,Config.Headers, andConfig.CSP) are skipped either way, since there is nothing for them to inspect. This is shorthand for--router :none, which can also be set in.sobelow-confasrouter: :none. -
--exit- Return non-zero exit status at or above a confidence threshold oflow,medium, orhigh. Defaults tofalsewhich returns a zero exit status -
--threshold- Return findings at or above a confidence level oflow(default),medium, orhigh. -
--formator-f- Specify findings output format. Acceptstxt,json,sarif,github,quiet,compact, orflycheck.Note that options such as
--verbosewill not work with thejsonformat. Alljsonformatted findings contain atype,file, andlinekey. Other keys may vary.githubemits GitHub Actions workflow annotations. Each finding is a warning with its confidence level in the message. Locations are relative toGITHUB_WORKSPACEwhen set, so scans from a subdirectory attach to the repository's files. Otherwise, locations are relative to the current directory. Findings without a location use line 1, column 1. -
--quiet- Return a single line indicating number of findings. Otherwise, return no output if there are no findings. -
--compact- Minimal, single-line findings with output colorised according to confidence. -
--flycheck- Minimal, single-line findings that are compatible with flycheck-based tooling. -
--save-config- Generates a configuration file based on command line options. See Configuration Files for more information. -
--[no-]config- Run Sobelow with or without configuration file. See Configuration Files for more information. -
--mark-skip-all- Mark all displayed findings as skippable. -
--clear-skip- Clear configuration created by--mark-skip-all. -
--legacy-skips- Append to.sobelow-skipsrather than rewriting it in sorted order. Only needed if you have tooling that relies on the file being append-only. -
--skip- Ignore findings that have been marked for skipping. See False Positives for more information. -
--version- Outputs the current version of Sobelow. This is useful for CI steps or integration with other tools like Salus.
Configuration Files
Sobelow allows users to save frequently used options in a configuration file. For example, if you find yourself constantly running:
$ mix sobelow -i XSS.Raw,Traversal --verbose --exit Low
You can use the --save-config flag to create your .sobelow-conf
config file:
$ mix sobelow -i XSS.Raw,Traversal --verbose --exit Low --save-config
This command will create the .sobelow-conf file at the root
of your application. You can edit this file directly to make
changes.
You can also run the command without any options:
$ mix sobelow --save-config
when you first start out using this package - the generated configuration file will be populated with the default values for each option. (This helps in quickly incorporating this package into a pre-existing codebase.)
The .sobelow-conf file is automatically used if detected. CLI switches will
take precedence over options in the config file. You can also specify
--no-config to prevent any config file settings being used if needed.
False Positives
Sobelow favors over-reporting versus under-reporting. As such,
you may find a number of false positives in a typical scan.
These findings may be individually ignored by adding a
# sobelow_skip comment, along with a list of modules, before
the function definition.
# sobelow_skip ["Traversal"]
def vuln_func(...) do
...
end
The same syntax works for Phoenix router pipelines, which is
useful for configuration findings such as Config.CSRF and
Config.Headers:
# sobelow_skip ["Config.Headers"]
pipeline :browser do
...
end
Listing the parent Config module skips every Config check on
that pipeline, in the same way that -i Config ignores the
whole group.
When integrating Sobelow into a new project, there can be a
large number of false positives. To mark all printed findings
as false positives, run sobelow with the --mark-skip-all flag.
Once you have tagged the appropriate findings, run
Sobelow with the --skip flag.
$ mix sobelow --skip
While # sobelow_skip comments mark function- and pipeline-level
findings, the --mark-skip-all flag can be used to skip any
finding type.
Modules
Findings categories are broken up into modules. These modules
can then be used to either ignore classes of findings (via the
ignore and skip options) or to get vulnerability details (via the
details option).
This list, and other helpful information, can be found on the command line:
$ mix help sobelow
Usage Rules
Sobelow ships a usage-rules.md covering how to interpret
confidence levels, suppress false positives correctly, and wire the tool into CI.
It follows the usage_rules convention, so
projects using an AI coding assistant can pull it into their agent's context:
$ mix usage_rules.sync AGENTS.md sobelow
Umbrella Apps
In order to run Sobelow against all child apps within an umbrella app with a single command, you can add an alias for sobelow in your root mix.exs file:
defp aliases do
[
sobelow: ["cmd mix sobelow"]
]
end
If you wish to use configuration files in an umbrella app, create a .sobelow-conf in each child application.
Development and Coverage
Run the tests and repository checks with:
mix test
MIX_ENV=test mix coveralls.html
mix test.all
MIX_ENV=prod mix escript.build
bash test/escript_smoke.sh
mix coveralls.html runs the full test suite, enforces the 98% line coverage
minimum, and writes cover/excoveralls.html. CI uses this command on its newest
Elixir/OTP entry; other entries run mix test. mix test.all checks formatting,
compiler warnings, dependencies and Credo, and does not run the tests.
Coverage excludes the test harness, the deprecated documentation-only
Misc.FilePath module, and the developer-only --diff command that launches
external Mix processes. Fixture applications remain regression inputs and are
explicitly ignored by test discovery; they and benchmark scripts are not in the
compiled application paths. Modules with no executable lines display as covered
without adding lines to the coverage total.
Production security checks, CLI handling and defensive error paths stay in the coverage scope. VM termination cannot be measured in the parent test process, so the escript smoke tests also verify strict source/template/inline-template errors, failure and clean-scan exit thresholds, malformed config, and skip clearing in separate processes. Older-runtime fallbacks and unreachable defensive branches can leave a small gap on a single runtime; coverage does not imply every branch or supported runtime has been exercised.
Updates
When scanning a project, Sobelow will occasionally check for
updates, and will print an alert if a new version is available.
Sobelow keeps track of the last update-check by writing a
sobelow-vsn-check file to ~/.sobelow.
The location of that directory can be changed with the
SOBELOW_HOME environment variable:
$ SOBELOW_HOME=/var/cache/sobelow mix sobelow
If this functionality is not desired, the --private flag can
be used with the scan. With --private, Sobelow makes no network
requests and does not write the cache file at all.