Skip to main content
Version: 1.6.0

Gleam projects

SAFE analyses Gleam projects that target the Erlang VM. Gleam compiles to Erlang, so SAFE runs its full set of BEAM security checks over your Gleam codebase and reports every finding against the .gleam file it came from.

Gleam analysis is free during the beta

Gleam support is new in SAFE 1.6.0 and is free while it is in beta. SAFE needs a gleam.toml that names the project and applications the Gleam compiler built. A build tree that mixes Gleam with Erlang or Elixir applications needs a license of its own.


Analysing a project

You need the SAFE CLI on your PATH (Download) and a project built for the Erlang target — SAFE reads compiled BEAM code, so the JavaScript target has nothing for it to read.

gleam build      # produces build/dev/erlang, the directory SAFE reads
safe analyse # run from the directory holding gleam.toml
safe sca # scans manifest.toml
✓ No SAFE license required — analysing this Gleam project under the free beta.
✓ SAFE license is valid until 2027-03-31.
Info: 6 months, 24 days left in the Gleam free beta (ends 2027-03-31).
Always rebuild before analysing

SAFE reads build/dev/erlang, not your .gleam sources. Without a fresh gleam build it analyses the previous build — so put the build immediately before safe analyse in CI:

- run: |
gleam build
safe analyse
safe sca

Full example: Setting up in CI/CD.


What SAFE analyses

gleam build compiles src/, dev/ and test/ into a single ebin, and no build profile leaves the last two out. Only src/ is production code, so that is what SAFE analyses.

To pull an excluded module or a dependency back in, name it in additional_includes (Configuration). Scope is decided per application from the build output, not from the project type, so a Gleam package inside an Erlang or Elixir build tree gets the same treatment while the applications around it are handled as usual. Lines of code, which the fingerprint and the license check use, are counted on the sources you wrote, not on the generated Erlang.

The checks themselves are the same ones SAFE applies to Erlang and Elixir: atom exhaustion, unsafe deserialisation, command and SQL injection, dynamic code evaluation, weak cryptography, and the rest.

Findings anchor to the function, not the call

Gleam's compiler records one source position per function, not per expression, so SAFE reports the function declaration line and attaches the whole function body as the snippet:

src/my_app.gleam
38  pub fn with_name(barnacle: Barnacle(error), name: String) -> Barnacle(error) {
39 Barnacle(..barnacle, name: Some(name |> atom.create))
40 }

The finding reports line 38 with a snippet covering 38–40, though atom.create on line 39 is the actual problem. vulnerable_fun names the Erlang function the compiled code calls, so atom.create/1 is reported as erlang:binary_to_atom/1. Both facts matter when suppressing a finding: the ignore comment goes above the function declaration and names the Erlang function. Full field breakdown: Vulnerability Report.

note

Reading a snippet to find the call, and translating Gleam names into Erlang ones before you can suppress a finding, is more work than it should be. It reflects what the compiled output currently tells us, not where we intend to leave it: Gleam support ships as a beta, and upcoming releases will bring both analysis and UX improvements here. Feedback on what gets in your way is welcome at safe@erlang-solutions.com.


When you need a config file

Running with no config covers the common case. Generate a .safe/config.json with safe setup gleam when you want a report file, SARIF or Checkmarx output; additional_includes; to run SAFE from another directory via --config-path; or to keep analysing after the beta ends.

Commit .safe/config.json; do not commit fingerprint.json. Every flag is on the CLI page, the generated config is documented here, and Gleam failure modes are in Troubleshooting.