Skip to content

Conversation

@gaby
Copy link
Member

@gaby gaby commented Jan 26, 2026

Motivation

  • Harden multipart upload handling by performing path normalization and writable/symlink probes at startup to avoid runtime directory creation and unexpected panics.
  • Replace ad-hoc, unexported error values with exported errors to provide consistent API-level error reporting for invalid upload paths and root-escape attempts.
  • Prefer github.com/gofiber/utils/v2 trimming helpers for reliable, high-performance string trimming in hot code paths.

Description

  • Add Config.RootDir and Config.RootFs and initialize per-app upload state in configureUploads() (uploadRootDir, uploadRootEval, uploadRootPath, uploadRootFSPrefix, uploadRootFSWriter) to probe/create roots at startup and verify writability.
  • Introduce validateUploadPath, isAbsUploadPath, containsDotDot, storageRootPrefix, cleanUploadRootPrefix, ensureUploadPathWithinRoot, ensureNoSymlinkFS, probeUploadDirWritable, and probeUploadFSWritable in upload.go to centralize path normalization, validation, and probes; use utils.TrimLeft instead of strings.TrimPrefix where appropriate.
  • Add exported errors ErrInvalidUploadPath and ErrUploadPathEscapesRoot to error.go and update all upload-related code to return those errors.
  • Harden DefaultCtx.SaveFile and DefaultCtx.SaveFileToStorage to validate and normalize the provided path, resolve it against the configured root (OS dir or RootFs prefix), reject traversal/absolute attempts, and write either to the RootFs via its OpenFile or to the pre-resolved RootDir path; add saveMultipartFileToFS helper for FS writer paths.
  • Add tests covering allowed and rejected paths and storage behavior (Test_Ctx_SaveFile, Test_Ctx_SaveFile_RootDirTraversal, Test_Ctx_SaveFileToStorage, Test_Ctx_SaveFileToStorage_RootDirTraversal), and update docs (docs/api/ctx.md, docs/api/fiber.md) to document relative-path requirement and root resolution behavior.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

Walkthrough

This PR introduces upload root filesystem configuration to Fiber. Three new Config fields (RootDir, RootPerms, RootFs) control upload destination and permissions. SaveFile and SaveFileToStorage gain comprehensive path validation, symlink protection, and filesystem abstraction support. New error types signal validation failures, with extensive test coverage and documentation additions.

Changes

Cohort / File(s) Summary
Config System
app.go, docs/api/fiber.md
Add three public Config fields: RootDir (base directory), RootPerms (creation permissions, default 0o750), RootFs (custom filesystem). Initialize RootPerms during app setup and wire configureUploads.
SaveFile Implementation
ctx.go, error.go
Enhance SaveFile and SaveFileToStorage with path validation, RootFs-backed storage, symlink protection, and storage key prefixing. Add ErrInvalidUploadPath and ErrUploadPathEscapesRoot error variables.
Upload Infrastructure
upload.go
New comprehensive module (332 lines) providing path validation (validateUploadPath, isAbsUploadPath, containsDotDot), symlink detection (ensureNoSymlinkFS), root containment checks (ensureUploadPathWithinRoot), filesystem abstraction support, and directory writability probing.
Core Tests
app_test.go, ctx_test.go, upload_test.go
Filesystem mocks and test doubles; path validation test suite (validateUploadPath, isAbsUploadPath, etc.); storage behavior verification with RootDir/RootFs; comprehensive 763-line upload handling test suite covering success/failure paths and symlink escapes.
Supporting Changes
listen_test.go, docs/api/ctx.md, docs/whats_new.md
Update logging imports to use Fiber's logger package; document path handling constraints in SaveFile/SaveFileToStorage; announce new Upload Root Permissions feature.

Sequence Diagram

sequenceDiagram
    participant Client
    participant SaveFile as SaveFile Handler
    participant Validator as Path Validator
    participant RootFS as RootFs/RootDir
    participant Storage as Storage Backend

    Client->>SaveFile: POST file with path
    SaveFile->>Validator: validateUploadPath(path)
    alt Invalid path
        Validator-->>SaveFile: ErrInvalidUploadPath
        SaveFile-->>Client: Error
    else Path valid
        Validator-->>SaveFile: Normalized path
        SaveFile->>RootFS: Check path within root
        alt Path escapes root
            RootFS-->>SaveFile: ErrUploadPathEscapesRoot
            SaveFile-->>Client: Error
        else Path contained
            RootFS-->>SaveFile: saveMultipartFileToFS()
            SaveFile->>Storage: SetWithContext(storageKey, data)
            Storage-->>SaveFile: Success
            SaveFile-->>Client: File saved
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • sixcolors
  • efectn

Poem

🐰 With RootDir as my burrow's ground,
And RootPerms to keep files sound,
The uploads hop on paths so true—
No "../" tricks will slip on through!
Symlinks blocked, escapes repelled with care,
Safe uploads blooming everywhere! 🌱✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.62% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely summarizes the main feature addition: support for RootDir and RootFs configuration options for upload handling.
Description check ✅ Passed The PR description comprehensively covers motivation, implementation details, API changes, and test/documentation updates, aligning well with the repository template.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@ReneWerner87 ReneWerner87 added this to v3 Jan 26, 2026
@ReneWerner87 ReneWerner87 added this to the v3 milestone Jan 26, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @gaby, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly improves the security and reliability of file uploads by introducing a comprehensive validation and normalization system for upload paths. It allows applications to define secure root directories or filesystems for uploads, preventing malicious path manipulations like directory traversal. The changes also standardize error reporting for invalid upload attempts and ensure that the upload environment is properly configured and writable at startup, leading to a more secure and predictable file handling mechanism.

Highlights

  • Enhanced Upload Security: Implemented robust path normalization and validation for multipart uploads to prevent path traversal and absolute path attacks, ensuring files are saved within designated boundaries.
  • Configurable Upload Roots: Introduced new configuration options, Config.RootDir and Config.RootFs, allowing developers to define a base directory or a filesystem interface for SaveFile and SaveFileToStorage operations.
  • Standardized Error Reporting: Replaced ad-hoc error values with exported errors (ErrInvalidUploadPath, ErrUploadPathEscapesRoot) for consistent and clear API-level error reporting related to upload path issues.
  • Startup Validation and Probes: Added logic to perform path normalization, writability checks, and symlink probes at application startup, preventing runtime panics and ensuring secure upload environments from the start.
  • Updated API and Documentation: Modified DefaultCtx.SaveFile and DefaultCtx.SaveFileToStorage to incorporate the new validation logic, and updated documentation to reflect the new path requirements and root resolution behavior.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gaby gaby changed the title 🐛 bug: expose upload path validation errors 🔥 feat: Add support for RootDir and RootFs Jan 26, 2026
@gaby gaby removed the ☢️ Bug label Jan 26, 2026
Copy link
Contributor

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

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 significantly enhances the security and robustness of file upload handling by introducing comprehensive path validation, root directory confinement, and symlink checks. The changes centralize upload logic into a new upload.go file, ensuring that all file saving operations (SaveFile and SaveFileToStorage) are hardened against path traversal and other malicious attempts. New configuration options (RootDir, RootFs) provide flexible control over upload destinations, and dedicated error types (ErrInvalidUploadPath, ErrUploadPathEscapesRoot) improve API-level error reporting. The accompanying tests thoroughly cover various scenarios, including valid and invalid paths, ensuring the new security measures function as intended. Overall, this is a well-executed and crucial security improvement.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens multipart upload handling by introducing configurable upload roots (RootDir, RootFs), centralized upload path validation/normalization, and stronger traversal/symlink protections for SaveFile and SaveFileToStorage, with corresponding tests and docs.

Changes:

  • Added Config.RootDir/Config.RootFs and configureUploads() to pre-resolve, create, and probe upload roots (OS directory or fs.FS-backed) at app startup.
  • Introduced upload.go helpers (validateUploadPath, ensureUploadPathWithinRoot, ensureNoSymlinkFS, etc.) and wired them into DefaultCtx.SaveFile / SaveFileToStorage to enforce relative, non-escaping paths and symlink checks.
  • Exported new upload errors (ErrInvalidUploadPath, ErrUploadPathEscapesRoot), updated docs for config and context APIs, and extended tests to cover allowed paths and root traversal rejection.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
upload.go New upload configuration and helper functions for path validation, root resolution, symlink and writability checks backing the updated upload behavior.
error.go Adds exported error values for invalid upload paths and root-escape attempts to standardize error reporting.
app.go Extends Config with RootDir/RootFs, stores derived upload root state, and calls configureUploads() during app initialization.
ctx.go Reimplements SaveFile and SaveFileToStorage to normalize/validate paths, enforce RootDir/RootFs boundaries, check symlinks, and write via either fasthttp or a custom fs.FS writer.
ctx_test.go Updates SaveFile tests to use RootDir and adds traversal/absolute path rejection tests for both disk and storage uploads, plus a recordingStorage helper.
docs/api/fiber.md Documents the new Config.RootDir and Config.RootFs options and their role in upload path resolution.
docs/api/ctx.md Documents the new path constraints for SaveFile and SaveFileToStorage (relative-only, no .. or absolute prefixes, enforced against configured roots).

@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

❌ Patch coverage is 78.74016% with 54 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.94%. Comparing base (4b70666) to head (09cb0c8).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
upload.go 82.74% 17 Missing and 17 partials ⚠️
ctx.go 62.96% 13 Missing and 7 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4035      +/-   ##
==========================================
- Coverage   91.21%   90.94%   -0.27%     
==========================================
  Files         119      120       +1     
  Lines       11117    11368     +251     
==========================================
+ Hits        10140    10339     +199     
- Misses        620      649      +29     
- Partials      357      380      +23     
Flag Coverage Δ
unittests 90.94% <78.74%> (-0.27%) ⬇️

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.

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.50.

Benchmark suite Current: ca7e850 Previous: 4b70666 Ratio
Benchmark_Ctx_IsProxyTrusted/NoProxyCheckParallel 8.319 ns/op 0 B/op 0 allocs/op 5.528 ns/op 0 B/op 0 allocs/op 1.50
Benchmark_Ctx_IsProxyTrusted/NoProxyCheckParallel - ns/op 8.319 ns/op 5.528 ns/op 1.50

This comment was automatically generated by workflow using github-action-benchmark.

@gaby
Copy link
Member Author

gaby commented Jan 27, 2026

/gemini review

Copy link
Contributor

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

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 robust security enhancements for file uploads by adding RootDir and RootFs configurations. The changes centralize path normalization, validation, and writability probes, effectively preventing path traversal and symlink attacks. The new upload.go file encapsulates this logic, and the comprehensive test suite demonstrates thorough coverage of various scenarios, including edge cases and error conditions. The documentation updates clearly explain the new features and security implications for users. Overall, this is a well-implemented and critical improvement for the framework's file upload capabilities.

@gaby gaby marked this pull request as ready for review January 27, 2026 15:28
@gaby gaby requested a review from a team as a code owner January 27, 2026 15:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@docs/api/ctx.md`:
- Around line 1713-1714: The note about "Storage keys are prefixed by the
configured root when one is set" is misplaced under the SaveFile section; move
that sentence to the SaveFileToStorage section (and remove it from the current
paragraph at lines near 1713), and also update the duplicate mention around
lines 1748-1749 so the storage-key prefixing detail only appears in the
SaveFileToStorage doc; ensure the SaveFile paragraph keeps only disk/path
constraints (relative paths, no .., resolved against Config.RootDir/RootFs) and
that SaveFileToStorage explains the root prefixed storage key behavior.
🧹 Nitpick comments (2)
app_test.go (2)

199-204: Prefer require assertions for consistency.

The test file predominantly uses require from testify for assertions. For consistency, consider using require.Equal instead of manual comparisons with t.Fatalf.

♻️ Suggested refactor
-			if tfs.mkdirPath != "uploads" {
-				t.Fatalf("expected RootFs prefix %q, got %q", "uploads", tfs.mkdirPath)
-			}
-			if tfs.mkdirPerm != tt.wantPerm {
-				t.Fatalf("expected RootPerms %o, got %o", tt.wantPerm, tfs.mkdirPerm)
-			}
+			require.Equal(t, "uploads", tfs.mkdirPath, "RootFs prefix mismatch")
+			require.Equal(t, tt.wantPerm, tfs.mkdirPerm, "RootPerms mismatch")

214-224: Prefer require assertions for consistency.

Similar to the previous test, this test should use require assertions for consistency with the rest of the test file.

♻️ Suggested refactor
-	if err != nil {
-		t.Fatalf("expected no error, got %v", err)
-	}
-
-	if !strings.HasPrefix(normalized.osPath, ".") {
-		t.Fatalf("expected os path %q to preserve leading dot", normalized.osPath)
-	}
-	if normalized.slashPath != ".hidden/file.txt" {
-		t.Fatalf("expected slash path %q, got %q", ".hidden/file.txt", normalized.slashPath)
-	}
+	require.NoError(t, err)
+	require.True(t, strings.HasPrefix(normalized.osPath, "."), "expected os path %q to preserve leading dot", normalized.osPath)
+	require.Equal(t, ".hidden/file.txt", normalized.slashPath)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 11 comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants