Skip to content

Commit 8092ad6

Browse files
Use safer tar/zip extract in get.py (#3171)
1 parent 2793282 commit 8092ad6

File tree

1 file changed

+47
-5
lines changed

1 file changed

+47
-5
lines changed

tools/get.py

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,51 @@
2323

2424
dist_dir = 'dist/'
2525

26+
27+
## Borrowed from Arduino-ESP32. Thanks!
28+
def is_safe_archive_path(path):
29+
# Check for absolute paths (both Unix and Windows style)
30+
if path.startswith("/") or (len(path) > 1 and path[1] == ":" and path[2] in "\\/"):
31+
raise ValueError(f"Absolute path not allowed: {path}")
32+
33+
# Normalize the path to handle any path separators
34+
normalized_path = os.path.normpath(path)
35+
36+
# Check for directory traversal attempts using normalized path
37+
if ".." in normalized_path.split(os.sep):
38+
raise ValueError(f"Directory traversal not allowed: {path}")
39+
40+
# Additional check for paths that would escape the target directory
41+
if normalized_path.startswith(".."):
42+
raise ValueError(f"Path would escape target directory: {path}")
43+
44+
# Check for any remaining directory traversal patterns in the original path
45+
# This catches cases that might not be normalized properly
46+
path_parts = path.replace("\\", "/").split("/")
47+
if ".." in path_parts:
48+
raise ValueError(f"Directory traversal not allowed: {path}")
49+
50+
return True
51+
52+
53+
def safe_tar_extract(tar_file, destination):
54+
# Validate all paths before extraction
55+
for member in tar_file.getmembers():
56+
is_safe_archive_path(member.name)
57+
58+
# If all paths are safe, proceed with extraction
59+
tar_file.extractall(destination, filter="tar")
60+
61+
62+
def safe_zip_extract(zip_file, destination):
63+
# Validate all paths before extraction
64+
for name in zip_file.namelist():
65+
is_safe_archive_path(name)
66+
67+
# If all paths are safe, proceed with extraction
68+
zip_file.extractall(destination)
69+
70+
2671
def sha256sum(filename, blocksize=65536):
2772
hash = hashlib.sha256()
2873
with open(filename, "rb") as f:
@@ -51,15 +96,12 @@ def unpack(filename, destination):
5196
if filename.endswith('tar.gz'):
5297
tfile = tarfile.open(filename, 'r:gz')
5398
os.chdir("../system/")
54-
try:
55-
tfile.extractall(destination, filter='fully_trusted')
56-
except TypeError:
57-
tfile.extractall(destination)
99+
safe_tar_extract(tfile, destination)
58100
dirname= tfile.getnames()[0]
59101
elif filename.endswith('zip'):
60102
zfile = zipfile.ZipFile(filename)
61103
os.chdir("../system/")
62-
zfile.extractall(destination)
104+
safe_zip_extract(zfile, destination)
63105
dirname = zfile.namelist()[0]
64106
else:
65107
raise NotImplementedError('Unsupported archive type')

0 commit comments

Comments
 (0)