Repository navigation
prevent path traversal via chmod and mtime in zip unpacking - #5499
Merged
ivankravets merged 3 commits intoAug 22, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
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_pathintoBaseArchiverfor 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 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) |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Member
|
Thanks for the PR! |
ivankravets
added a commit
that referenced
this pull request
Aug 22, 2026
… path traversal entries // Pull #5499
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ZIPArchiverapplied chmod and mtime to the raw archive entry name joined onto the destination directory, so an entry like../namecould change permissions (including setuid) on a file outside the extraction directory. the tar path already guards this withis_bad_path, the zip path did notthis lifts
is_bad_path/resolve_pathto the base archiver and blocks zip entries that resolve outside the destination, matching the existing tar behavior