There are two built-in Python modules that allow for file manipulation. The original module is called os
. It provides the ability to perform various functions related to the operating system, among which are creating, updating, and deleting files [Python docs - os]. Python 3.4 introduced the pathlib
module, which represents file paths as objects as opposed to string variables [Python docs - pathlib].
Both modules use the same OS exceptions for file-related errors. Here are some of the common file-related exceptions:
Exception name | Description |
---|---|
OSError |
Base exception for all OS-related error. |
FileExistsError |
Raised when trying to create a file or directory which already exists. |
FileNotFoundError |
Raised when a file or directory is requested but doesn’t exist. |
IsADirectoryError |
Raised when a file operation is requested on a directory. |
NotADirectoryError |
Raised when a directory operation is requested on something which is not a directory. |
PermissionError |
Raised when trying to run an operation without adequate access rights - for example, filesystem permissions. |
The full list of OS exceptions can be found in the Python documentation [Python docs - OS Exceptions].
It is important to handle those exceptions when performing file I/O operations.
os.remove()
/os.unlink()
)This code example demonstrates an attempt to read a non-existent file using the os
module. The read_file()
function opens a file and reads its content using os.open()
and os.read()
. If the file does not exist, an OSError
or FileNotFoundError
will be raised when trying to access the randomly generated file name.
""" Non-compliant Code Example """
import os
import uuid
def read_file(file):
"""Function for opening a file and reading it's content."""
fd = os.open(file, os.O_RDONLY)
content = os.read(fd)
return content.decode()
#####################
# exploiting above code example
#####################
# Attempting to read a random non-existent file
read_file(f"{uuid.uuid4()}.txt")
try/except
blocks)The file opening and reading should be surrounded by the try/except
block. This way, we can catch the generic OSError
and handle the error differently depending on its cause (such as the file not existing or it being a directory instead).
""" Compliant Code Example """
import os
import uuid
def read_file(file):
"""Function for opening a file and reading its content."""
try:
fd = os.open(file, os.O_RDONLY)
try:
content = os.read(fd, 1024)
finally:
os.close(fd)
return content.decode()
except OSError as e:
if not os.path.exists(file):
print(f"File not found: {file}")
elif os.path.isdir(file):
print(f"Is a directory: {file}")
else:
print(f"An error occurred: {e}")
#####################
# exploiting above code example
#####################
# Attempting to read a random non-existent file
read_file(f"{uuid.uuid4()}.txt")
pathlib.Path.unlink()
)The pathlib
module also provides functions for opening and reading files. The Path.read_text()
method attempts to read the content of the file represented by the Path
object. If the file does not exist, it will raise a FileNotFoundError
. In this code example, this exception is expected when attempting to read a randomly generated non-existent file.
""" Non-compliant Code Example """
import uuid
from pathlib import Path
def read_file(file):
"""Function for opening a file and reading its content."""
path = Path(file)
content = path.read_text(encoding="utf-8")
return content
#####################
# exploiting above code example
#####################
# Attempting to read a random non-existent file
read_file(f"{uuid.uuid4()}.txt")
The pathlib.Path.unlink()
function has an optional parameter missing_ok
that will suppress the FileNotFoundError
on file deletion, if the parameter’s value is True
. However, without proper handling, using this parameter will cause the script to fail silently.
""" Non-compliant Code Example """
import pathlib
import uuid
def delete_temporary_file(file):
"""Function for deleting a temporary file from a certain location"""
resource_path = pathlib.Path(file)
resource_path.unlink(missing_ok=True)
#####################
# exploiting above code example
#####################
# Attempting to remove a random non-existent file
delete_temporary_file(f"{uuid.uuid4()}.txt")
pathlib
module)Since the pathlib
module uses the same exceptions as the os
module, error handling can be implemented in the same way.
""" Non-compliant Code Example """
import uuid
from pathlib import Path
def read_file(file):
"""Function for opening a file and reading its content."""
path = Path(file)
try:
content = path.read_text(encoding="utf-8")
return content
except OSError as e:
if path.is_dir():
print(f"Is a directory: {file}")
elif not path.exists():
print(f"File not found: {file}")
else:
print(f"An error occurred: {e}")
return None
#####################
# exploiting above code example
#####################
# Attempting to read a random non-existent file
read_file(f"{uuid.uuid4()}.txt")
Tool | Version | Checker | Description |
no detection |
SEI CERT C++ Coding Standard | VOID FIO04-CPP. Detect and handle input and output errors |
SEI CERT Oracle Coding Standard for Java | FIO02-J. Detect and handle file-related errors - SEI CERT Oracle Coding Standard for Java |
CWE MITRE Pillar | CWE-703, Improper Check or Handling of Exceptional Conditions |
CWE MITRE Class | CWE-755, Improper Handling of Exceptional Conditions |