Skip to content

fix(webserver): uncontrolled data used in path expression could lead path traversal #645

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pghoard/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,12 @@ def _rename(self, src: str, dst: str) -> None:

@staticmethod
def _validate_target_path(pg_data_directory: str, target_path: str) -> None:
# Normalize the target path to remove any ".." components
normalized_target_path = os.path.normpath(target_path)

# Ensure the normalized path is within the pg_data_directory
if not normalized_target_path.startswith(os.path.abspath(pg_data_directory)):
raise HttpResponse("Invalid target path: outside of allowed directory", status=400)
# The `restore_command` (postgres_command.py or pghoard_postgres_command_go.go) called by PostgresSQL has
# prepended the PostgresSQL 'data' directory with `%p` parameter from PostgresSQL server, hence here
# `target_path` is expected to be an absolute path.
Expand Down Expand Up @@ -551,6 +557,8 @@ def get_wal_or_timeline_file(self, site: str, filename: str, filetype: str) -> N
self.server.log.warning("Found file: %r but it was invalid: %s", xlog_path, e)
else:
if xlog_path != target_path:
# Ensure target_path is validated before use
self._validate_target_path(site_config["pg_data_directory"], target_path)
shutil.copyfile(xlog_path, target_path)
self.server.served_from_disk.append(filename)
self.server.most_recently_served_files[filetype] = {
Expand Down