Skip to content

Make path traversal in Orbit more robust - #46570

Merged
juan-fdz-hawa merged 7 commits into
mainfrom
14263-security-improvement-go-file-handling-issue
Jun 2, 2026
Merged

Make path traversal in Orbit more robust#46570
juan-fdz-hawa merged 7 commits into
mainfrom
14263-security-improvement-go-file-handling-issue

Conversation

@juan-fdz-hawa

@juan-fdz-hawa juan-fdz-hawa commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Make path traversal in Orbit more robust.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced security for file downloads by validating and sanitizing filenames from server responses. The system now prevents path traversal vulnerabilities by properly handling directory paths and special references, ensuring downloaded files always save to the intended destination directory.
  • Tests

    • Added test coverage for file path validation to verify secure handling of downloaded filenames.

Make path traversal in Orbit more robust.
@juan-fdz-hawa
juan-fdz-hawa requested a review from a team as a code owner June 1, 2026 16:40

@claude claude 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.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.

Tip: disable this comment in your organization's Code Review settings.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0f8e58a-5323-40e1-b083-a9e5b0ccb6fb

📥 Commits

Reviewing files that changed from the base of the PR and between 5a7c9e9 and da74b87.

📒 Files selected for processing (1)
  • orbit/changes/14263-security-improvement-go-file-handling-issue
✅ Files skipped from review due to trivial changes (1)
  • orbit/changes/14263-security-improvement-go-file-handling-issue

Walkthrough

This PR hardens client-side file handling by preventing path traversal attacks through HTTP Content-Disposition headers. The FileResponse.Handle method now sanitizes filenames by extracting only the base name component and rejecting traversal patterns (.., .) and empty strings, falling back to a supplied fallback filename or a generated UUID. Accompanying tests verify that traversal attempts are neutralized while normal filenames are preserved, and all destination paths remain within the intended directory. A changelog entry documents the security improvement.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal and lacks required details like related issue, checklist confirmation, testing information, and other mandatory sections from the template. Complete the PR description using the repository template: add related issue number, check relevant security/testing checklist items, document test coverage, and confirm manual QA.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: improving robustness of path traversal handling in Orbit through filename sanitization and validation.
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.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 14263-security-improvement-go-file-handling-issue

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 and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
client/base_client_test.go (1)

212-261: ⚡ Quick win

Add one case for the filename="." fallback path.

This suite exercises .., but the new logic also special-cases empty/. names. A small test with DestFile set would pin that branch and catch regressions in the new fallback behavior.

🤖 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 `@client/base_client_test.go` around lines 212 - 261, Add a new subtest in
TestFileResponseHandlePathTraversal that exercises the fallback path when
Content-Disposition contains filename=".": create fr := &FileResponse{DestPath:
destDir, DestFile: "fallback.txt"} and a resp whose Header has
`Content-Disposition: []string{`attachment;filename="."`}` then call
fr.Handle(resp) and assert no error, that filepath.Base(fr.DestFilePath) ==
"fallback.txt", and that fr.DestFilePath is inside destDir; this pins the
FileResponse.Handle branch that special-cases empty/"." names and verifies the
DestFile fallback behavior.
🤖 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 `@server/service/software_installers.go`:
- Line 623: Normalize backslashes in the uploaded filename before deriving the
download basename: in the code paths that set Content-Disposition (references:
r.payload.Filename used at the Content-Disposition header setting lines and the
similar usage around line 1078), replace Windows separators (`\`) with `/` (or
use path.Base from the "path" package which treats `/`) and then call
filepath.Base (or path.Base) on the normalized string; update the header
assignment to use the sanitized basename so backslash-containing names cannot
leak into the header.

---

Nitpick comments:
In `@client/base_client_test.go`:
- Around line 212-261: Add a new subtest in TestFileResponseHandlePathTraversal
that exercises the fallback path when Content-Disposition contains filename=".":
create fr := &FileResponse{DestPath: destDir, DestFile: "fallback.txt"} and a
resp whose Header has `Content-Disposition: []string{`attachment;filename="."`}`
then call fr.Handle(resp) and assert no error, that
filepath.Base(fr.DestFilePath) == "fallback.txt", and that fr.DestFilePath is
inside destDir; this pins the FileResponse.Handle branch that special-cases
empty/"." names and verifies the DestFile fallback behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c19977fe-e031-4467-85ba-f9ad897f8d8c

📥 Commits

Reviewing files that changed from the base of the PR and between 5313984 and 97d9666.

📒 Files selected for processing (3)
  • client/base_client.go
  • client/base_client_test.go
  • server/service/software_installers.go

Comment thread server/service/software_installers.go Outdated
@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 66.86%. Comparing base (9032883) to head (da74b87).
⚠️ Report is 18 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #46570   +/-   ##
=======================================
  Coverage   66.85%   66.86%           
=======================================
  Files        2809     2809           
  Lines      223741   223734    -7     
  Branches    11347    11347           
=======================================
+ Hits       149592   149601    +9     
+ Misses      60562    60547   -15     
+ Partials    13587    13586    -1     
Flag Coverage Δ
backend 68.59% <ø> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 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.

Comment thread client/base_client.go Outdated
Comment on lines +245 to +256
// Confirm the resolved path is still inside DestPath.
cleanDest, err := filepath.Abs(f.DestPath)
if err != nil {
return fmt.Errorf("resolving destination directory: %w", err)
}
cleanFile, err := filepath.Abs(f.DestFilePath)
if err != nil {
return fmt.Errorf("resolving destination file path: %w", err)
}
if cleanFile != cleanDest && !strings.HasPrefix(cleanFile, cleanDest+string(filepath.Separator)) {
return errors.New("invalid filename: path escapes destination directory")
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is this part protecting from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

After filepath.Join, verify the result path starts with the intended DestPath directory

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry. I'm still not following why this is needed.
If filename was already checked what else is this checking? (f.DestPath is not user-provided)

Comment thread client/base_client.go Outdated
Comment thread server/service/software_installers.go Outdated

@lucasmrod lucasmrod left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM.

  • Missing orbit/changes/ file.
  • Left one question around the changes in the orbit endpoints.
  • Also if we are keeping changes in server/service/software_installers.go then we need a changes/ file

Comment thread changes/14263-security-improvement-go-file-handling-issue Outdated
@juan-fdz-hawa
juan-fdz-hawa merged commit 18f1f10 into main Jun 2, 2026
41 checks passed
@juan-fdz-hawa
juan-fdz-hawa deleted the 14263-security-improvement-go-file-handling-issue branch June 2, 2026 11:33
@xpkoala

xpkoala commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

PR #46570 — Path traversal hardening validation

All four sub-tests in TestFileResponseHandlePathTraversal pass, and the full client package runs clean (ok ... 24.6s) with no regressions. A before/after reproduction confirms the fix contains both genuine escapes from the old code (../../../… and bare ..), while normal filenames pass through unchanged.

Payload Old (vulnerable) New (fixed)
../../../etc/cron.d/backdoor /etc/cron.d/backdoor ← escapes …/download/backdoor
/etc/cron.d/backdoor (absolute) …/download/etc/cron.d/backdoor …/download/backdoor
.. /var/orbit ← escapes …/download/FALLBACK
. / "" collapses to dest dir …/download/FALLBACK
/ dest dir (write fails, no traversal) dest dir (same) ✅
installer.pkg unchanged unchanged ✅

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.

3 participants