From 6a98f536a8b499d53b429a9cfd736a14884ed6a7 Mon Sep 17 00:00:00 2001 From: knQzx <75641500+knQzx@users.noreply.github.com> Date: Wed, 12 Aug 2026 22:34:48 +0200 Subject: [PATCH 1/3] prevent path traversal via chmod and mtime in zip unpacking --- platformio/package/unpack.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/platformio/package/unpack.py b/platformio/package/unpack.py index ea140ae930..710ddab96c 100644 --- a/platformio/package/unpack.py +++ b/platformio/package/unpack.py @@ -45,6 +45,13 @@ def get_item_filename(self, item): def is_link(self, item): raise NotImplementedError() + @staticmethod + def resolve_path(path): + return os.path.realpath(os.path.abspath(path)) + + def is_bad_path(self, path, base): + return not self.resolve_path(os.path.join(base, path)).startswith(base) + def extract_item(self, item, dest_dir): self._afo.extract(item, dest_dir) self.after_extract(item, dest_dir) @@ -70,13 +77,6 @@ def get_item_filename(self, item): def is_link(item): # pylint: disable=arguments-differ return item.islnk() or item.issym() - @staticmethod - def resolve_path(path): - return os.path.realpath(os.path.abspath(path)) - - def is_bad_path(self, path, base): - return not self.resolve_path(os.path.join(base, path)).startswith(base) - def is_bad_link(self, item, base): return not self.resolve_path( os.path.join(os.path.join(base, os.path.dirname(item.name)), item.linkname) @@ -129,6 +129,16 @@ def get_items(self): def get_item_filename(self, item): return item.filename + def extract_item(self, item, dest_dir): + dest_dir = self.resolve_path(dest_dir) + if self.is_bad_path(item.filename, dest_dir): + return click.secho( + "Blocked insecure item `%s` from ZIP archive" % item.filename, + fg="red", + err=True, + ) + return super().extract_item(item, dest_dir) + def after_extract(self, item, dest_dir): self.preserve_permissions(item, dest_dir) self.preserve_mtime(item, dest_dir) From 3dd0a065a7f954cfa3dd4d5f41cd9de266ef60d7 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Sat, 22 Aug 2026 15:11:05 +0300 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- platformio/package/unpack.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/platformio/package/unpack.py b/platformio/package/unpack.py index 710ddab96c..b17e0ea81e 100644 --- a/platformio/package/unpack.py +++ b/platformio/package/unpack.py @@ -49,8 +49,13 @@ def is_link(self, item): def resolve_path(path): return os.path.realpath(os.path.abspath(path)) - def is_bad_path(self, path, base): - return not self.resolve_path(os.path.join(base, path)).startswith(base) +def is_bad_path(self, path, base): + base = self.resolve_path(base) + target = self.resolve_path(os.path.join(base, path)) + try: + return os.path.commonpath([base, target]) != base + except ValueError: + return True def extract_item(self, item, dest_dir): self._afo.extract(item, dest_dir) From 6cae3ab576944f51377ec9e386ae8196a269dfb8 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Sat, 22 Aug 2026 16:24:44 +0300 Subject: [PATCH 3/3] Fix indentation of is_bad_path method --- platformio/package/unpack.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/platformio/package/unpack.py b/platformio/package/unpack.py index b17e0ea81e..0d443ead99 100644 --- a/platformio/package/unpack.py +++ b/platformio/package/unpack.py @@ -49,13 +49,13 @@ def is_link(self, item): def resolve_path(path): return os.path.realpath(os.path.abspath(path)) -def is_bad_path(self, path, base): - base = self.resolve_path(base) - target = self.resolve_path(os.path.join(base, path)) - try: - return os.path.commonpath([base, target]) != base - except ValueError: - return True + def is_bad_path(self, path, base): + base = self.resolve_path(base) + target = self.resolve_path(os.path.join(base, path)) + try: + return os.path.commonpath([base, target]) != base + except ValueError: + return True def extract_item(self, item, dest_dir): self._afo.extract(item, dest_dir)