Skip to main content

Non-upgradable dependencies

Need

Explicitly declare and manage dependencies in the source code to ensure upgradability and avoid potential security vulnerabilities.

Context

  • Usage of Go 1.16 for building high-performance and efficient applications
  • Usage of Gin framework for building web applications in Go

Description

Non compliant code

package main

import (
"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

In the code above, we are importing the Gin web framework to create a simple HTTP server. However, the version of the Gin library is not explicitly declared. This means that the application is using the latest version of the library available at the time of the last build.

This is a vulnerability because if the library is updated with breaking changes, the application may stop working correctly when it's rebuilt. Similarly, if a version of the library is discovered to have a security vulnerability, the application could be at risk if it's not updated to a version of the library where the vulnerability is patched.

The best practice is to use a versioned import, which ensures that the application always uses a specific version of the library. This makes the build reproducible, meaning it will behave the same way no matter when and where it's built.

Another problem here is that the dependencies are copied directly into the repositories. This is not a good practice because it increases the size of the repository and makes it difficult to update the dependencies. It's better to use a package manager which can handle the dependencies in a more efficient way.

Steps

  • Identify all the dependencies used in the project.
  • Explicitly declare the dependencies in a manifest file (e.g., go.mod) with their names and versions.
  • Remove the direct copying of dependencies into the repositories.
  • Use a package manager (e.g., Go Modules) to manage the dependencies.
  • Regularly update the dependencies to their latest versions to ensure security patches and bug fixes.
  • Implement a process to review and approve any changes to the dependencies before updating them.

Compliant code

package main

import (
"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

The above code is a simple Go application using the Gin framework. It sets up a basic HTTP server that responds with a JSON message "pong" when a GET request is made to the "/ping" endpoint.

However, the code has a vulnerability related to its dependency management. The Gin framework is imported directly into the code without specifying a version. This means that the application is using the latest version of Gin available at the time of the last build. If a new version of Gin is released with breaking changes or security vulnerabilities, this could cause problems for the application.

To fix this vulnerability, we need to explicitly declare the version of Gin that the application depends on. This can be done using Go Modules, which is the official dependency management solution for Go.

Here is how you can fix the code:

  1. Initialize Go Modules in your project directory:
go mod init github.com/my/repo

Replace "github.com/my/repo" with the path to your repository.

  1. Add the Gin dependency with a specific version:
go get github.com/gin-gonic/[email protected]

Replace "v1.7.4" with the version of Gin you want to use.

  1. Your go.mod file should now look something like this:
module github.com/my/repo

go 1.16

require github.com/gin-gonic/gin v1.7.4
  1. Update your code to use the version of Gin specified in the go.mod file:
package main

import (
"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

Now, your application will always use the specified version of Gin, regardless of any new releases. This makes your application more stable and secure. Remember to regularly update your dependencies to benefit from security patches and bug fixes.

References