Changelog
View Sourcev0.17.0
Enhancements
DOS.ListToAtomnow also reports:erlang.list_to_atom/1, including piped calls, matching howDOS.BinToAtomcovers:erlang.binary_to_atom.- Add
--onlyto run a chosen set of checks or categories, e.g.mix sobelow --only XSS.Raw,SQL. It takes the same names as--ignore, which still applies on top. Unknown names fail the run instead of scanning nothing..sobelow-confacceptsonly:and--save-configrecords it. - Add
DOS.DecodeAtomsfor dynamic input decoded with atom-creating options in Jason, Poison, and YamlElixir string readers.
Bug fixes
XSS.SendResprecognizes literal attachment headers on the same response connection for PDF and XML downloads. Uncertain or overridden headers, HTML, and SVG continue to report. Header state no longer leaks between conditional branches; this can expose previously missed findings. Existing finding locations and skip fingerprints remain unchanged. (#55)- Checks now retain tainted variables in interpolated string sigils and piped capture arguments such as &1. These patterns can produce new findings; existing call locations and skip fingerprints are unchanged.
- Unrelated application-level config no longer causes Config.HTTPS to report a secure endpoint as missing HTTPS. Explicit app-level transport settings and separate endpoint checks remain supported. (#56)
- Lockfile dependency advisories now read lockfiles in the format Mix
writes. Mix's
"plug": {...}keys parse as atoms, so the lookup never matched and every advisory check passed silently wheneverdeps/was absent. Parsing the lockfile also no longer prints a "found quoted keyword" warning per dependency. CI.OS,Misc.BinToTermandDOS.BinToAtomnow detect calls made through explicit Erlang imports, such asimport :os, only: [cmd: 1]followed bycmd(input).
v0.16.0
- Bug fixes
XSS.Rawno longer reports calls to a benign localrawhelper with the matching arity, including defaults, guards, pipes, captures, and inline HEEx. Local definitions stay within their module; qualified Phoenix calls and implicitly imported template helpers retain detection. Helpers returning dynamic{:safe, value}output or wrapping anotherrawcall retain their caller's original findings, locations, and fingerprints. (#44)XSS.SendRespnow recognizesput_resp_header(conn, "content-type", type)on the response connection, including piped, aliased, nested, and assigned calls. HTML, SVG, malformed, and unknown types still report; other XML and PDF document types retain low-confidence findings. Discarded, later, unrelated, locally shadowed, or ambiguously imported setters cannot suppress findings. Known unrelated response headers retain the connection's content type, and MIME parameters do not change its classification. (#45)XSS.Rawnow respects explicit imports of unrelatedrawhelpers. Unknown raw macros and delegates retain detection. Older inline lexical contexts without local-signature metadata remain supported.- Invalid project roots, roots with no scannable source files, invalid scan options, and unwritable output files now fail with actionable errors.
- Repeated scans in the same VM now start with fresh findings, template, and skip state. Malformed sources and templates are skipped with a warning in non-strict mode, and unreadable files are skipped with a warning.
- Dynamic socket options, literal statements in router pipelines, and access on a literal keyword list no longer abort scans. Unknown socket options produce low-confidence findings.
XSS.SendRespnow follows the connection passed to each response and its content type before that sink. Later or discarded setters cannot suppress an earlier finding, and rebindings in branches, patterns, callbacks, generators, and call arguments cannot borrow another connection's content type. Unchanged bindings, pins, guards, and explicit setters retain their existing handling.- HTTPS and HSTS checks now use effective settings for the scanned application and each endpoint, including ordered overrides and nested keyword merges. One endpoint cannot satisfy another's settings. Dynamic and conditional settings produce low-confidence findings. Empty CSP policies are reported.
- Enabled sockets now inherit endpoint origin settings from base, production,
and runtime configuration, including
socket/2andwebsocket: true. Explicit socket overrides retain precedence, and disabled WebSockets remain excluded. Defaults are isolated to each endpoint module. Origin allowlists and:connare recognized; an enabled CSRF check lowers confidence when origin checks are disabled. - HEEx comments and script/style text no longer change brace-interpolation
scope or introduce findings from literal markup. The
phx-no-curly-interpolationdirective is recognized as an attribute name; the same text inside another attribute's value cannot suppress findings. Inline columns account for sigil prefixes and heredoc indentation. - Module-local
useandimportdeclarations now apply only to their own module. Named captures and inline HEEx retain lexical aliases and import selections, including renamed aliases and imported arity restrictions. - Lockfile dependency advisories now require the Hex package name to match the checked dependency, preventing false advisories for package aliases. Missing or nonliteral versions no longer abort scans.
- Explicit router paths now resolve relative to
--root. SARIF locations are relative to the scan root, or use file URIs for external files. Reserved filename characters, including#,?,%, and:, are percent-encoded. JSON filenames and skip fingerprints are unchanged. - SARIF's
Vuln.CookieRCErule name now matches its existing result type and rule ID. Unknown finding types receive a null rule ID instead of raising.Config.CSRFRouteis now listed inmix help sobelow. - Optional version checks now verify TLS certificates and hostnames when a trusted CA store is available, and tolerate malformed responses, cache, filesystem, and network failures. OTP 24 skips the notification because it has no built-in CA-store API.
- Enhancements
- Added a
githuboutput format for GitHub Actions workflow annotations, with confidence levels and repository-relative finding locations. - Added detection of raw output in HEEx files and inline
~Hsigils, including body and attribute expressions, legacy EEx, qualified and pipedPhoenix.HTML.rawcalls, nested sigils, and Elixir comments. Controller renders are correlated with embedded*_htmltemplates. Brace interpolation is disabled in script/style bodies and regions marked withphx-no-curly-interpolation. Invalid expressions warn and are skipped, or exit 2 under--strict. - Added static resolution of renamed and grouped aliases, nested module
inheritance, function-local imports, and
only/exceptarity selections. Confidence grading follows direct local aliases before a sink. Calls retain their original ASTs, and implicit Phoenix controller imports retain their historical detection. - The existing fixed set of
Vuln.*dependency advisories can now read literal Hex versions frommix.lockwhendeps/is unavailable, without evaluating project code. - Added
--include-mix-tasksand--include-scriptsto opt into additional source paths, and--summaryto print file counts on stderr. SARIF now includes invocation warnings and uses the published OASIS schema URL. .sobelow-confsaves and sorted skip rewrites are now atomic, preserve Unix permissions, and follow symlinks. Unreadable existing skip files are preserved and produce an actionable error.--legacy-skipsretains the existing append behavior.- Improved scan speed with shared function call, pipe, parameter, and confidence analysis; incremental HEEx delimiter parsing; combined file/module metadata extraction; bounded, ordered file preparation; and smaller lexical contexts with compatible lookup paths on older OTP.
- Batched finding and fingerprint updates, retaining each function source once per batch. Quiet output and exit status use confidence counts, and repeated reports reuse sorted results. Scan settings, enabled checks, paths, skip lookups, and relevant template snapshots are reused, with per-worker cache counters to avoid contention. Source, AST, lockfile, and dependency-version caches are released when the scan finishes or fails. Existing public finding-log results and output formats are preserved.
- Added a
- Testing
- Expanded regression coverage for output and verbose highlighting, advisory boundaries, configuration uncertainty, atomic writes, source edge cases, and scan/lexical context restoration. Added failing regressions and safe controls for Copilot and adversarial review findings.
- Added release 0.15.0 fixtures for JSON/SARIF output, rule IDs, fingerprints, and both historical skip formats. Exact fingerprint assertions run on the captured parser family; output/rule contracts and generated skip round trips run across the supported matrix. Independent release column captures qualify older parsers, and highlighting follows each runtime's printer.
- Added compatibility coverage for all 70 pre-extraction public function and
arity pairs on
SobelowandSobelow.Parse, including traversal callbacks and default arities. - Expanded benchmarks to seven workloads and quiet/JSON/SARIF output, with
complete finding and output comparisons, reduction counts, and separate
sampled VM memory. Methodology, measurements, and limits are documented in
bench/README.md; the benchmark entry point isbench/scan.exs. - Raised line coverage from 85.2% to 98.5%, with a 98% gate on the newest CI runtime. Coverage excludes only the test harness, documentation-only legacy module, and developer diff helper. Fixtures are ignored by test discovery.
- Isolated named processes and application configuration in legacy tests to prevent order-dependent failures on older Elixir versions. Version-check cache, response, and TLS-option tests make no network requests.
- CI builds and smoke-tests the escript on every supported Elixir/OTP combination, including exit codes; strict Credo runs on every entry.
- Misc
- Split scan discovery, execution, skip-file persistence, and version checks
into focused internal modules. Shared AST helpers now live in source,
metadata, calls, variables, and template modules, with the existing
SobelowandSobelow.Parseentry points preserved. - Consolidated duplicate matchers and worker setup, reused relative-path handling, and shortened stale comments. Updated contributor and usage guidance to document the new module boundaries and scan options.
- Split scan discovery, execution, skip-file persistence, and version checks
into focused internal modules. Shared AST helpers now live in source,
metadata, calls, variables, and template modules, with the existing
Upgrade notes
- Newly supported unsafe patterns can produce additional findings. HEEx, inline templates, lexical resolution, response rebindings, and configuration fixes can reveal previously missed sinks. Corrected lockfile package matching can remove false dependency advisories.
- Invalid invocations and failed output writes now fail explicitly. Check project roots, scan options, and output permissions if an invocation previously exited successfully without scanning or writing its report.
- Existing check names, finding types, CLI flags, JSON finding fields, original
ASTs, existing finding locations, and current/legacy fingerprint calculations
are preserved. SARIF artifact paths are corrected as described above. The
minimum supported Elixir version remains
~> 1.12.
v0.15.0
- Bug fixes
Config.Secretsno longer crashes the scan when a secret is written as anything other than a plain double-quoted string. Heredoc values and values containing escaped quotes previously raised aMatchErrorand aborted the entire run. These secrets are now reported, using the line of the enclosingconfigcall.- A corrupt or unreadable version-check cache file no longer aborts the scan. Sobelow previously printed "This does not appear to be a Phoenix application" and exited 0 — a CI gate could pass having scanned nothing.
--strictnow reports syntax errors instead of raising. It has been broken since Elixir 1.13 changed the error shape returned byCode.string_to_quoted/2. Errors are now reported asfile:line:column:.- A template that cannot be parsed is now skipped (or reported under
--strict) rather than aborting the scan with anEEx.SyntaxError. The error now names the offending template instead ofnofile. - A malformed
.sobelow-confnow produces an actionable message instead of a rawMatchErrorstacktrace. This mattered more since v0.14.1 began reading the file automatically. - An empty, whitespace-only, or comment-only
.sobelow-confis now read as no options rather than aborting the scan. Such a file parses to an empty block instead of a keyword list, so it originally crashed with aFunctionClauseErrorand then, once that was fixed, exited 1 with a configuration error. Since the file is read automatically, a straytouch .sobelow-confor a truncated write was enough to break every scan in a project. Contents that cannot be interpreted are still an error. --save-confignow storesignore_filesrelative to the project root. Absolute paths were previously baked into.sobelow-conf, breaking the committed file on every other machine and in CI.Config.Secretsnow reports the line of the secret itself when aconfigcall spans multiple lines. The line search compared a tuple against an integer, so it never worked as intended.- An unwritable
~/.sobelowno longer fails a scan. - Fixed a string-interpolation typo that rendered dot-access variables as
conn.${atom_to_string(field)}. .sobelow-confkeys are now genuinely sorted alphabetically.- A
.sobelow-confcan no longer stop Sobelow from scanning.--save-configwroteversioninto every file it generated, somix sobelow --version --save-configproduced a committed file that made every later run print the version and exit 0 — a CI gate reading that as a clean scan.version,details,all-details,save-config, anddiffchoose what Sobelow does rather than configure a scan, and are now accepted on the command line only. One in the file is ignored, with a warning when it would have changed anything.versionis no longer written to the file in the first place. # sobelow_skipcomments are no longer thrown away over whitespace. The pattern demanded exactly one space after the#and exactly one before the list, so# sobelow_skip["XSS.Raw"],# sobelow_skip ["XSS.Raw"], and# sobelow_skip [ "XSS.Raw" ]were all ignored — silently, and indistinguishably from a skip that had simply not applied. Spacing around the marker, inside the list, and around commas is now irrelevant.SQL.Queryno longer reports a project's ownquery/1as SQL injection. An unqualifiedquery/query!call was matched regardless of what it referred to, so every call to a local function that happened to carry one of those very ordinary names produced a finding. The unqualified form is now only considered in a file that hasimport Ecto.Adapters.SQLoruse Ecto.Repo— the two ways the bare name can actually reach Ecto. Qualified calls, such asRepo.query/1andEcto.Adapters.SQL.query/3, are unaffected.
- Enhancements
- Added
--no-router, for scanning a project that has no Phoenix router. Sobelow warned that it could not find one and offered no way to silence it, which was noise for plain Elixir libraries. It is shorthand for--router :none, which can also be set in.sobelow-confasrouter: :none. The router-dependent checks are skipped either way. .sobelow-skipsis now written in sorted order, so regenerating it after fixing or adding a finding produces a small diff instead of reshuffling the file. Entries sort by type, file, and line number — numerically, so line 10 follows line 9 rather than line 1. The whole file is sorted, not just the newly added entries, so the ordering holds however many times it is regenerated. Comments and pre-v0.14 bare-fingerprint lines are preserved. Pass--legacy-skipsfor the previous append-only behaviour, which never rewrites lines it did not add.# sobelow_skipcomments now work on Phoenix router pipelines, not just functions. This makesConfig.CSRF,Config.Headers, andConfig.CSPsuppressible per pipeline instead of only via--mark-skip-all, so an API pipeline that legitimately has no:protect_from_forgerycan be annotated in place. Listing the parentConfigmodule skips every Config check on that pipeline. As with function-level skips, this only takes effect under--skip.- A
# sobelow_skipcomment that cannot be read now warns on stderr, naming the file and line, instead of being dropped without a word. Single quotes and a list broken across several comment lines are still not accepted, but they now say so rather than leaving you to wonder why the finding came back. --privatenow skips the version check entirely rather than still writing the cache file. It makes no network requests and touches no files outside the scanned project.SOBELOW_HOMEis now documented, and is treated as the directory holding the version-check cache.- Added
usage-rules.md, following theusage_rulesconvention, so projects using AI coding assistants can pull Sobelow's guidance into their agent's context withmix usage_rules.sync. It is shipped in the Hex package. - Added
AGENTS.mddocumenting the checker-module contract for contributors. - Added support for Elixir v1.20.x.
- Added
- Testing
- Added an end-to-end test harness (
Sobelow.ScanCase) that runs full scans against fixture applications undertest/fixtures/apps, plus regression coverage for every bug above. Line coverage went from 29% to 67%. - Added coverage for CLI option parsing,
.sobelow-confprecedence,--exitand--thresholdmapping, and thejson/sarif/quiet/txtrenderers. - Added end-to-end coverage for pipeline-level
# sobelow_skipcomments, and unit coverage for how skips associate with pipelines in the AST. Sobelow.ScanCase.temp_fixture_file/3now restores a committed fixture's original contents instead of deleting the file, so a test can vary a checked-in fixture without destroying it.
- Added an end-to-end test harness (
- Misc
- Replaced the deprecated
:preferred_cli_envproject key withdef cli. - Bumped
credoto~> 1.7.19; 1.7.12 crashed on Elixir 1.20. - Removed a dead Elixir 1.5 version guard and fixed an always-true conditional in the SARIF renderer.
- Replaced the deprecated
Upgrade notes
Config.Secretsline numbers may change forconfigcalls that span multiple lines, and for files where the same secret value appears more than once. Finding fingerprints include the line number, so any affected.sobelow-skipsentries will stop matching and those findings will resurface. Re-runmix sobelow --mark-skip-allif you rely on a committed skip file.- Secrets that previously crashed the scan are now reported. If a heredoc or escaped-quote secret exists in your config, you will see new findings where the scan previously failed outright.
SOBELOW_HOMEsemantics changed from "path to the cache file" to "directory holding the cache file". The previous behaviour raised aMatchErrorfor the natural usage, so this is unlikely to affect anyone.
v0.14.1
- Enhancements
- Implicitly use
.sobelow-confif detected in the root directory rather than require--configswitch. The--no-configswitch is still supported to prevent any settings from being read in from the file if needed. - Added guidance for
warn_if_outdatedoption in mix deps - Added support for Elixir v1.19.x
- Implicitly use
- Bug fixes
- Handled extra config options for app releases in mix.exs
- Properly handle the use of CLI switches and config file settings in the same run. These would previously clobber each other in unapparent ways leading to confusing behavior. CLI switch take precedence.
.sobelow-confnow sorted alphabetically- Fix edwarning from zero argument functions
- Fixed broken skip funcationality
- Fixed broken GitHub Actions CI
- Misc
- Typo fix
v0.14.0
- Removed
- Support for minimum Elixir versions 1.7 - 1.11 (POTENTIALLY BREAKING - only applies if you relied on Elixir 1.7 through 1.11, 1.12+ is still supported)
- Enhancements
- Added support for multiple variations of
SQL.query() - Added support for
System.shell' command introduced in Elixir v1.12 * Ignore runtime config duringConfig.HSTS* Updated developer dependencies (exdoc&credo) * Bug fixes * Fixedis_endpoint?error in main * Fixed findings normalization bug * Fixed truncation error * Misc * GitHub Actions test matrix updated (hence the large drop in support for old Elixir versions) * Addressed compiler warnings from Elixir v1.18.x * Moved frommasterbranch tomain## v0.13.0 * Removed * Support for minimum Elixir versions 1.5 & 1.6 (**POTENTIALLY BREAKING** - only applies if you relied on Elixir 1.5 or 1.6, 1.7+ is still supported) * Enhancements * Fixed allcredowarnings * Implemented allcredo` "Code Readability" adjustments * Took advantage of _somecredorefactoring opportunities Added (sub)module documentation that was missing for some vulnerabilities and unified presentation of others Bug fixes Fixed--details/-dnot displaying correct information Fixed incompatibility issue with Elixir 1.15 Misc Addedmix credo --strictto project Improvements to GitHub CI Hex Audit Compiler Warnings as Errors Checks Formatting Added helpermix test.allalias ## v0.12.2 Bug fixes Removed:castoreand introduced:verify_noneto quiet warning and unblock escript usage, see #133 for more context on why this is necessary ## v0.12.1 Bug fixes Lowered required version of:castoreto remove upgrade path issues Reconfigured:verify_peerto actually use CAStore and remove warning ## v0.12.0 Removed Support for minimum Elixir version 1.4 (POTENTIALLY BREAKING - only applies if you relied on Elixir 1.4, 1.5+ is still supported) Enhancements Adds support for HEEx to XSS.Raw Adds--versionCLI flag README Improvements Umbrella App usage Clearer installation process Layout changes Updated dependencies Bug fixes Adds to_string() to exit_on Sets SSL opt verify_peer in version check Reworks-v, --verboseprinting to not use the now deprecatedMacro.to_string/2Misc Allows atom values for threshold in config file Uses SPDX ID for licenses in mixfile Fixed typo ## v0.11.2 Enhancements Simplify--flycheckoutput to align with expected format ## v0.11.1 Enhancements Sarif output with--outflag--strictflag, which throws compilation errors instead of suppressing them. ## v0.11.0 Enhancements Sarif output for GitHub integration--flycheckflag, which reverses output of--compactBug fixes Non-compiling files now return an empty syntax tree instead of causing Sobelow errors. Command Injection finding description are properly formatted Misc If you use Sobelow as a standalone utility (i.e. not as part of a Phoenix application), you now need to install as an escript withmix escript.install hex sobelow. Custom JSON serialization replaced with Jason. ## v0.10.6 Bug fixes Handle nilconfigcase ## v0.10.5 Misc Update code to clean up deprecation warnings ## v0.10.4 Enhancements Sobelow is now smarter about cross-site websocket hijacking Update URL for CSRF description ## v0.10.3 Bug fixes Fix directory structure issue in umbrella applications Handle function capture edge cases ## v0.10.2 Bug fixes Fix a format error in JSON output encoding ## v0.10.1 Bug fixes Sobelow will use ".sobelow-skips" instead of ".sobelow" in your root directory for--mark-skip-all## v0.10.0 Enhancements Sobelow now uses "~/.sobelow/sobelow-vsn-check" for update checks The ".sobelow" file in your project root is for--mark-skip-allonly ## v0.9.3 Enhancements Improved checks for all aliased functions Bug Fixes JSON output for Raw findings is now properly normalizedsend_downloadcorrectly flags aliased function callssend_downloadnow correctly flags piped functions ## v0.9.2 Bug Fixes Fix error that resulted from redefining imported functions ## v0.9.1 Bug Fixes Revert umbrella app recursion ## v0.9.0 Enhancements Add--mark-skip-alland--clear-skipflags New CSRF via action reuse checks Sobelow can now be run in umbrella apps Bug Fixes Fix an error when printing some kinds of variables ## v0.8.0 Enhancements Improve output consistency All JSON findings containtype,file, andlinekeys "Line" output now refers directly to the vulnerable line Default output headers have been normalized Note: If you depend on the structure of the output, this may be a breaking change. More information can be found at https://sobelow.io. ## v0.7.8 Enhancements Add--thresholdflag Add module names to finding output Deprecations File/Path check has been deprecated Bug Fixes Fix inaccurate CSRF details ## v0.7.7 Enhancements Add check for insecure websocket settings Bug Fixes Accept module attributes for application name ## v0.7.6 Bug Fixes Fix issue that suppressed output options when config files were in use ## v0.7.5 Misc Sobelow will now only halt when--exitflag is used ## v0.7.4 Bug Fixes Log hardcoded secrets for txt output ## v0.7.3 Misc Tweaks to--outflag. ## v0.7.2 Enhancements Add router path to config findings Add--outflag for writing to file ## v0.7.1 Enhancements Improved handling of JSON format Additional checks for File functions ## v0.7.0 Enhancements Improved handling of vulnerabilities within templates. Bug Fixes Sobelow no longer incorrectly flags :binarysend_downloadfunctions. ## v0.6.9 Enhancements Improve template parsing and validation. Support multiple routers, and improve route discovery. Misc. Update language for missing directory. ## v0.6.8 Bug Fixes Fix bug in the handling of certain piped functions. Revert not/in update that broke Elixir 1.4 compatibility. ## v0.6.7 Enhancements Remove banner print from JSON format. Bug Fixes Fix error that occurred with certain function names in JSON format. ## v0.6.6 Enhancements Add check for directory traversal viasend_downloadAdd check for missing Content-Security-Policy Check additional XSS vectors ## v0.6.5 Bug Fixes Allow RCE module to be appropriately ignored. ## v0.6.4 Enhancements Set timeout for version check. ## v0.6.3 Enhancements Add RCE module to check for code execution viaCodeandEEx. Deprecations * The--with-codeflag has been changed to--verbose. The--with-codeflag will continue to work as expected until v1.0.0, but will print a warning message.
- Added support for multiple variations of