Skip to content

Commit 08ec477

Browse files
committed
fix(get.py): Add path validation for archives
1 parent f2697f3 commit 08ec477

File tree

1 file changed

+50
-3
lines changed

1 file changed

+50
-3
lines changed

tools/get.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,49 @@
5050
dist_dir = current_dir + "/dist/"
5151

5252

53+
def is_safe_archive_path(path):
54+
# Check for absolute paths (both Unix and Windows style)
55+
if path.startswith('/') or (len(path) > 1 and path[1] == ':' and path[2] in '\\/'):
56+
raise ValueError(f"Absolute path not allowed: {path}")
57+
58+
# Normalize the path to handle any path separators
59+
normalized_path = os.path.normpath(path)
60+
61+
# Check for directory traversal attempts using normalized path
62+
if ".." in normalized_path.split(os.sep):
63+
raise ValueError(f"Directory traversal not allowed: {path}")
64+
65+
# Additional check for paths that would escape the target directory
66+
if normalized_path.startswith(".."):
67+
raise ValueError(f"Path would escape target directory: {path}")
68+
69+
# Check for any remaining directory traversal patterns in the original path
70+
# This catches cases that might not be normalized properly
71+
path_parts = path.replace('\\', '/').split('/')
72+
if '..' in path_parts:
73+
raise ValueError(f"Directory traversal not allowed: {path}")
74+
75+
return True
76+
77+
78+
def safe_tar_extract(tar_file, destination):
79+
# Validate all paths before extraction
80+
for member in tar_file.getmembers():
81+
is_safe_archive_path(member.name)
82+
83+
# If all paths are safe, proceed with extraction
84+
tar_file.extractall(destination, filter="tar")
85+
86+
87+
def safe_zip_extract(zip_file, destination):
88+
# Validate all paths before extraction
89+
for name in zip_file.namelist():
90+
is_safe_archive_path(name)
91+
92+
# If all paths are safe, proceed with extraction
93+
zip_file.extractall(destination)
94+
95+
5396
def sha256sum(filename, blocksize=65536):
5497
hash = hashlib.sha256()
5598
with open(filename, "rb") as f:
@@ -212,6 +255,10 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901
212255
print("File corrupted or incomplete!")
213256
cfile = None
214257
file_is_corrupted = True
258+
except ValueError as e:
259+
print(f"Security validation failed: {e}")
260+
cfile = None
261+
file_is_corrupted = True
215262

216263
if file_is_corrupted:
217264
corrupted_filename = filename + ".corrupted"
@@ -243,15 +290,15 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901
243290
if filename.endswith("tar.gz"):
244291
if not cfile:
245292
cfile = tarfile.open(filename, "r:gz")
246-
cfile.extractall(destination, filter="tar")
293+
safe_tar_extract(cfile, destination)
247294
elif filename.endswith("tar.xz"):
248295
if not cfile:
249296
cfile = tarfile.open(filename, "r:xz")
250-
cfile.extractall(destination, filter="tar")
297+
safe_tar_extract(cfile, destination)
251298
elif filename.endswith("zip"):
252299
if not cfile:
253300
cfile = zipfile.ZipFile(filename)
254-
cfile.extractall(destination)
301+
safe_zip_extract(cfile, destination)
255302
else:
256303
raise NotImplementedError("Unsupported archive type")
257304

0 commit comments

Comments
 (0)