Skip to content

Fix tar slip path traversal in codesign archive extraction#2291

Open
rhamitarora wants to merge 3 commits into
openshift:mainfrom
rhamitarora:rhamitarora/ARO-26358-codeql-fix
Open

Fix tar slip path traversal in codesign archive extraction#2291
rhamitarora wants to merge 3 commits into
openshift:mainfrom
rhamitarora:rhamitarora/ARO-26358-codeql-fix

Conversation

@rhamitarora

@rhamitarora rhamitarora commented Jun 19, 2026

Copy link
Copy Markdown

Jira: ARO-26358
Jira: ARO-26359
Jira: ARO-26373

Summary by CodeRabbit

This PR fixes two CodeQL go/zipslip path-traversal alerts (SM03409, Jira ARO-26358, , Jira ARO-26359, Jira ARO-26373) where untrusted tar entry names containing .. were used in filesystem operations, allowing a malicious archive to write outside the intended extraction directory. In machoresign.go, extractTarToTmpAndSign now computes the clean temp dir once and rejects any entry whose resolved path escapes it via a single strings.HasPrefix boundary check before opening files. In archive.go, the existing containment guard in unpackLayer is moved above the parent-directory creation so it dominates every filesystem sink, with no behavioral change for valid archives. New unit tests cover both a malicious ../ entry (rejected, nothing written outside the dir) and a benign entry (extracted successfully). Validated with make verify plus builds and tests for both packages; since these are first-party sources, the issue is fixed in code rather than suppressed.

Release Notes

  • Bug Fixes

    • Enhanced archive extraction security across code-signing and image layer unpacking by adding stronger path traversal protections to ensure extracted files remain within the intended directory.
  • Tests
    Added unit tests covering both malicious archive entries (a ../ path that is rejected safely, with an assertion that nothing is written outside the extraction area) and valid entries (extracted as expected). Both affected packages build and pass: go test -tags 'include_gcs include_oss containers_image_openpgp gssapi' ./pkg/cli/image/archive/... internal..

  • Verification
    — Ran the full suite make verify (CLI conventions, build, generated completions, kube-version) — all green. Re-ran the CodeQL go/zipslip analysis against the branch after applying the changes and confirmed both alerts (machoresign.go:146 and archive.go:120) are now resolved, whereas the prior scan on main (oc-zipslip-main.sarif) still reported them — confirming the fix clears the issue.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed path traversal vulnerabilities in tar archive extraction to prevent malicious archives from writing files outside their intended extraction directory.
  • Tests

    • Added unit tests validating proper rejection of archives with path traversal attempts.

Validate that each tar entry resolves within the extraction
directory before writing, preventing a malicious archive from
escaping via '..' entries (CodeQL SM03409). Add unit tests
covering a traversal entry and a benign entry.

Jira: ARO-26358
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 19, 2026
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Walkthrough

The PR hardens two tar extraction paths against path traversal attacks. In extractTarToTmpAndSign, a cleaned temp directory baseline is computed once, and each tar entry's resolved path is validated to remain within it, with a descriptive error returned on escape. Unit tests verify both traversal rejection and normal extraction. In unpackLayer, an existing containment check is repositioned earlier in the header loop and redundant validation in whiteout handling is removed.

Changes

Tar-Slip Protection

Layer / File(s) Summary
Tar-slip guard implementation in extractTarToTmpAndSign
pkg/cli/admin/internal/codesign/machoresign.go
Adds fmt and strings imports; computes cleanTempDir once at function entry; checks each tar entry's resolved extraction path against cleanTempDir with a path-separator boundary, returning an error if any entry escapes the directory.
Unit tests for path traversal rejection and normal extraction
pkg/cli/admin/internal/codesign/machoresign_test.go
writeTarGz helper builds gzip-compressed tar archives from a name-to-content map. TestExtractTarToTmpAndSign_RejectsPathTraversal asserts failure and no sentinel file creation for a ../ entry. TestExtractTarToTmpAndSign_ExtractsRegularFile asserts success and file existence for a benign entry.
Path validation refactoring in unpackLayer
pkg/cli/image/archive/archive.go
Moves tar-entry path containment check earlier, before any filesystem operations; removes redundant path recomputation and re-validation in the whiteout handling section.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 14 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix tar slip path traversal in codesign archive extraction' accurately describes the main purpose of the PR, which is fixing zipslip/tar-slip path traversal vulnerabilities in tar archive extraction.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed All test names in the PR are stable and deterministic. The two Go test functions added (TestExtractTarToTmpAndSign_RejectsPathTraversal and TestExtractTarToTmpAndSign_ExtractsRegularFile) use stati...
Test Structure And Quality ✅ Passed PR contains standard Go unit tests (testing.T), not Ginkgo tests. The custom check targets Ginkgo test patterns (Describe/It blocks, BeforeEach/AfterEach, Eventually/Consistently), which do not app...
Microshift Test Compatibility ✅ Passed This PR adds only standard Go unit tests (machoresign_test.go), not Ginkgo e2e tests. The custom check specifically applies to "new Ginkgo e2e tests," which are absent here.
Single Node Openshift (Sno) Test Compatibility ✅ Passed PR adds standard Go unit tests (func TestXxx(t *testing.T)), not Ginkgo e2e tests. Check only applies to Ginkgo e2e tests (It/Describe/Context/When patterns).
Topology-Aware Scheduling Compatibility ✅ Passed PR modifies only utility code (code signing and archive extraction) with no deployment manifests, operator code, controllers, or scheduling constraints that would require topology-aware scheduling...
Ote Binary Stdout Contract ✅ Passed All modified files are internal utility packages with no stdout writes in process-level code. fmt imports used only for fmt.Errorf (error creation), not stdout. No init(), main(), or Ginkgo test su...
Ipv6 And Disconnected Network Test Compatibility ✅ Passed This PR adds standard Go unit tests (using testing.T), not Ginkgo e2e tests. The custom check is specifically for Ginkgo e2e tests that may have IPv4/IPv6 or external connectivity issues; it does n...
No-Weak-Crypto ✅ Passed PR introduces no weak cryptography. All changes are path traversal security fixes using strings.HasPrefix validation; no MD5, SHA1, DES, RC4, 3DES, Blowfish, ECB, custom crypto, or non-constant-tim...
Container-Privileges ✅ Passed PR contains only Go source code fixes for path traversal security in tar extraction; no K8s manifests or container privilege configurations present.
No-Sensitive-Data-In-Logs ✅ Passed The PR introduces only two fmt.Errorf calls that log archive entry names and filesystem directory paths - no passwords, tokens, API keys, PII, session IDs, hostnames, or customer data are exposed.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@openshift-ci openshift-ci Bot added the needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test. label Jun 19, 2026
@openshift-ci

openshift-ci Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Hi @rhamitarora. Thanks for your PR.

I'm waiting for a openshift member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@openshift-ci

openshift-ci Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: rhamitarora
Once this PR has been reviewed and has the lgtm label, please assign ardaguclu for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/cli/admin/internal/codesign/machoresign.go (1)

125-166: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clean up MkdirTemp directories on error returns in extraction.

The new rejection branch returns "" (Line 165), so callers cannot remove the created temp dir. Repeated invalid archives can accumulate stale dirs under /tmp.

Suggested fix
-func extractTarToTmpAndSign(path string) (string, error) {
+func extractTarToTmpAndSign(path string) (retDir string, retErr error) {
 	tempDir, err := os.MkdirTemp(os.TempDir(), "oc-release-extract-*")
 	if err != nil {
 		return "", err
 	}
+	defer func() {
+		if retErr != nil {
+			_ = os.RemoveAll(tempDir)
+		}
+	}()
 	cleanTempDir := filepath.Clean(tempDir)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/cli/admin/internal/codesign/machoresign.go` around lines 125 - 166, The
function extractTarToTmpAndSign creates a temporary directory with os.MkdirTemp
but does not clean it up when returning errors, causing stale directories to
accumulate. Add a deferred cleanup function immediately after creating tempDir
that removes the directory if the function exits with an error, or alternatively
add explicit os.RemoveAll(tempDir) calls before each error return statement
(including the tar slip validation check at line 165 that validates
tempExtractionPath).
🧹 Nitpick comments (1)
pkg/cli/admin/internal/codesign/machoresign_test.go (1)

48-90: ⚡ Quick win

Refactor these two cases into a single table-driven test.

Both cases share setup/execution and differ only by archive entries and expected outcome; consolidating into table-driven form will align with repo test conventions and reduce duplication.

As per coding guidelines, "Unit tests should use co-located *_test.go files with table-driven tests and standard testing.T."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/cli/admin/internal/codesign/machoresign_test.go` around lines 48 - 90,
Consolidate the two test functions
TestExtractTarToTmpAndSign_RejectsPathTraversal and
TestExtractTarToTmpAndSign_ExtractsRegularFile into a single table-driven test.
Create a test cases slice where each case contains a name, the archive entries
to pass to writeTarGz, a boolean indicating whether an error is expected, and
assertions to verify (such as checking if a file exists or doesn't exist).
Replace both test functions with a single test function that iterates over the
test cases, executing the common setup and extractTarToTmpAndSign call for each
case, then running the case-specific assertions.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@pkg/cli/admin/internal/codesign/machoresign.go`:
- Around line 125-166: The function extractTarToTmpAndSign creates a temporary
directory with os.MkdirTemp but does not clean it up when returning errors,
causing stale directories to accumulate. Add a deferred cleanup function
immediately after creating tempDir that removes the directory if the function
exits with an error, or alternatively add explicit os.RemoveAll(tempDir) calls
before each error return statement (including the tar slip validation check at
line 165 that validates tempExtractionPath).

---

Nitpick comments:
In `@pkg/cli/admin/internal/codesign/machoresign_test.go`:
- Around line 48-90: Consolidate the two test functions
TestExtractTarToTmpAndSign_RejectsPathTraversal and
TestExtractTarToTmpAndSign_ExtractsRegularFile into a single table-driven test.
Create a test cases slice where each case contains a name, the archive entries
to pass to writeTarGz, a boolean indicating whether an error is expected, and
assertions to verify (such as checking if a file exists or doesn't exist).
Replace both test functions with a single test function that iterates over the
test cases, executing the common setup and extractTarToTmpAndSign call for each
case, then running the case-specific assertions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 4c193e4d-3228-4069-9072-61ccb5d72ed0

📥 Commits

Reviewing files that changed from the base of the PR and between c639a31 and d753496.

📒 Files selected for processing (2)
  • pkg/cli/admin/internal/codesign/machoresign.go
  • pkg/cli/admin/internal/codesign/machoresign_test.go

Move the path-containment check in unpackLayer above the parent
directory creation so the '..' sanitizer dominates every filesystem
operation (CodeQL go/zipslip at archive.go). Simplify the codesign
extraction guard to a single strings.HasPrefix so the static
analyzer recognizes it as a sanitizer barrier.

Jira: ARO-26358

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/cli/image/archive/archive.go`:
- Around line 147-155: The containment check in the filepath validation logic
only checks if the relative path `rel` starts with ".." followed by a path
separator, but misses the exact case where `rel` equals exactly "..". Modify the
condition that currently uses `strings.HasPrefix(rel,
".."+string(os.PathSeparator))` to also explicitly check for the equality case
`rel == ".."` by combining both conditions with an OR operator, ensuring that
both the exact ".." case and the prefix case that indicates path traversal are
caught before the breakoutError is returned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 1f61c187-1842-418c-82f8-af4c777ff6c5

📥 Commits

Reviewing files that changed from the base of the PR and between d753496 and 9710f19.

📒 Files selected for processing (2)
  • pkg/cli/admin/internal/codesign/machoresign.go
  • pkg/cli/image/archive/archive.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/cli/admin/internal/codesign/machoresign.go

Comment thread pkg/cli/image/archive/archive.go
@rhamitarora rhamitarora marked this pull request as ready for review June 23, 2026 09:41
@openshift-ci openshift-ci Bot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jun 23, 2026
@openshift-ci openshift-ci Bot requested review from atiratree and tchap June 23, 2026 09:42
filepath.Rel returns exactly ".." (no trailing separator) when an
entry normalizes to the parent of dest, which the HasPrefix-only
guard missed, allowing a directory escape. Also reject rel == "..".

Jira: ARO-26358
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ok-to-test Indicates a PR that requires an org member to verify it is safe to test.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant