CVE
CVE-2026-82455
CWE
CWE-59
Affected Surface
- RubyGems versions `>=3.2.30 <4.0.13`
- Developer workstations, CI runners, and build containers that install or unpack attacker-controlled gems into directories that may already contain symlinks
- Shared build caches, reused workspaces, and automation that extracts gems for inspection, vendoring, or packaging rather than only for direct local development
- Linux distributions and base images that package vulnerable RubyGems builds, including distro-maintained Ruby environments that lag the upstream 4.0.13 fix
The newest Ruby package-security issue in this window is not a malicious gem. It is a reminder that archive extraction is its own trust boundary.
CVE-2026-82455 was published on 29 August 2026 and affects RubyGems before 4.0.13. The bug is simple to state but easy to miss in review: RubyGems rejected obvious traversal entries such as absolute paths or .., yet it could still write outside the intended extraction root if part of the destination tree already existed as a symlink pointing elsewhere.
That sounds narrower than a typical supply-chain story, but the affected surface is real:
gem install / gem unpack / build automation
-> RubyGems extracts files into destination_dir
-> pre-existing symlink redirects a parent directory
-> file write lands outside destination_dir
If the destination is a reused workspace, shared cache, or CI volume, “outside destination_dir” can still mean “inside some other trusted part of the system.”
Affected versions and fix boundary
The affected component is RubyGems itself.
| Package | Affected versions | Fixed version |
|---|---|---|
rubygems | >=3.2.30 <4.0.13 | 4.0.13 |
The public CVE record, NVD entry, and upstream fix all agree on the core version boundary. Debian’s tracker also shows why Linux teams should not stop at upstream version numbers alone: distro-packaged RubyGems builds may still lag the fix even when the host otherwise looks current.
Why the old checks were not enough
The vulnerable behavior did not require a gem entry with an obvious ../escape path. RubyGems could reject that and still lose later.
The problem showed up when the apparent extraction path stayed under destination_dir, but one of its parent directories already existed as a symlink to a location outside the extraction root. In that case, a file that looked harmless on paper:
destination_dir/lib/owned.txt
could actually resolve to something like:
/tmp/escape/owned.txt
if destination_dir/lib was a pre-existing symlink to /tmp/escape.
That is why this is best understood as a containment bug after path resolution, not before it.
The upstream fix makes the missing check obvious
RubyGems patched the issue with a short realpath check in Gem::Package#extract_tar_gz:
real_mkdir = File.realpath(mkdir)
unless real_mkdir == destination_dir || normalize_path(real_mkdir).start_with?(normalize_path(destination_dir + "/"))
raise Gem::Package::PathError.new(real_mkdir, destination_dir)
end
That is the safety boundary RubyGems was missing. It was not enough to inspect the string form of the destination path. The code had to resolve the parent directory as the filesystem sees it and then ask a second question:
After symlink resolution, is this still inside destination_dir?
Before the patch, the answer was never rechecked. After the patch, extraction raises Gem::Package::PathError and stops.
The regression test shows the exploit shape clearly
The test added in the same patch is more instructive than a long threat model. It plants a symlink before extraction:
FileUtils.rm_rf File.join(@destination, "lib")
File.symlink escape_dir, File.join(@destination, "lib")
Then it confirms two things:
assert_raise Gem::Package::PathError do
package.extract_tar_gz tgz_io, @destination
end
refute File.exist?(escaped), "must not write outside extraction root via symlink"
That is the bug in one screen:
- a pre-existing symlink redirects a parent directory
- extraction follows it
- the fixed code raises instead of writing outside the root
Where this shows up in real application-security work
This is not a blanket “any use of RubyGems is remote code execution” claim. The attacker needs influence over both the gem content and an extraction environment whose directory tree can be shaped in advance or reused from prior jobs.
That still leaves several realistic paths:
- CI jobs that unpack gems into persistent workspaces or caches
- automation that mirrors, inspects, or vendors gems before later build steps
- multi-step build pipelines where a less-trusted earlier stage can plant symlinks in a directory later reused by Ruby tooling
- container images that share mounted volumes across jobs or stages
In those environments, a write outside the extraction root can become more than file corruption. It can target files that later influence build or deployment behavior.
Why Linux teams should care
This CVE is not Linux-only, but Linux-heavy build infrastructure makes the exposure pattern more common:
- shared CI volumes and bind mounts are common
- gem extraction often happens inside containerized build steps
- distro-packaged Ruby toolchains may not move in lockstep with upstream RubyGems
Debian’s tracker is a useful reminder here. Public distro status showed vulnerable packaged releases even after the upstream fix existed, which means “we use the OS package” is not the same as “we inherited 4.0.13 behavior.”
What to check
Start with the RubyGems version actually present on the machine or image:
ruby -e "require 'rubygems'; puts Gem::VERSION"
gem --version
Then look for extraction flows that reuse directories or caches:
rg -n "bundle install|gem install|gem unpack|vendor/cache|bundle package|cache" \
.github .gitlab-ci.yml Dockerfile docker-compose.yml scripts .
The question that matters is:
Can a less-trusted build step or dependency influence the destination tree
before RubyGems extracts files into it?
If the answer is yes, treat the extraction path as security-sensitive code, not a routine packaging detail.
Response guidance
- Upgrade RubyGems to
4.0.13or later, or move to a distro package that backports the fix. - Rebuild CI images and runner templates that bake in older Ruby environments.
- Avoid extracting gems into reused directories that can retain attacker-planted symlinks between jobs.
- Treat shared caches and mounted workspaces as part of the package trust boundary, especially in Linux build systems.
- If you maintain internal gem-processing automation, add your own post-resolution containment checks anywhere archive extraction writes to disk.
CVE-2026-82455 is a good example of why package security is not just about whether a dependency is malicious. Sometimes the package manager does the unsafe thing first. Here, the fix is small, but the lesson is larger: when archive extraction crosses a filesystem boundary, string-level path validation is not enough. You also need to validate where the filesystem will really write.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.