Skip to content

Check that resolved path is still in dstdir for export logs#7416

Open
hgreebe wants to merge 1 commit into
aws:developfrom
hgreebe:develop
Open

Check that resolved path is still in dstdir for export logs#7416
hgreebe wants to merge 1 commit into
aws:developfrom
hgreebe:develop

Conversation

@hgreebe
Copy link
Copy Markdown
Contributor

@hgreebe hgreebe commented Jun 1, 2026

Description of changes

  • Check that resolved path is still in dstdir for export logs
  • Fixes bug where an attacker-controlled S3 object key could be used to escape the intended local export directory and overwrite an arbitrary file in the security context of the user or automation account running the export command.

Tests

  • Ran pcluster export-cluster-logs

Please review the guidelines for contributing and Pull Request Instructions.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

@hgreebe hgreebe requested review from a team as code owners June 1, 2026 13:36
)
decompressed_path = os.path.realpath(decompressed_path)
if not decompressed_path.startswith(os.path.realpath(destdir) + os.sep):
LOGGER.warning("Skipping unsafe S3 key (path traversal detected): %s", archive_object.key)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Path traversal" may be a bit cryptic/not-actionable for the end user. I suggest to make the message more explicit by mentioning the decompressed path and the destination path that it is escaping.

Also, I suggest to log this as an error because:

  1. the effect is that the export does not complete, which is a pretty bad outcome for the user
  2. the reason why it does not occur is related to a security issue

decompressed_path = decompressed_path.replace(
r"{unwanted_path_segment}{sep}".format(unwanted_path_segment=prefix, sep=os.path.sep), ""
)
decompressed_path = os.path.realpath(decompressed_path)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Testing] Can we cover this change with a unit test focused on provind the defense against the attack scenairo?

r"{unwanted_path_segment}{sep}".format(unwanted_path_segment=prefix, sep=os.path.sep), ""
)
decompressed_path = os.path.realpath(decompressed_path)
if not decompressed_path.startswith(os.path.realpath(destdir) + os.sep):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if destdir is passed by the user already with a trailing slash, e.g. /my/destination/dir/ ?
In that case even a legitimate decompressed path could fail the check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants