|
23 | 23 |
|
24 | 24 | dist_dir = 'dist/'
|
25 | 25 |
|
| 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 | + |
26 | 71 | def sha256sum(filename, blocksize=65536):
|
27 | 72 | hash = hashlib.sha256()
|
28 | 73 | with open(filename, "rb") as f:
|
@@ -51,15 +96,12 @@ def unpack(filename, destination):
|
51 | 96 | if filename.endswith('tar.gz'):
|
52 | 97 | tfile = tarfile.open(filename, 'r:gz')
|
53 | 98 | 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) |
58 | 100 | dirname= tfile.getnames()[0]
|
59 | 101 | elif filename.endswith('zip'):
|
60 | 102 | zfile = zipfile.ZipFile(filename)
|
61 | 103 | os.chdir("../system/")
|
62 |
| - zfile.extractall(destination) |
| 104 | + safe_zip_extract(zfile, destination) |
63 | 105 | dirname = zfile.namelist()[0]
|
64 | 106 | else:
|
65 | 107 | raise NotImplementedError('Unsupported archive type')
|
|
0 commit comments