Improper dependency pinning - Scala

Improper dependency pinning - Scala

Need

Proper management of dependency versions and explicit declaration of the entire dependency tree

Context

  • Usage of Scala 2.13.3 for building scalable and high-performance applications
  • Usage of Guice for Dependency Injection in Java
  • Usage of scalatestplus-play for testing Play Framework applications
  • Usage of play-slick for integrating Slick database access library with the Typesafe Play framework

Description

Non compliant code

        name := """scala-play-app"""

version := "1.0"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.13.3"

libraryDependencies ++= Seq(
  guice,
  "org.scalatestplus.play" %% "scalatestplus-play" % "5.0.0" % Test,
  "com.typesafe.play" %% "play-slick" % "5.0.0"
)
        
        

In the build.sbt file above, the application has dependencies on guice, scalatestplus-play, and play-slick. However, it does not explicitly declare the versions of these dependencies or any of their sub-dependencies.

This is a problem because it could lead to unpredictable behavior or security vulnerabilities. If a dependency has a security vulnerability in one version but not another, not specifying the version could lead to the vulnerable version being used.

Additionally, the application could break if a new version of a dependency is released and it is not compatible with the application. By not pinning the exact version of the dependencies, the application is at risk of breaking changes introduced in the new versions of the dependencies.

Also, the application does not list out all the indirect dependencies (dependencies of dependencies). These indirect dependencies could also have vulnerabilities or compatibility issues, and not listing them out makes it harder to manage these risks.

In summary, the application is at risk of security vulnerabilities and compatibility issues due to not properly pinning its dependencies and not explicitly declaring its full dependency tree.

Steps

  • Identify all the direct and indirect dependencies of the application.
  • Determine the specific versions of each dependency that the application requires.
  • Explicitly specify the versions of all dependencies in the build configuration file.
  • Regularly update and review the dependencies to ensure they are up to date and do not have any known vulnerabilities.

Compliant code

        name := """scala-play-app"""

version := "1.0"

lazy val root = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.13.3"

libraryDependencies ++= Seq(
  guice,
  "org.scalatestplus.play" %% "scalatestplus-play" % "5.1.0" % Test,
  "com.typesafe.play" %% "play-slick" % "5.0.0"
)
        
        

The above code is a build configuration file for a Scala application using the Play framework. It specifies the application's dependencies and their versions. However, it was vulnerable due to improper dependency pinning. The application did not make explicit the whole dependency tree it requires (direct and indirect third-party libraries) and their respective version.

To fix this vulnerability, we need to identify all the direct and indirect dependencies of the application and determine the specific versions of each dependency that the application requires. Then, we need to explicitly specify the versions of all dependencies in the build configuration file.

In the fixed code, we have updated the version of "scalatestplus-play" from "5.0.0" to "5.1.0". This is just an example, and the actual versions should be determined based on the application's requirements and the latest stable versions of the dependencies.

It's also important to regularly update and review the dependencies to ensure they are up to date and do not have any known vulnerabilities. This can be done manually or using automated tools.

References