Skip to content

Commit d81addd

Browse files
committed
Prevent path traversal attack in FilesystemExporter
Fix validation so that relative paths cannot escape their base path by introducing "../" segments.
1 parent 5c6f060 commit d81addd

4 files changed

Lines changed: 22 additions & 1 deletion

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved validation of relative paths to prevent a path traversal attack in filesystem exports. (CVE-2026-12701)

pulpcore/app/serializers/fields.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def relative_path_validator(relative_path):
2121
raise serializers.ValidationError(
2222
_("Relative path can't start with '/'. {0}").format(relative_path)
2323
)
24+
if os.path.normpath(relative_path).startswith("../"):
25+
raise serializers.ValidationError(
26+
_("Relative path must not reach beyond the base path. {0}").format(relative_path)
27+
)
2428

2529

2630
# Prefer JSONDictField and JSONListField over JSONField:

pulpcore/app/tasks/export.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
Task,
3131
)
3232
from pulpcore.app.models.content import ContentArtifact, RemoteArtifact
33-
from pulpcore.app.serializers import PulpExportSerializer
33+
from pulpcore.app.serializers import PulpExportSerializer, relative_path_validator
3434
from pulpcore.app.util import Crc32Hasher, HashingFileWriter, compute_file_hash
3535
from pulpcore.constants import FS_EXPORT_METHODS
3636

@@ -135,6 +135,7 @@ def _export_local_to_file_system(
135135
method: FS_EXPORT_METHODS constant (WRITE, SYMLINK, or HARDLINK).
136136
"""
137137
for relative_path, src_path in relative_paths_to_local_paths.items():
138+
relative_path_validator(relative_path)
138139
dest = os.path.join(path, relative_path)
139140
os.makedirs(os.path.split(dest)[0], exist_ok=True)
140141

@@ -182,6 +183,7 @@ def _export_to_file_system(path, relative_paths_to_artifacts, method=FS_EXPORT_M
182183
method = FS_EXPORT_METHODS.WRITE
183184

184185
for relative_path, artifact in relative_paths_to_artifacts.items():
186+
relative_path_validator(relative_path)
185187
dest = os.path.join(path, relative_path)
186188
os.makedirs(os.path.split(dest)[0], exist_ok=True)
187189

pulpcore/tests/unit/serializers/test_fields.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from pulpcore.app.serializers.fields import (
66
PgpKeyFingerprintField,
77
pulp_labels_validator,
8+
relative_path_validator,
89
)
910

1011

@@ -208,3 +209,16 @@ def test_pgp_key_fingerprint_field_custom_max_length():
208209
def test_pgp_key_fingerprint_field_normalize(value, expected):
209210
"""PgpKeyFingerprintField.normalize should uppercase hex after the colon."""
210211
assert PgpKeyFingerprintField.normalize(value) == expected
212+
213+
214+
@pytest.mark.parametrize(
215+
("path",),
216+
[
217+
pytest.param("/absolute/path", id="absolute"),
218+
pytest.param("../sneaky/path", id="path_traversal"),
219+
pytest.param("suspicious/../../sneaky/path", id="hidden_path_traversal"),
220+
],
221+
)
222+
def test_relative_path_validator_rejects(path):
223+
with pytest.raises(serializers.ValidationError):
224+
relative_path_validator(path)

0 commit comments

Comments
 (0)