Skip to content

prevent path traversal via chmod and mtime in zip unpacking - #5499

Merged
ivankravets merged 3 commits into
platformio:developfrom
knQzx:fix/zip-path-traversal-chmod
Aug 22, 2026
Merged

ivankravets merged 3 commits into
platformio:developfrom
knQzx:fix/zip-path-traversal-chmod

Conversation

@knQzx

@knQzx knQzx commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

ZIPArchiver applied chmod and mtime to the raw archive entry name joined onto the destination directory, so an entry like ../name could change permissions (including setuid) on a file outside the extraction directory. the tar path already guards this with is_bad_path, the zip path did not

this lifts is_bad_path/resolve_path to the base archiver and blocks zip entries that resolve outside the destination, matching the existing tar behavior

@ivankravets
ivankravets requested a lite review from Copilot August 22, 2026 12:05
@ivankravets ivankravets added this to the 6.2.0 milestone Aug 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens archive extraction in platformio/package/unpack.py by extending the existing TAR “bad path” guardrails to ZIP extraction, specifically to prevent applying chmod/mtime to paths that escape the destination directory.

Changes:

  • Lifted resolve_path / is_bad_path into BaseArchiver for reuse.
  • Added ZIP entry validation before extraction to block paths resolving outside dest_dir.
  • Ensured ZIP extraction uses a normalized destination path when performing safety checks.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread platformio/package/unpack.py Outdated
Comment on lines +134 to +140
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)
ivankravets and others added 2 commits August 22, 2026 15:11
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@ivankravets
ivankravets merged commit fe7c14a into platformio:develop Aug 22, 2026
26 of 30 checks passed
@ivankravets

Copy link
Copy Markdown
Member

Thanks for the PR!

ivankravets added a commit that referenced this pull request Aug 22, 2026
Jason2866 added a commit to pioarduino/platformio-core that referenced this pull request Sep 15, 2026
* fix integrity.dat not being cleaned on version string typo (platformio#5412)

catch SemanticVersionError when removing unused libdeps so a typo
in the version string doesn't permanently block builds

fixes platformio#5232

* Fix test

* Fixed an issue where the library integrity metadata was not invalidated // Resolve platformio#5232

* Fixed an issue where the library integrity metadata was not invalidated // Resolve platformio#5232

* Fix library test

* Stop closing Arduino serial port at Unity test completion (platformio#5405)

refactor: stop closing arduino serial port at unity test completion

Signed-off-by: Nguyen Huy Hoang <181364121+huyhoang171106@users.noreply.github.com>

* Update dependencies

* Resolved an issue where Unit Testing failed on Windows due to improper handling of ``Serial.end()`` // Resolve platformio#5359

* Resolved an issue with floating-point precision in duration formatting // Resolve platformio#5424

* Bump version to 6.1.20b1

* Remove unused imports

* Update deps

* Fixed an issue where the test suite status was incorrectly hardcoded as PASSED // Resolve platformio#5183 Resolve platformio#5302 Resolve platformio#5436

* Dropped support for Python 3.6

* Add support for stable Starlette 1.0 // Resolve platformio#5350 , Resolve platformio#5423

* Bump version to 6.2.0a1

* Add support for the latest Click

* Update 99-platformio-udev.rules: add `303a:4001 Espressif Systems Espressif Device` (platformio#5445)

Thanks for the PR

* Avoid infinite loop when cached mirror redirect keeps failing (platformio#5435)

* Avoid infinite loop when cached mirror redirect keeps failing

The registry mirror iterator caches the HEAD redirect for one hour,
but the cache hit path never advanced _visited_mirrors, and the
cached payload did not include X-PIO-Mirror. When a prior run cached
a redirect to a mirror that now fails (for example an SSL error on
the download), subsequent iterations kept hitting the same cache key
and returning the same failing mirror forever, so tool installs
looped until interrupted.

Store X-PIO-Mirror in the cached payload, and on cache hit only
reuse the entry if it names a mirror not already tried; otherwise
fall through to a fresh HEAD with bypass set. Stale cache entries
written before this change lack X-PIO-Mirror and fall through
naturally, so the fix self heals without a manual cache clear.

* Add unit tests for registry mirror iterator cache handling

Cover the cache hit path advancing _visited_mirrors, stale cache
entries without X-PIO-Mirror falling through to a fresh HEAD, and
new HEAD responses persisting X-PIO-Mirror so later runs can tell
whether the cached redirect still names an untried mirror. Each
test fails against the previous iterator implementation.

* Add type annotations to mirror iterator tests

* Use typing.X annotations for 3.6 compatibility

* Satisfy pylint and black on mirror iterator tests

* run: add -p/--port shortcut to set upload-port and monitor-port simultaneously. (platformio#5429)

Implement port shortcut for upload and monitor.

Add shortcut option for upload and monitor ports

* Add PLATFORMIO_DISABLE_UPGRADE_CHECK env var (platformio#5428)

Skips the periodic PyPI version check and core package auto-update
when set. Distro packages and CI typically manage upgrades themselves.

Ref: platformio#5427

* Update changelog

* Update changelog

* Option to disable udev rules check (platformio#5204)

This introduces "disable_udev_rules_check" option in Linux environment.

If set to True, udev rules are not checked. This allows to suppress annoying
warnings when 99-platformio-udev.rules is not installed intentionally.

If set to False (default), udev rules are checked as before.

* Added an option to disable the 99-platformio-udev.rules

* Resolve the issue with circular import // Resolve platformio#5462

* Bump version to 6.2.0a2

* Increase the maximum length of dependency version specifications to support long package URLs in project manifests // Resolve platformio#5459

* Fix: relative -d/--project-dir path causes FileNotFoundError in device monitor (platformio#5470)

Fix: resolve relative -d/--project-dir path in `pio device monitor`

`device_monitor_cmd`'s `-d/--project-dir` option accepted a relative path
without resolving it to absolute. The command then entered
`with fs.cd(options["project_dir"]):`, changing the working directory into
that relative subdir. Downstream, a platform monitor filter (e.g. the
espressif32 exception decoder) re-enters the same still-relative
`project_dir` via `load_build_metadata()`'s own `fs.cd()`, duplicating the
path segment onto the already-changed cwd and crashing with
FileNotFoundError (platformio#5439).

Add `resolve_path=True` to the click.Path type so the path is resolved to
absolute once, at the point it enters the system, before any fs.cd() chain
runs. This does not change the no-flag default (os.getcwd already returns
absolute) or the already-absolute case (passes through unchanged, modulo
symlink resolution).

Add a regression test mirroring the nested fs.cd() scenario, plus coverage
for the default and already-absolute cases.

* Update changelog for pull platformio#5470

* Fixed compatibility with ``pipx``- and ``uvx``-managed environments // Resolve platformio#5305 Resolve platformio#5418 Resolve platformio#5468

* Bump version to 6.2.0a3

* Fixed an issue where debug flags with level specifications (such as ``-g2``) were passed to the standalone assembler // Resolve platformio#5005

* Improve caller recognition

* Upgrade to Scons 4.11 // Resolve platformio#5105

* prevent path traversal via chmod and mtime in zip unpacking (platformio#5499)

* prevent path traversal via chmod and mtime in zip unpacking

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fix indentation of is_bad_path method

---------

Co-authored-by: Ivan Kravets <me@ikravets.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fixed a security vulnerability where ZIP archives containing relative path traversal entries // Pull platformio#5499

* Update CI actions and switch CI to Python 3.14

* Removed support for static code analysis via PVS-Studio // Resolve platformio#5360

* Do not exit when test fails

* Bump version to 6.2.0b1

* Handled Windows path length limitations during package extraction // Resolve platformio#5500

* Fail with a clear error when device monitor stdin is not a TTY  // Resolve 5113

* Fail with a clear error when device monitor stdin is not a TTY

`pio device monitor` crashed with a raw termios traceback
("Inappropriate ioctl for device") whenever stdin was piped or
redirected instead of a real terminal, e.g. `echo "" | pio device
monitor`. This happened because pyserial's Miniterm.Console tries to
call termios.tcgetattr() on stdin during Terminal construction, which
only works on a real TTY.

Detect non-TTY stdin before constructing the Terminal and raise a
clear UserSideException instead, pointing users toward talking to the
serial port directly (e.g. with pyserial) if they're trying to
automate device I/O from a script.

Fixes platformio#5113

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Ivan Kravets <me@ikravets.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Update history

* Modernize deprecated ::set-output to \ (platformio#5485)

* Update SCons to 4.11.1

* Add support for Click 8.5

* Bump version to 6.2.0b1

* Handle user typo when `owner` is declared in the `name` field

* Disable MISRA test on Python >= 3.14

* Update marshmallow constraints (allow 3.x and 4.x) (platformio#5507)

Update marshmallow dependency constraints and ensure compatibility with version 4

* Skip discontinued Mbed framework from CI

* Sync docs

* Update Sphinx docs engine to the latest version

* Fix improper static analysis report in verbose mode

Now internal clang-tidy diagnostic erros are skipped

Resolves platformio#4943

* Sync docs

* Apply formatting

* Fixed an issue where the test framework library was unintentionally removed from the project // Resolve platformio#5388

* Mark pioarduino as conflicted extension due to the security issues

* Fixed an issue where relative header paths provided via ``-include`` build flags were not resolved // Resolve platformio#5449

* Dropped support for Python 3.6, 3.7 and 3.8 // Resolve platformio#5414

* Bump version to 6.2.0rc1

* Omitted skipped environments and test suites // Resolve platformio#5402

* Bump version to 6.2.0

* Bump version to 6.2.1a1

* Document Neovim IDE

* Added support for the Neovim text editor

* Speed up detection of containerized environments

* New pio (#31)

* Update projects.yml

* Delete .github/workflows/docs.yml

* Delete .github/workflows/deployment.yml

* Delete .github/workflows/examples.yml

* pioarduino changes v6.1.19

* remove telemetry
* no full git clone
* add intelhex as required
* no core packages
* add `rich_click` as pip dependencies
* remove advertisings
* add intelhex as required
* install scons and Pio home from github
* replace "get_core_package_dir" for piohome and pioremote

* pioarduino v6.1.19

* remove telemetry
* no full git clone
* add intelhex as required
* no core packages
* add `rich_click` as pip dependencies
* remove advertisings
* add intelhex as required
* install scons and Pio home from github
* replace "get_core_package_dir" for piohome and pioremote

* Add esp-decoder to VSCode recommendations (#29)

* Add esp-decoder to VSCode recommendations

* fix: gdb use actual cmd syntax

* manage VSC extension (#30)

* add recommended extension / support clangd (#24)

* remove telemetry
* no full git clone
* add intelhex as required
* no core packages
* add `rich_click` as pip dependencies
* remove advertisings
* add intelhex as required
* install scons and Pio home from github
* replace "get_core_package_dir" for piohome and pioremote
* Add esp-decoder to VSCode recommendations (#29)
* fix: gdb use actual cmd syntax
* manage VSC extension (#30)

* Add udev rule for Espressif ESP-PROG 2 (#26)

* add support for ESP-PROG 2 (#32)

* Update ArduinoJson version in tests

* Update test_lib_complex.py

* Refactor JSON writing in test_manager.py

* Update python deps starlette uvicorn

* Rename title from 'pioarduino core' to 'pioarduino'

* Rename title from 'pioarduino core' to 'pioarduino'

* Remove test_check_pio_upgrade function

Removed the test_check_pio_upgrade function due to disabled upgrade check.

* Remove upgrade check test for PlatformIO

Removed test_check_pio_upgrade due to disabled upgrade check.

* updates

* migrate everything to `pioarduino`

* add retry to FileDownloader for transient errors (platformio#34)

* add GH PyPi deploy action

* update publish to actual requirements

* fix: pypi upload wheel and sdist

* pypi package exclude tests

* add MANIFEST.in to exclude test

* Upgrade actions/checkout and actions/setup-python

Updated GitHub Actions to use newer versions of checkout and setup-python.

* Restore test-only install gating

Co-authored-by: Jason2866 <24528715+Jason2866@users.noreply.github.com>

* Delete platformio/check/tools/pvsstudio.py

* Update version number to 6.2.0

* Remove 6.2.1 entry from HISTORY.rst

Removed version 6.2.1 entry and its features from HISTORY.rst.

* Update SCons tool version to 4.11.1

* Refactor GDB process to use asyncio for loop

* Remove telemetry logging from gdb.py

Removed telemetry logging for debug start and exceptions.

* Remove unused import in gdb.py

Removed unused import of DebugInitError.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* Remove duplicated ESP-PROG 2 udev rule

Co-authored-by: Jason2866 <24528715+Jason2866@users.noreply.github.com>

* Update GDB process to use asyncio for event loop

Replaced aio_get_running_loop with asyncio.get_running_loop for compatibility.

---------

Signed-off-by: Nguyen Huy Hoang <181364121+huyhoang171106@users.noreply.github.com>
Co-authored-by: Ivan Kravets <me@ikravets.com>
Co-authored-by: knQzx <75641500+knQzx@users.noreply.github.com>
Co-authored-by: Nguyen Huy Hoang <24520554@gm.uit.edu.vn>
Co-authored-by: chunix64 <141021414+chunix64@users.noreply.github.com>
Co-authored-by: J. Nick Koston <nick+github@koston.org>
Co-authored-by: Tinyu <tinyu0806@gmail.com>
Co-authored-by: Jamie Magee <jamie.magee@gmail.com>
Co-authored-by: Dmytro Bagrii <dimich.dmb@gmail.com>
Co-authored-by: josh <joshazmy@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jackson Bopp <boppjackson@gmail.com>
Co-authored-by: John <153130034+KarloffsGhost@users.noreply.github.com>
Co-authored-by: Austin <vidplace7@gmail.com>
Co-authored-by: valeros <valeros08@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants