Skip to content

🛡️ Sentinel: [CRITICAL] Fix directory traversal vulnerability in static file serving#183

Open
Tuntii wants to merge 2 commits into
mainfrom
secure-static-files-traversal-17484869118111250650
Open

🛡️ Sentinel: [CRITICAL] Fix directory traversal vulnerability in static file serving#183
Tuntii wants to merge 2 commits into
mainfrom
secure-static-files-traversal-17484869118111250650

Conversation

@Tuntii
Copy link
Copy Markdown
Owner

@Tuntii Tuntii commented May 20, 2026

This PR secures the StaticFile serving module in rustapi-core against directory traversal and Local File Inclusion (LFI) vulnerabilities.

Vulnerability

Previously, the sanitize_path function attempted to prevent directory traversal by stripping literal .. segments. However, this could be bypassed using URL percent-encoding (e.g., %2e%2e%2f for ../), as the path was not decoded before sanitization. Depending on the routing layer configuration, this could allow attackers to access files outside the intended root directory.

Fix

This PR implements a robust defense-in-depth approach:

  1. URL Decoding: The incoming relative path is now explicitly decoded using percent_encoding::percent_decode_str before any processing occurs.
  2. Path Canonicalization: The system now utilizes tokio::fs::canonicalize on both the static file root directory and the requested file path.
  3. Boundary Verification: The server explicitly checks if the canonicalized file path starts_with the canonicalized root directory. If it doesn't, it returns a 404 Not Found. This completely eliminates the possibility of serving files outside the intended directory tree, regardless of symlinks or complex encoding tricks.

New tests have been added to verify that standard and percent-encoded traversal attempts are properly blocked.


PR created automatically by Jules for task 17484869118111250650 started by @Tuntii

This commit fixes a vulnerability where percent-encoded characters like
`%2e%2e%2f` could bypass the simple string-based `sanitize_path`
protection, potentially leading to Local File Inclusion (LFI).

The fix:
1. Adds `percent-encoding` to properly decode requested paths.
2. Uses `tokio::fs::canonicalize()` on both the root directory and the
   requested file path.
3. Explicitly verifies that the resolved, canonicalized file path
   starts with the canonicalized root directory.

This canonicalization check provides an iron-clad defense against all
forms of directory traversal.
Copilot AI review requested due to automatic review settings May 20, 2026 17:49
@google-labs-jules
Copy link
Copy Markdown
Contributor

đź‘‹ Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a đź‘€ emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown
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 rustapi-core static file serving against directory traversal/LFI by decoding percent-encoded paths and enforcing a canonicalized “must be under root” boundary check before serving files.

Changes:

  • Percent-decode the incoming relative path before sanitization in StaticFile::serve.
  • Add canonicalization + starts_with boundary enforcement in serve_file.
  • Add integration tests covering traversal attempts and encoded filenames; add percent-encoding dependency.

Reviewed changes

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

File Description
crates/rustapi-core/src/static_files.rs Adds percent-decoding and canonicalized root/file boundary verification to block traversal.
crates/rustapi-core/tests/static_files_security_test.rs Adds traversal/LFI regression tests and a percent-encoded filename test.
crates/rustapi-core/Cargo.toml Adds percent-encoding dependency.
Cargo.lock Locks the new dependency.

đź’ˇ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +290 to +294
// Security check: ensure the resolved path is within the root directory
let canonical_root = match tokio::fs::canonicalize(&config.root).await {
Ok(root) => root,
Err(_) => return Err(ApiError::internal("Static file root directory not found")),
};
assert!(res_encoded.is_err(), "Encoded traversal should be blocked");

// Double encoded
let relative_path_double = "%2e%2e%2f%2e%2e%2fetc%2fpasswd";
Comment on lines +296 to +300
let canonical_file = match tokio::fs::canonicalize(path).await {
Ok(file) => file,
Err(_) => return Err(ApiError::not_found("File not found")),
};

Comment on lines +7 to +10
let config = serve_dir("/static", "./"); // root is crates/rustapi-core

// We try to access something outside the root
let relative_path = "../../etc/passwd";
Comment on lines +37 to +40
let _ = std::fs::create_dir_all("./test_dir");
let _ = File::create("./test_dir/file with spaces.txt");

let config = serve_dir("/static", "./test_dir");
This commit fixes a vulnerability where percent-encoded characters like
`%2e%2e%2f` could bypass the simple string-based `sanitize_path`
protection, potentially leading to Local File Inclusion (LFI).

The fix:
1. Adds `percent-encoding` to properly decode requested paths.
2. Uses `tokio::fs::canonicalize().await` on both the root directory and the
   requested file path, maintaining async best practices.
3. Explicitly verifies that the resolved, canonicalized file path
   starts with the canonicalized root directory.

This canonicalization check provides an iron-clad defense against all
forms of directory traversal.
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.

2 participants