Skip to content

Fix path traversal in local tar base-image manifest parsing (CWE-22)#4522

Open
Dreamweaver156191 wants to merge 5 commits into
GoogleContainerTools:masterfrom
Dreamweaver156191:fix/tar-base-image-path-traversal
Open

Fix path traversal in local tar base-image manifest parsing (CWE-22)#4522
Dreamweaver156191 wants to merge 5 commits into
GoogleContainerTools:masterfrom
Dreamweaver156191:fix/tar-base-image-path-traversal

Conversation

@Dreamweaver156191

Copy link
Copy Markdown

Summary

Fixes a path traversal issue (CWE-22) in LocalBaseImageSteps.cacheDockerImageTar, reported to Google's OSS VRP as issue 536528963 (closed there only because Jib's current reward tier makes product vulnerabilities ineligible for a monetary reward — the maintainers did not dispute the finding and invited a direct PR).

The problem

When Jib containerizes using a local tar file as the base image (Jib.from(TarImage.at(path)), or the tar:// image reference), it parses the tar's manifest.json and resolves the Config and Layers string fields directly against the extraction directory via destination.resolve(...), with no path canonicalization or bounds-checking:

  • LocalBaseImageSteps.java — the Config path resolution
  • LocalBaseImageSteps.java — the gzip-sniff of the first layer
  • LocalBaseImageSteps.java — each Layers entry inside the processing loop

Since manifest.json is part of the tar's own (potentially untrusted) contents, a crafted tar can set a Layers entry to an absolute path or a path containing ../ segments, causing Jib to read an arbitrary file elsewhere on the host filesystem, gzip-compress it, and embed it as a real layer blob in the output image — with no check that the content matches the declared diffId. If that output image is later pushed to a registry, the file's contents are exposed.

This mirrors the "Zip Slip" bug class, except the malicious path arrives via a manifest field trusted verbatim rather than an archive entry name. Notably, tar extraction in this same code path (TarExtractor.extract) already defends against exactly this kind of escape — this fix brings the manifest-parsing step up to the same standard.

The fix

Adds a resolveManifestPath(Path destination, String manifestDeclaredName) helper that resolves the manifest-declared name and then canonicalizes and bounds-checks the result against the destination directory, throwing IOException if it would escape — the same pattern TarExtractor.extract already uses for tar entry names. All three unguarded resolve() call sites now go through this helper.

Testing

Manually verified with a proof-of-concept malicious tar whose manifest.json Layers array pointed at an absolute path to a file outside the tar. Before this fix, that file's contents were read and embedded in the output image. After this fix, cacheDockerImageTar throws IOException: Illegal file name in manifest.json, potential path traversal: ... instead.

Happy to add a JUnit test alongside this if preferred — didn't want to guess at the project's existing test fixture conventions without seeing them first.

@google-cla

google-cla Bot commented Jul 19, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces path traversal validation in LocalBaseImageSteps.java by resolving manifest-declared paths (such as the config file and layers) against the extraction directory and verifying they do not escape it. The review feedback suggests optimizing this validation by pre-computing the canonical destination path once and passing it to the resolution helper, rather than recalculating it repeatedly (especially inside loops). Additionally, it is recommended to make the helper method package-private and annotate it with @VisibleForTesting to facilitate unit testing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Dreamweaver156191 and others added 4 commits July 19, 2026 13:54
…s/LocalBaseImageSteps.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…s/LocalBaseImageSteps.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…s/LocalBaseImageSteps.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…s/LocalBaseImageSteps.java

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.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