Improvement of coding practices to reduce cyclomatic complexity
import 'package:shelf/shelf.dart' as shelf;
shelf.Response handleRequest(shelf.Request request) {
var method = request.method;
var url = request.requestedUri.path;
if (method == 'GET') {
if (url == '/home') {
return shelf.Response.ok('Home Page');
} else if (url == '/about') {
return shelf.Response.ok('About Page');
} else if (url == '/contact') {
return shelf.Response.ok('Contact Page');
} else if (url == '/help') {
return shelf.Response.ok('Help Page');
} else if (url == '/privacy') {
return shelf.Response.ok('Privacy Page');
} else if (url == '/terms') {
return shelf.Response.ok('Terms Page');
} else {
return shelf.Response.notFound('Page Not Found');
}
} else if (method == 'POST') {
if (url == '/login') {
return shelf.Response.ok('Login Page');
} else if (url == '/signup') {
return shelf.Response.ok('Signup Page');
} else if (url == '/logout') {
return shelf.Response.ok('Logout Page');
} else {
return shelf.Response.notFound('Page Not Found');
}
} else {
return shelf.Response.forbidden('Method Not Allowed');
}
}
This code has a high cyclomatic complexity due to the nested
if
and
else if
statements. Each
if
or
else if
statement adds a path through the code, increasing the cyclomatic complexity. This makes the code harder to understand, maintain, and test.
In addition, the code's complexity can lead to the introduction of new security vulnerabilities. For example, if a new endpoint is added, it might be easy to forget to add it to the list of allowed endpoints. This could lead to unauthorized access to the endpoint.
Moreover, the high complexity of the code makes it harder to detect and fix existing vulnerabilities. For example, if a vulnerability is found in the way the application handles requests, it will be harder to locate the vulnerable code due to the high number of paths through the code.
To mitigate this, the code can be refactored to reduce the number of conditional statements, thus reducing the cyclomatic complexity. This can be achieved by using a routing library or creating a map of routes and their corresponding handlers.
import 'package:shelf/shelf.dart' as shelf;
// Define a map for GET requests
final Map getResponses = {
'/home': 'Home Page',
'/about': 'About Page',
'/contact': 'Contact Page',
'/help': 'Help Page',
'/privacy': 'Privacy Page',
'/terms': 'Terms Page',
};
// Define a map for POST requests
final Map postResponses = {
'/login': 'Login Page',
'/signup': 'Signup Page',
'/logout': 'Logout Page',
};
shelf.Response handleRequest(shelf.Request request) {
var method = request.method;
var url = request.requestedUri.path;
switch (method) {
case 'GET':
return getResponses.containsKey(url)
? shelf.Response.ok(getResponses[url])
: shelf.Response.notFound('Page Not Found');
case 'POST':
return postResponses.containsKey(url)
? shelf.Response.ok(postResponses[url])
: shelf.Response.notFound('Page Not Found');
default:
return shelf.Response.forbidden('Method Not Allowed');
}
}
The refactored code reduces the cyclomatic complexity by eliminating the nested if-else statements and replacing them with a switch-case statement and map lookups.
The
handleRequest
function now uses a switch-case statement to handle different HTTP methods. For 'GET' and 'POST' methods, it looks up the requested URL in the corresponding map (
getResponses
or
postResponses
). If the URL is found in the map, it returns a response with the corresponding value from the map. If the URL is not found, it returns a 'Page Not Found' response.
For any other HTTP methods, the function returns a 'Method Not Allowed' response.
This refactoring makes the code easier to understand and maintain, and reduces the risk of introducing new security vulnerabilities.