Security in Elm
On the heels of a recent string of supply chain security incidents in the JavaScript ecosystem, I thought it might be timely to look at security within the ecosystem of the Elm programming language.
If you’re not familiar with Elm, you may want to review my post, Using Elm in 2025. As a brief overview that touches on the aspects that affect security, Elm is a language which is focused on web applications and therefore compiles to JavaScript. Elm is, however, very different from JavaScript, noticeably because it is completely pure, disallowing all side effects (i.e. it uses managed effects). In terms of security boundaries, it also includes a package manager for downloading and including 3rd party code from GitHub.
Elm Is Not a Target
While Elm is incredibly influential and loved by many, it is not among the most popular languages or web frameworks. As in the days when you didn’t need an antivirus program if you were on a Mac because Windows was such a bigger (and easier) target, Elm is in a situation where the interest in its security is mostly academic, because there has been no attempt to attack its ecosystem. As we’ll see, that doesn’t mean that security is lax, it just means that there are real security benefits from going with a niche option and avoiding the most popular ecosystems.
All Packages Are Open
When publishing a package to ecosystems like npm or NuGet, the contents of the package do not have to be connected to any public source code, and can also contain extra scripts or other behaviors aside from the code. These attributes have been used in the above-mentioned incidents to allow an attacker to add a malicious payload to a previously trusted package. While there are programs to voluntarily assert the provenance of a package, these have not gained wide enough acceptance to prevent real harm.
In contrast, when adding a new package to the Elm Packages registry, only metadata about the code is uploaded, not the code itself. As an end-user of the package, running elm install just updates the elm.json manifest, and elm make downloads the source code of the associated tag from GitHub (assuming it’s not already cached). What this means is that the package that you use is 1️⃣ only Elm code, not any extra scripts or artifacts, and 2️⃣ always publicly available for review on GitHub.
No Injection Escape Hatches
One of the biggest concerns with a web framework is injection attacks (e.g. cross-site scripting) where an attacker causes a site to render a script which the attacker controls. Elm does not have any mechanism to dangerously embed unparsed HTML, and all parsed HTML (and SVG) is rendered in a way that neuters any potential script injections. All JavaScript interactions must occur outside of Elm (generally through ports or custom elements), so you can be certain that all Elm code (e.g. any 3rd party code from packages) does not have the ability to cause unwanted injections.
Well, Actually
In the course of preparing for this article, I came across an “escape hatch” which was not properly protected against. I brought this to the attention of Jeroen Engels and Simon Lydell and we collaborated on some options, which Simon took ownership of, ultimately creating a series of patches resulting in version 1.0.1 of elm/html and an upcoming release of elm/virtual-dom.
Purity Simplifies Auditing
Another concern when using 3rd party packages is that the code might do something nefarious, like start sending data to an attacker. In JavaScript, you could call a function to combine CSS classes and, unbeknownst to you, it also hits an API with the contents of the user’s authentication token. Elm’s purity significantly reduces these concerns. At the simplest level, a function is pure if it always provides the same outputs given the same inputs. This means that a function cannot access any global state without it being explicitly passed in. It also means that, in order to do something like make an HTTP request, it must be part of the output of the function (i.e. in Elm you don’t make an HTTP request, you create a command for the Elm runtime to make an HTTP request). Because of these properties, when reviewing 3rd party code, you know that any code which does not return a Cmd does not have the ability to do anything that could reach out into the “real world”. This allows auditors of 3rd party code to focus on those functions, giving attackers nowhere to hide.
Conclusion
The Elm programming language and ecosystem have a number of desirable security properties. Its packages are easy to audit, and it automatically prevents XSS injection attacks at multiple levels. For more information about security and Elm, check out the Elm Radio episode on security, as well as Jeroen’s post about a patch to the Virtual DOM library.