Skip to content

fix(publish): sanitize server-controlled artifact path (GHSA-x55v-q459-68ch)#4989

Merged
proggeramlug merged 2 commits into
PerryTS:mainfrom
proggeramlug:fix/publish-path-traversal-ghsa-x55v
Jun 11, 2026
Merged

fix(publish): sanitize server-controlled artifact path (GHSA-x55v-q459-68ch)#4989
proggeramlug merged 2 commits into
PerryTS:mainfrom
proggeramlug:fix/publish-path-traversal-ghsa-x55v

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Fixes the path-traversal / arbitrary-file-write reported in GHSA-x55v-q459-68ch.

perry publish processes ArtifactReady WebSocket messages from the build server and used the server-supplied artifact_name (and download_path) verbatim when building the local destination path. Since PathBuf::join does not normalize .. or reject absolute paths, a malicious/compromised hub could:

  • Arbitrary write (Path A & B destination): deliver artifact_name = "../../.ssh/authorized_keys" (or an absolute path) and have downloaded content written anywhere the process can write.
  • Arbitrary local read (Path B): in the self-hosted-hub local-copy path, set download_path to any local file (e.g. ~/.aws/credentials) and have it copied into the output directory.

The primary multi-victim vector is a malicious PR setting [publish] server = "https://attacker…" in a repo's perry.toml; CI runs (--no-interactive / non-TTY) get no confirmation prompt.

Fix

  • sanitize_artifact_name() reduces the server-supplied name to a single, traversal-free file name (rejects empty, ., .., embedded / or \, absolute/drive-prefixed paths) before it's joined onto the output dir. This closes the arbitrary-write vector for both paths.
  • server_is_local() gates the download_path local-copy shortcut to loopback hubs only (localhost, 127.0.0.0/8, ::1). A remote hub's local filesystem path is meaningless and is no longer honored — remote hubs fall back to the existing HTTP download. This closes the arbitrary local-file-read vector.

Tests

Added unit tests in commands/publish/tests.rs:

  • accepted plain names (incl. whitespace trimming)
  • a battery of traversal/absolute/separator payloads, all rejected
  • the loopback gate (localhost/127.0.0.1/[::1] allowed; public hosts, private LAN IPs, and unparseable URLs rejected)

cargo test -p perry --bins publish::tests → 27 passed, 0 failed.

Notes

  • No behavioral change for legitimate runs: artifact names are normally bare filenames, and the local-copy shortcut only applied to same-machine self-hosted hubs (still allowed via loopback). Remote self-hosted hubs that previously relied on a shared-filesystem download_path will transparently fall back to HTTP download.
  • Heads-up: this PR references an advisory that is still in triage. If you'd prefer to land it privately first, it can instead go through the advisory's temporary private fork before publishing.

Ralph Küpper added 2 commits June 11, 2026 11:48
…9-68ch)

perry publish trusted the build server's ArtifactReady.artifact_name and
download_path verbatim when constructing the local destination path. Because
PathBuf::join does not normalize .. or reject absolute paths, a malicious hub
could write downloaded content anywhere the process can write (e.g.
../../.ssh/authorized_keys), and in the self-hosted-hub local-copy path could
also copy out arbitrary local files via a server-chosen download_path.

- sanitize_artifact_name() reduces artifact_name to a bare, traversal-free
  file name (rejects absolute paths, ., .., and embedded separators) before
  it is joined onto the output directory. Closes the arbitrary-write vector.
- server_is_local() gates the download_path local-copy shortcut to loopback
  hubs only; a remote hub's local filesystem path is never honored, closing
  the arbitrary local-file-read vector. Remote hubs fall back to HTTP download.

Adds unit tests covering accepted names, traversal payloads, and the
loopback gate.
@proggeramlug proggeramlug merged commit 95e1043 into PerryTS:main Jun 11, 2026
13 checks passed
proggeramlug added a commit that referenced this pull request Jun 11, 2026
Security release. Bumps [workspace.package].version 0.5.1158 -> 0.5.1159
(+ Cargo.lock, CLAUDE.md version line) and prepends a v0.5.1159 CHANGELOG
entry. The only code change since v0.5.1158 is #4989, which fixes a path
traversal / arbitrary file write in perry publish (GHSA-x55v-q459-68ch).

After merge: tag v0.5.1159 + publish the GitHub release, then set the
advisory's patched version to 0.5.1159 and publish it.

Co-authored-by: Ralph Küpper <ralph@skelpo.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.

1 participant