Skip to content

fix(packager): reject zip entries that escape the extraction directory - #796

Merged
fatelei merged 2 commits into
langgenius:mainfrom
SashaMIT:fix/zip-extract-traversal
Aug 7, 2026
Merged

fix(packager): reject zip entries that escape the extraction directory#796
fatelei merged 2 commits into
langgenius:mainfrom
SashaMIT:fix/zip-extract-traversal

Conversation

@SashaMIT

@SashaMIT SashaMIT commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

ZipPluginDecoder.ExtractTo builds the output path with path.Join(dst, dir) / filepath.Join(workingPath, filename) from raw zip entry names. path.Join cleans the result but does not contain it, so an entry named ../../evil.py resolves outside the plugin working directory.

Impact: installing an attacker-crafted .difypkg yields arbitrary file write on the daemon host - overwriting runtime files, or planting code that the daemon subsequently loads/executes - anywhere signature verification is disabled (local/self-hosted installs) or the package otherwise reaches extraction.

Type of Change

  • Bug fix

Essential Checklist

Testing

  • I have tested the changes locally and confirmed they work as expected
  • I have added unit tests where necessary and they pass successfully

Each entry is now resolved against the destination with filepath.Rel and rejected unless strictly contained (illegal file path in plugin package). On rejection the partially-written working directory is removed, matching the existing error path.

Added pkg/plugin_packager/decoder/zip_extract_test.go:

  • TestExtractToRejectsPathTraversal: a ../../ entry errors and nothing is written outside dst
  • TestExtractToAllowsNormalEntries: nested legitimate entries still extract correctly

go test ./pkg/plugin_packager/decoder/ -run TestExtractTo -count=1 -v - both pass.

Additional Information

The read paths (ReadFile/ReadDir) are unaffected; this hardens only the write sink. Behavior for well-formed packages (including marketplace packages) is unchanged.

Made with Cursor

ZipPluginDecoder.ExtractTo joined each entry's dir/filename onto the
destination with path.Join, which cleans but does not contain: an entry
named '../../x' resolves outside dst, giving an uploaded .difypkg
arbitrary file write (overwriting runtime files or planted code that
the daemon then executes) wherever signature verification is off or the
package source is otherwise trusted enough to extract.

Each entry is now resolved against dst with filepath.Rel and rejected
unless the result is strictly contained. Adds regression tests for the
traversal case and for normal nested entries.

Signed-off-by: Sasha Mitchell <sash.t.mitchell@gmail.com>
@dosubot dosubot Bot added size:S This PR changes 10-29 lines, ignoring generated files. bug Something isn't working go Pull requests that update go code labels Aug 7, 2026
@fatelei

fatelei commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

you can use os.OpenRoot

Adopt the review suggestion: writes now go through a root-scoped handle,
so extraction cannot escape dst even if a path slips past the lexical
Rel() guard (symlink or platform edge). The explicit check stays for the
domain error message; OpenRoot is the enforcement layer.
@SashaMIT

SashaMIT commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Good call, done. Writes now go through os.OpenRoot(dst) (root.MkdirAll / root.OpenFile), so extraction is contained at the filesystem layer rather than only by the lexical check. Kept the filepath.Rel guard as well: it produces the clear "illegal file path in plugin package" error for traversal entries, while OpenRoot enforces containment even for anything the check misses. Module targets Go 1.26 so the API is available; decoder tests pass.

@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:S This PR changes 10-29 lines, ignoring generated files. labels Aug 7, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Aug 7, 2026
@fatelei
fatelei merged commit 6fde0a7 into langgenius:main Aug 7, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working go Pull requests that update go code lgtm This PR has been approved by a maintainer size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants