[security] Zip-Slip arbitrary file write in import and package install
Have you enabled troubleshooting mode?
- I confirm that the troubleshooting mode is enabled.
Describe the bug
The unzipFile method in internal/manager/task_import.go:156 joins zip entry names directly into the extraction path without validating that the result stays within BaseDir:
fn := filepath.Join(t.BaseDir, f.Name)A zip entry named ../../../../arbitrary.txt resolves to a path outside the import directory, allowing arbitrary file write. No authentication is required in the default configuration (HasCredentials() returns false).
A second instance exists in pkg/pkg/manager.go:221-222 where filepath.Clean(f.Name) preserves leading ../ and store.writeFile at pkg/pkg/store.go:106 joins it with the package directory, escaping it.
A third issue: internal/api/routes_plugin.go:60 uses strings.HasPrefix(dir, pluginDir) without a trailing separator, allowing a plugin directory named foo to match a sibling foobar.
Steps to reproduce
- Start stash with default configuration (no credentials set).
- Complete setup via the
setupGraphQL mutation. - Create a zip file containing an entry with path
../../../../canary.txt. - Upload it via the
importObjectsGraphQL mutation: curl -X POST http://127.0.0.1:9999/graphql
-F 'operations={"query":"mutation ImportObjects($input: ImportObjectsInput!) { importObjects(input: $input) }","variables":{"input":{"file":null,"duplicateBehaviour":"IGNORE","missingRefBehaviour":"IGNORE"}}}'
-F 'map={"0":["variables.input.file"]}'
-F '[email protected];type=application/zip' - The file is written outside the import directory to the traversal-resolved path.
Expected behaviour
Zip entries containing path traversal sequences (../) should be rejected or sanitized before extraction. The extracted path should be validated to remain within the target directory using strings.HasPrefix with a trailing path separator.
Screenshots or additional context
Stash version
v0.31.1
Device details
macOS 15.5 arm64 (Apple Silicon), tested via curl against local instance
Relevant log output
VULN CONFIRMED — file written outside BaseDir:
path: /var/folders/81/rrdppfgn3f1_1krl95k89lv00000gn/T/tmp.ykP6j2coo7/canary_proof.txt
content: ZIP-SLIP-PROOF: arbitrary file write outside BaseDirSource: stashapp/stash