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 Dart for building cross-platform mobile, web, and desktop applications
  • Usage of Shelf for managing and organizing data in a Node.js application
  • Usage of shelf_io for managing input/output operations in a Shelf application

Description

Non compliant code

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

void main() {
var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests()).addHandler(_echoRequest);

io.serve(handler, 'localhost', 8080);
}

shelf.Response _echoRequest(shelf.Request request) {
return shelf.Response.ok('Request for "${request.url}"');
}

The above code is a simple server application using the "shelf" package in Dart. The vulnerability here is that the "shelf" package is directly imported without any version specification. This is a problem because it means the application could be using an outdated or insecure version of the package. It also makes it difficult to ensure that the application will behave the same way in different environments, because the version of the package could be different in each environment.

The proper way to manage dependencies in Dart is to specify them in the pubspec.yaml file, which is not done here. This file should list the name and version of each dependency, which allows you to control exactly which versions of each package your application uses. This makes your application more secure and predictable, because you can ensure that you're always using a secure version of each package and that your application behaves the same way in every environment.

Steps

  • Identify all the dependencies used in your project.
  • For each dependency, determine the specific version that your project is compatible with.
  • Explicitly declare each dependency and its version in your project's pubspec.yaml file.
  • Remove any direct copies of dependencies from your repositories.
  • Use a package manager like pub to manage your dependencies. This will ensure that the correct versions are used and that they can be updated easily.
  • Regularly check for updates to your dependencies and test your project with these updates to ensure compatibility.
  • Consider using a tool or service that can automatically check for updates to your dependencies and alert you when they are available.

Compliant code

// pubspec.yaml
name: my_project
description: A simple backend application using shelf framework
version: 1.0.0

environment:
sdk: '>=2.12.0 <3.0.0'

dependencies:
shelf: ^1.1.5
shelf_io: ^1.2.0

dev_dependencies:
pedantic: ^1.11.0

// main.dart
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

void main() {
var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests()).addHandler(_echoRequest);

io.serve(handler, 'localhost', 8080);
}

shelf.Response _echoRequest(shelf.Request request) {
return shelf.Response.ok('Request for "${request.url}"');
}

The vulnerability in the code was due to non-upgradable dependencies. The dependencies were not explicitly declared with their versions in the source code. They were directly copied into the repositories which is a bad practice and can lead to potential security risks.

To fix this vulnerability, we have explicitly declared each dependency and its version in the project's pubspec.yaml file. The dependencies used in this project are shelf and shelf_io. We have specified the versions of these dependencies as ^1.1.5 and ^1.2.0 respectively. The caret ^ symbol before the version number means that the project is compatible with any version that is compatible with the specified version.

We have also removed any direct copies of dependencies from the repositories. Now, we are using the Dart package manager pub to manage the dependencies. This ensures that the correct versions of the dependencies are used and they can be easily updated.

It is also recommended to regularly check for updates to the dependencies and test the project with these updates to ensure compatibility. You can consider using a tool or service that can automatically check for updates to the dependencies and alert you when they are available.

References