Vet is a dependency security scanner for Elixir. It detects supply chain attacks by walking the AST of every dependency in your lock file and flagging patterns that have no legitimate reason to appear in a library.
The Problem
On March 24th, someone compromised the PyPI publishing token for LiteLLM, an open-source AI gateway with 3.4 million daily downloads. They pushed a version with a .pth file — the kind Python executes automatically when the interpreter starts. The payload swept SSH keys, AWS tokens, and Kubernetes secrets, then exfiltrated them. The poisoned package was live for three hours. In that window, hundreds of thousands of systems downloaded it. Mercor, a $10 billion AI startup, lost 4TB of data — candidate records, source code, video interviews.
The entire attack was three lines of work: steal a publishing token, push a package, wait. The ecosystem did the rest.
In Elixir, the same pattern works. A package calls System.get_env("AWS_SECRET_ACCESS_KEY") inside a @before_compile hook and POSTs the result during mix deps.compile. Your application hasn’t started. Your tests haven’t run. The BEAM doesn’t distinguish between your code and your dependency’s code.
What Vet Does
Vet walks the AST of every dependency in your lock file and flags patterns with no legitimate reason to appear in a library:
-
Compile-time system commands (
System.cmd,:os.cmd,Port.open) -
Credential access (environment variables containing
SECRET,KEY,TOKEN,AWS_*) -
Network calls to suspicious endpoints
-
Compile-time hooks (
@before_compile,@after_compile) -
Obfuscated payloads (high-entropy strings, Base64+eval patterns)
-
Atom exhaustion DoS attacks
-
Slopsquatting detection (attackers register names that LLMs commonly hallucinate)
mix vet.check catches these before mix deps.get — no execution, no risk.
Why Elixir is Positioned Well
Elixir has the tools to do this properly. Code.string_to_quoted and Macro.prewalk let you walk dependency code with the same tools the compiler uses. Python can’t do this. That said, AST-level checks are ultimately a compiler concern — the compiler already walks every node, sees macro-expanded code, and can’t be skipped. What the compiler can’t do is check download counts, score dependency depth, or detect slopsquatting. The full solution is both.






















