Scala gives you an expressive type system, immutability by default, and functional patterns that make a lot of bugs unrepresentable. None of that changes the security surface underneath, because Scala compiles to JVM bytecode and lives in the same Maven ecosystem as Java. A Scala service inherits Log4Shell, the jackson-databind deserialization gadget chains, and every other JVM supply-chain CVE, and the Scala-native ecosystem has shipped serious advisories of its own. Apache Spark — written in Scala — carried CVE-2022-33891, a command-injection flaw in its UI that reached CISA's Known Exploited Vulnerabilities catalog; Akka HTTP shipped CVE-2021-42697, a remotely triggerable denial of service; and any Scala app that parses JSON through jackson-module-scala pulls in jackson-databind, where CVE-2019-12384 turned polymorphic deserialization into remote code execution. sbt resolves a deep transitive tree behind all of it. This guide covers the Scala-specific hazards and the build hygiene that contains them.
How does deserialization become RCE in a Scala app?
Through JSON libraries that enable polymorphic typing, and through any surviving use of Java serialization. jackson-module-scala sits on top of jackson-databind, and CVE-2019-12384 showed how default typing plus a gadget on the classpath (an H2 driver, a logback source) escalates an untrusted JSON payload to code execution. Keep default typing off and constrain subtypes explicitly:
// SAFE -- explicit allowlist of deserializable subtypes
val ptv = BasicPolymorphicTypeValidator.builder()
.allowIfSubType(classOf[MyTrustedBase])
.build()
val mapper = JsonMapper.builder()
.polymorphicTypeValidator(ptv) // never activateDefaultTyping without this
.build() :: ClassTagExtensions
Avoid Java's ObjectInputStream entirely for untrusted data, and be deliberate about Spark closures and any readObject path, which drag the full JVM gadget-chain risk into otherwise-functional code.
What are the Scala-native framework CVEs to watch?
Two stand out. CVE-2022-33891 in Apache Spark: when spark.acls.enable is on, a code path in the UI's security filter built a Unix shell command from a caller-supplied doAs user name and executed it, giving arbitrary command execution as the Spark process user. Patch to Spark 3.1.3, 3.2.2, or 3.3.0, or disable ACLs if you do not need them. CVE-2021-42697 in Akka HTTP: a User-Agent header with deeply nested comments could exhaust the parser's stack and crash the server. Upgrade to Akka HTTP 10.2.7 or later (the same fix line applies to the Pekko fork), which caps comment-parsing depth. Both are reachable from a stock deployment, not exotic configurations.
Does Scala protect me from SQL injection?
Only when you use the library the intended way. Slick and Doobie both offer string interpolators that parameterize automatically — and both let you drop to raw strings that do not.
// DANGEROUS -- string built from user input reaches SQL directly
sql"""SELECT * FROM users WHERE name = '#{name}'""" // #$ / #{} splice = raw
// SAFE -- $name binds a parameter, not raw text
sql"""SELECT * FROM users WHERE name = $name"""
In Doobie and Slick, the $value form binds a parameter while the splice forms interpolate raw text into the query. Treat every splice into a SQL fragment the way you would treat string concatenation in Java: as an injection point that needs an allowlist.
How do I secure the sbt supply chain?
By pinning and auditing. sbt resolves dependencies through Coursier, and a vulnerable transitive artifact is exploitable regardless of how clean your Scala is. Pin exact versions in build.sbt rather than using dynamic latest.release revisions, commit the resulting resolution, and run a dependency-vulnerability check in CI as a failing gate. Because Scala shares Maven Central with Java, the same Log4Shell and jackson-databind advisories apply — keep both patched, and never let a build float onto whatever version a repository happens to return.
Scala security checklist
| Practice | Why it matters |
|---|---|
Keep Jackson default typing off; use a PolymorphicTypeValidator | Blocks jackson-databind RCE (CVE-2019-12384) |
Avoid ObjectInputStream on untrusted data | Prevents JVM gadget-chain deserialization |
| Patch Spark; disable UI ACLs if unused | Closes command injection (CVE-2022-33891) |
| Upgrade Akka/Pekko HTTP past the fix line | Stops header-parsing DoS (CVE-2021-42697) |
| Use interpolator bind forms, not raw splices | Eliminates SQL injection in Slick/Doobie |
Pin exact versions in build.sbt; audit in CI | Contains the transitive supply chain |
How Safeguard Helps
Safeguard's software composition analysis resolves the full Coursier/sbt graph behind a Scala service — the transitive jackson-databind, Log4j, and Akka artifacts you never named directly — and applies call-path reachability so a CVE your code actually reaches is prioritized over one that never executes in your deployment. Griffin, our AI analysis engine, explains why a flagged JVM finding is or is not exploitable in plain language, cutting the triage back-and-forth. When a safe upgrade exists, auto-fix opens a tested pull request with the version bump applied. If you are weighing options, the Safeguard vs Snyk comparison covers where the reachability model differs. The functional discipline stays yours; Safeguard secures the JVM supply chain beneath it.
Bring your Scala supply chain under control — start for free or read the documentation.
Frequently Asked Questions
Is Scala safer than Java?
Scala's type system and immutability prevent many logic and concurrency bugs, but it compiles to the same JVM bytecode and depends on the same Maven artifacts, so it inherits every JVM supply-chain and deserialization CVE Java has. Security-wise the two share an attack surface; Scala's benefits are in expressiveness and correctness, not in a smaller vulnerability footprint.
How do I deserialize JSON safely in Scala?
If you use jackson-module-scala, keep default typing disabled and configure a PolymorphicTypeValidator that allowlists only the subtypes you expect, which is the control the CVE-2019-12384 fix introduced. Prefer libraries like circe that do not rely on runtime polymorphic typing at all, and never route untrusted bytes through Java's ObjectInputStream.
Is my Spark cluster affected by CVE-2022-33891?
If you run an affected Spark version (3.0.3 and earlier, 3.1.1 through 3.1.2, or 3.2.0 through 3.2.1) with spark.acls.enable set to true, a request can inject shell commands through the doAs user name. Upgrade to 3.1.3, 3.2.2, or 3.3.0 or later; if you do not use UI ACLs, disabling them removes the vulnerable code path.
Does sbt lock dependency versions automatically?
Not unless you make it. sbt through Coursier will resolve dynamic revisions to whatever is newest, which is a moving target. Pin exact versions in build.sbt, capture the resolved set, and run a vulnerability audit in CI so a compromised or newly vulnerable transitive artifact fails the build instead of shipping silently.