Skip to content

Improve static path security#3600

Merged
gaby merged 2 commits into2025-07-18-13-58-39from
2025-07-19-15-07-55
Jul 19, 2025
Merged

Improve static path security#3600
gaby merged 2 commits into2025-07-18-13-58-39from
2025-07-19-15-07-55

Conversation

@gaby
Copy link
Member

@gaby gaby commented Jul 19, 2025

Summary

  • add stricter sanitization of requested paths
  • expand path traversal tests for various malicious patterns
  • check env var errors and use constants in tests

Copilot AI review requested due to automatic review settings July 19, 2025 15:07
@gaby gaby requested a review from a team as a code owner July 19, 2025 15:08
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 19, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (3)
  • master
  • v2
  • main

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update introduces a new test for client proxy URL handling, centralizes and strengthens path sanitization logic in the static file middleware, and adds comprehensive path traversal security tests with platform-specific coverage for both Windows and non-Windows environments.

Changes

File(s) Change Summary
client/client_test.go Added Test_Client_SetProxyURL to verify proxy URL setting, request forwarding, and error handling.
middleware/static/static.go Introduced sanitizePath for path validation and replaced inline logic with centralized sanitization.
middleware/static/static_test.go Added constants and two test functions for path traversal security, covering both Windows and others.

Sequence Diagram(s)

sequenceDiagram
    participant Test as Test_Client_SetProxyURL
    participant ProxyServer
    participant HelperServer
    participant Client

    Test->>ProxyServer: Start proxy server (Fiber)
    Test->>HelperServer: Start helper server (Fiber)
    Test->>Client: Set proxy URL
    Client->>ProxyServer: Make request (should add isProxy header)
    ProxyServer->>HelperServer: Forward request
    HelperServer-->>ProxyServer: Respond
    ProxyServer-->>Client: Respond with isProxy header
    Test->>Client: Set invalid proxy URL
    Client->>ProxyServer: Make request (should fail)
Loading
sequenceDiagram
    participant StaticMiddleware
    participant sanitizePath
    participant fs

    StaticMiddleware->>sanitizePath: Receive requested path
    sanitizePath->>fs: (optional) Validate path with fs.ValidPath
    sanitizePath-->>StaticMiddleware: Return sanitized path or error
    StaticMiddleware-->>Client: Serve file or return 404/400
Loading

Possibly related PRs

Suggested labels

☢️ Bug, 🧹 Updates, v3

Suggested reviewers

  • sixcolors
  • efectn
  • ReneWerner87

Poem

In tunnels of code where the proxies hide,
And static paths must never collide,
We sanitize, we test, we block the sly—
No traversal escapes our vigilant eye!
With every patch and every test,
This code grows safer—hop, hop, the best! 🐇


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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.

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 focuses on bolstering the security of static file serving by introducing a comprehensive path sanitization mechanism. It aims to prevent various forms of path manipulation attacks, ensuring that only intended files within the designated root directory can be accessed. The changes are thoroughly validated with an extensive set of new and improved tests.

Highlights

  • Security Enhancement: Implemented a new sanitizePath function within the static file serving middleware to rigorously validate and clean incoming request paths. This prevents directory traversal attacks, null byte injections, and other malicious path manipulations by rejecting invalid patterns and normalizing paths.
  • Expanded Test Coverage: Significantly expanded the test suite for static file serving with new Test_Static_PathTraversal and Test_Static_PathTraversal_WindowsOnly functions. These tests cover a wide array of malicious path patterns, including various encodings, null bytes, and OS-specific traversal attempts, ensuring the new sanitization logic is robust.
  • Test Suite Improvements: Refactored existing tests to use constants for common string literals (e.g., windows, testdata/fs/css) for improved readability and maintainability. Additionally, environment variable handling in proxy tests was enhanced to ensure better test isolation and prevent side effects.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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 is currently in preview and 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 to provide feedback.

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 base branch from main to 2025-07-18-13-58-39 July 19, 2025 15:08
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 improves static path security by implementing stricter sanitization for path traversal attacks, expanding test coverage for various malicious patterns, and making minor code quality improvements.

  • Adds a new sanitizePath function that validates and cleans requested paths to prevent directory traversal attacks
  • Expands test coverage with comprehensive path traversal tests for both Unix and Windows environments
  • Refactors tests to use constants and fixes environment variable handling errors

Reviewed Changes

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

File Description
middleware/static/static.go Adds path sanitization function and integrates it into the path rewrite logic
middleware/static/static_test.go Introduces constants for test values and adds comprehensive path traversal security tests
client/client_test.go Fixes environment variable handling and corrects a typo in comments

}

// reject any null bytes or traversal attempts
if strings.Contains(s, "\x00") || strings.Contains(s, "..") || strings.Contains(s, ":") {
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

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

The colon character (:) check may be too broad and could block legitimate file names containing colons. Consider making this Windows-specific for drive letter detection or removing it if not necessary for security.

Suggested change
if strings.Contains(s, "\x00") || strings.Contains(s, "..") || strings.Contains(s, ":") {
if strings.Contains(s, "\x00") || strings.Contains(s, "..") || (runtime.GOOS == "windows" && strings.Contains(s, ":")) {

Copilot uses AI. Check for mistakes.
s = "/" + s
} else {
// verify no traversal after cleaning
if strings.Contains(raw, "..") || strings.Contains(s, "..") {
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

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

This check for '..' is redundant since it was already performed at line 42. The second check after path cleaning may be sufficient on its own.

Suggested change
if strings.Contains(raw, "..") || strings.Contains(s, "..") {
if strings.Contains(s, "..") {

Copilot uses AI. Check for mistakes.
require.NoError(t, err)
require.Contains(t, string(body), "Cannot GET",
"Blocked traversal should have a 'Cannot GET' message for %s", path)
} else {
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

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

The variable 'body' is used here but is not defined in this scope. It should be read from the response body similar to the 404 case above.

Suggested change
} else {
} else {
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)

Copilot uses AI. Check for mistakes.
require.Contains(t, string(body), "Cannot GET",
"Blocked traversal should have a Cannot GET message for %s", path)
} else {
require.Contains(t, string(body), "Are you a hacker?",
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

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

The error message 'Are you a hacker?' does not match the actual error messages that Fiber would return. This should likely be 'Cannot GET' or another appropriate error message that matches the actual implementation.

Suggested change
require.Contains(t, string(body), "Are you a hacker?",
require.Contains(t, string(body), "Cannot GET",

Copilot uses AI. Check for mistakes.
Comment on lines +1606 to +1611
require.NoError(t, os.Setenv("HTTP_PROXY", httpProxy))
require.NoError(t, os.Setenv("http_proxy", httpProxyLower))
require.NoError(t, os.Setenv("HTTPS_PROXY", httpsProxy))
require.NoError(t, os.Setenv("https_proxy", httpsProxyLower))
require.NoError(t, os.Setenv("NO_PROXY", noProxy))
require.NoError(t, os.Setenv("no_proxy", noProxyLower))
Copy link

Copilot AI Jul 19, 2025

Choose a reason for hiding this comment

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

Setting environment variables to empty string values when they were originally unset. Should check if the original value was empty and use os.Unsetenv() instead of os.Setenv() with empty string.

Suggested change
require.NoError(t, os.Setenv("HTTP_PROXY", httpProxy))
require.NoError(t, os.Setenv("http_proxy", httpProxyLower))
require.NoError(t, os.Setenv("HTTPS_PROXY", httpsProxy))
require.NoError(t, os.Setenv("https_proxy", httpsProxyLower))
require.NoError(t, os.Setenv("NO_PROXY", noProxy))
require.NoError(t, os.Setenv("no_proxy", noProxyLower))
if httpProxy == "" {
require.NoError(t, os.Unsetenv("HTTP_PROXY"))
} else {
require.NoError(t, os.Setenv("HTTP_PROXY", httpProxy))
}
if httpProxyLower == "" {
require.NoError(t, os.Unsetenv("http_proxy"))
} else {
require.NoError(t, os.Setenv("http_proxy", httpProxyLower))
}
if httpsProxy == "" {
require.NoError(t, os.Unsetenv("HTTPS_PROXY"))
} else {
require.NoError(t, os.Setenv("HTTPS_PROXY", httpsProxy))
}
if httpsProxyLower == "" {
require.NoError(t, os.Unsetenv("https_proxy"))
} else {
require.NoError(t, os.Setenv("https_proxy", httpsProxyLower))
}
if noProxy == "" {
require.NoError(t, os.Unsetenv("NO_PROXY"))
} else {
require.NoError(t, os.Setenv("NO_PROXY", noProxy))
}
if noProxyLower == "" {
require.NoError(t, os.Unsetenv("no_proxy"))
} else {
require.NoError(t, os.Setenv("no_proxy", noProxyLower))
}

Copilot uses AI. Check for mistakes.
@codecov
Copy link

codecov bot commented Jul 19, 2025

Codecov Report

❌ Patch coverage is 25.00000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.89%. Comparing base (5d802f2) to head (22fa00b).

Files with missing lines Patch % Lines
middleware/static/static.go 25.00% 3 Missing ⚠️
Additional details and impacted files
@@                   Coverage Diff                   @@
##           2025-07-18-13-58-39    #3600      +/-   ##
=======================================================
+ Coverage                90.86%   90.89%   +0.02%     
=======================================================
  Files                      111      111              
  Lines                    11252    11252              
=======================================================
+ Hits                     10224    10227       +3     
+ Misses                     770      768       -2     
+ Partials                   258      257       -1     
Flag Coverage Δ
unittests 90.89% <25.00%> (+0.02%) ⬆️

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

@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 improves static path security with stricter sanitization and expanded tests. However, the sanitization logic is overly strict and may block legitimate paths. I've suggested changes to rely on standard library functions for path cleaning. Additionally, I've identified a typo in an environment variable and an incorrect use of t.Parallel() in the tests.

require.NoError(t, os.Setenv("no_proxy", noProxyLower))
})

t.Parallel()
Copy link
Contributor

Choose a reason for hiding this comment

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

high

This test modifies global state (environment variables), so it should not be run in parallel with other tests at the top level. Calling t.Parallel() here can lead to race conditions and flaky tests if other tests depend on the same environment variables. Please remove this line. The sub-tests within this function can still run in parallel with each other. This is a high severity issue because it can lead to unpredictable test results and make it difficult to identify the root cause of failures.

Comment on lines +42 to +44
if strings.Contains(s, "\x00") || strings.Contains(s, "..") || strings.Contains(s, ":") {
return nil, errors.New("invalid path")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The check strings.Contains(s, "..") is too restrictive and will reject valid relative paths that normalize to a path within the static root, for example, /css/../js/script.js. This can break legitimate use cases. The path traversal security should primarily rely on path.Clean and fs.ValidPath, which correctly handle canonicalization. I recommend removing the .. check from this line. The added tests are also missing a case for this valid scenario. This is a high severity issue because it can lead to unexpected behavior and prevent users from accessing valid resources.

Suggested change
if strings.Contains(s, "\x00") || strings.Contains(s, "..") || strings.Contains(s, ":") {
return nil, errors.New("invalid path")
}
if strings.Contains(s, "\x00") || strings.Contains(s, ":") {
return nil, errors.New("invalid path")
}

Comment on lines +60 to +62
if strings.Contains(raw, "..") || strings.Contains(s, "..") {
return nil, errors.New("invalid path")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

This check is also overly restrictive. The check on raw (the un-cleaned path) will reject valid relative paths. The check on s (the cleaned path) is redundant because path.Clean on an absolute path will resolve all .. segments. The security is already handled by fasthttp.FS which ensures the final path is within the root directory. This block should be removed to avoid breaking valid use cases. This is a high severity issue because it can lead to unexpected behavior and prevent users from accessing valid resources.

@gaby gaby merged commit 9fdd1a8 into 2025-07-18-13-58-39 Jul 19, 2025
11 checks passed
@gaby gaby deleted the 2025-07-19-15-07-55 branch July 19, 2025 15:12
@gaby gaby added the 📜 RFC Compliance Feature, implementation, or contribution adheres to relevant RFC standards. label Nov 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex 📜 RFC Compliance Feature, implementation, or contribution adheres to relevant RFC standards.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants