Skip to main content

Insecure exceptions - Empty or no catch

Need

Proper exception handling and error management

Context

  • Usage of Ruby for building dynamic and object-oriented applications
  • Usage of Ruby for building dynamic and object-oriented applications

Description

Non compliant code

def vulnerable_method
begin
# Some code that might raise an exception
file = File.open("non_existent_file.txt", "r")
data = file.read
rescue
# Empty catch block
ensure
file.close if file
end
end

In this code snippet, we have a method vulnerable_method which is trying to open and read a file named non_existent_file.txt. Since this file does not exist, this will raise a FileNotFoundError exception.

However, the rescue block which is supposed to handle this exception is empty. This means that if an exception occurs, the program won't know how to handle it and it will stop execution, possibly making the application unavailable.

Moreover, the empty rescue block also swallows the exception, meaning that we won't have any information about what went wrong, making it much harder to debug the issue.

In Ruby, it is considered a bad practice to leave a rescue block empty, or to have a begin statement without a rescue. This is because it can lead to unexpected behavior and makes debugging more difficult.

Steps

  • Identify the specific exceptions that can be raised by the code within the 'begin' block.
  • Add specific 'rescue' blocks for each identified exception.
  • Within each 'rescue' block, handle the exception appropriately, such as logging the error or providing a user-friendly error message.
  • Consider whether any cleanup or finalization code needs to be executed regardless of whether an exception occurs. If so, move that code to the 'ensure' block.
  • Ensure that the 'ensure' block is used to close any resources that were opened in the 'begin' block, such as files or database connections.

Compliant code

def secure_method
begin
# Some code that might raise an exception
file = File.open("non_existent_file.txt", "r")
data = file.read
rescue Errno::ENOENT => e
# Handle the exception by logging the error
Rails.logger.error "File not found: #{e}"
rescue StandardError => e
# Handle all other errors
Rails.logger.error "An error occurred: #{e}"
ensure
# Ensure the file is closed, even if an error occurs
file.close if file
end
end

The updated code now handles exceptions properly.

In the begin block, we have the code that might raise an exception. In this case, it's trying to open and read a file.

We then have two rescue blocks. The first one catches the Errno::ENOENT exception, which is raised when the file we're trying to open doesn't exist. The second rescue block catches all other standard errors.

In each rescue block, we're logging the error using Rails' logger. This way, we're aware of the issues but they don't cause the application to crash.

Finally, in the ensure block, we make sure to close the file if it was opened. This block of code will run regardless of whether an exception was raised, ensuring that we don't leave resources like files or database connections open.

References