Skip to content

🌱 Use os.Root to extract and read remote repository archives - #5353

Open
Thanatos9404 wants to merge 2 commits into
ossf:mainfrom
Thanatos9404:archive-os-root
Open

Thanatos9404 wants to merge 2 commits into
ossf:mainfrom
Thanatos9404:archive-os-root

Conversation

@Thanatos9404

Copy link
Copy Markdown

What kind of change does this PR introduce?

Hardening of the remote clients' archive handling, with no score changes apart from the edge cases below.

What is the current behavior?

The GitHub, GitLab and Azure DevOps clients rely on a lexical ZipSlip check before plain os calls when extracting, and getFile opens filepath.Join(tempDir, name) unchecked. Callers only pass names from ListFiles or constants.

What is the new behavior (if this is a feature change)?

Like #4785 did for localdir, getFile now uses os.OpenInRoot, and extraction creates entries through an os.Root on the temp dir, so THR05 no longer rests on the lexical check alone (its text is updated). The ZipSlip check still runs first, so its errors, the skipped entries and the file lists are unchanged. getFile now refuses names that leave the temp dir through .. or a symlink, and no longer opens names relative to the working directory when no tarball was downloaded. On Windows, an entry named like a reserved device such as NUL, or containing a colon, now makes extraction fail with an error.

  • Tests for the changes have been added (for bug fixes/features)

New tests in each client fail on main and pass with this change on Linux.

Which issue(s) this PR fixes

NONE

Special notes for your reviewer

This overlaps #5342 and #5125, so I'll resolve conflicts with whichever lands first. Happy to add an openspec change for the getFile and Windows behavior if you'd like one.

Does this PR introduce a user-facing change?

NONE

@codecov

codecov Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.63265% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.36%. Comparing base (353ed60) to head (517a75a).
⚠️ Report is 423 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #5353       +/-   ##
===========================================
+ Coverage   66.80%   77.36%   +10.56%     
===========================================
  Files         230      255       +25     
  Lines       16602    14480     -2122     
===========================================
+ Hits        11091    11203      +112     
+ Misses       4808     3276     -1532     
+ Partials      703        1      -702     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI 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.

🟡 Changes recommended

GitHub and GitLab tarball handling leaves input and gzip reader resources open on return paths, causing descriptor leaks and preventing temporary-directory cleanup on Windows.

2 open issues
What changed in this PR

This pull request hardens remote repository archive extraction and file access by rooting operations in temporary directories with Go’s os.Root APIs, preventing traversal through .. paths and symlinks.

Changes:

  • Uses rooted extraction and file reads for GitHub, GitLab, and Azure DevOps archives.
  • Retains lexical ZipSlip validation while enforcing root-relative filesystem access.
  • Adds traversal and symlink-escape tests and updates the THR05 documentation.
File Description
docs/​security/​threat-catalog.yaml Documents rooted archive extraction and file access.
clients/​gitlabrepo/​tarball.go Roots GitLab tar extraction and file reads.
clients/​gitlabrepo/​tarball_test.go Tests GitLab tar extraction and file-read path escapes.
clients/​githubrepo/​tarball.go Roots GitHub tar extraction and file reads.
clients/​githubrepo/​tarball_test.go Tests GitHub tar extraction and file-read path escapes.
clients/​azuredevopsrepo/​zip.go Roots Azure DevOps ZIP extraction and file reads.
clients/​azuredevopsrepo/​zip_test.go Tests Azure DevOps ZIP extraction and file-read path escapes.

Review effort: Lite

Comment on lines +179 to +183
root, err := os.OpenRoot(handler.tempDir)
if err != nil {
return fmt.Errorf("os.OpenRoot: %w", err)
}
defer root.Close()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 517a75a: in and gz are now closed by defers right after each one opens, and outFile is closed before returning when io.Copy fails. On Windows the temp directory can now be removed after extraction. I also rebased onto main so the e2e update from #5360 is included.

Comment on lines +222 to +226
root, err := os.OpenRoot(handler.tempDir)
if err != nil {
return fmt.Errorf("os.OpenRoot: %w", err)
}
defer root.Close()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Same fix here in 517a75a, including the outFile close on the io.Copy error path.

The GitHub, GitLab and Azure DevOps clients now create extracted files
and open them for GetFileReader through an os.Root on the temporary
directory, the same way the local directory client opens files since
it moved to os.OpenInRoot. The ZipSlip checks stay in place, so their
errors are unchanged.

Signed-off-by: Yashvardhan Thanvi <[email protected]>
extractTarball opened the downloaded tarball and a gzip reader but never
closed them, and an output file stayed open when io.Copy failed. On
Windows the open handle stopped cleanup from removing the temp directory.

Signed-off-by: Yashvardhan Thanvi <[email protected]>

This comment was marked as off-topic.

@justaugustus justaugustus added this to the v5.7.0 milestone Nov 3, 2026
@justaugustus justaugustus mentioned this pull request Nov 3, 2026
22 tasks
@davic09

davic09 commented Nov 6, 2026

Copy link
Copy Markdown

Did some testing. The new outsideTempDir tests fail on main and pass on the branch, and I ran against sylva-core and two of my own test projects with and without the change: scores and show-details output were identical for every check that ran. ZipSlip still runs before os.Root, so escaped names never reach it. I also fed a "../evil" entry to the GitHub, GitLab, and Azure DevOps extractors on main and on the branch, and all three return the same error as before and write nothing outside the temp dir, because the existing lexical path check still runs before the os.Root calls.

One caveat on my side: the symlink-escape subtests skip on my Windows box for lack of symlink privilege, so I could not exercise those myself.
One thing you may want to know about, which is not caused by this PR. On Windows the GitLab client builds the flattened CI path as tempDir + "/gitlabscorecard_flattened_ci.yaml" (gitlabrepo/tarball.go:164), so the TrimPrefix at line 181 never matches and the file list holds an absolute path. On main that already makes Binary-Artifacts and Pinned-Dependencies error out, and with this PR the same case now reports "path escapes from parent" instead. Switching line 164 to filepath.Join fixed it for me on both main and the branch. Fine to leave for a separate change though!

@Thanatos9404

Copy link
Copy Markdown
Author

Thanks for testing this so thoroughly, including the ../evil entries on all three extractors. Good catch on the GitLab flattened CI path on Windows. Since it already breaks on main, I'll leave it out of this PR, as you suggest, so this one stays about os.Root.

This branch was previously deployed

2 inactive deployments
integration-test — 517a75a4 Deployed Oct 24, 2026 by Thanatos9404 via integration-trusted #12841
gitlab — 517a75a4 Deployed Oct 24, 2026 by Thanatos9404 via gitlab-integration-trusted #5519
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Review in progress

Development

Successfully merging this pull request may close these issues.

4 participants