diff --git a/.github/workflows/ci-post-merge.yml b/.github/workflows/ci-post-merge.yml index f135cd171..df6f21684 100644 --- a/.github/workflows/ci-post-merge.yml +++ b/.github/workflows/ci-post-merge.yml @@ -45,7 +45,7 @@ jobs: toolchain: ${{ matrix.version.version }} - name: Install cargo-hack - uses: taiki-e/install-action@v2.23.0 + uses: taiki-e/install-action@v2.23.7 with: tool: cargo-hack @@ -85,7 +85,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1.6.0 - name: Install cargo-hack - uses: taiki-e/install-action@v2.23.0 + uses: taiki-e/install-action@v2.23.7 with: tool: cargo-hack @@ -106,7 +106,7 @@ jobs: uses: actions-rust-lang/setup-rust-toolchain@v1.6.0 - name: Install nextest - uses: taiki-e/install-action@v2.23.0 + uses: taiki-e/install-action@v2.23.7 with: tool: nextest diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 721fc0378..81139fa23 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: toolchain: ${{ matrix.version.version }} - name: Install cargo-hack - uses: taiki-e/install-action@v2.23.0 + uses: taiki-e/install-action@v2.23.7 with: tool: cargo-hack diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 927ac86bb..810ea33dd 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -23,7 +23,7 @@ jobs: components: llvm-tools-preview - name: Install cargo-llvm-cov - uses: taiki-e/install-action@v2.23.0 + uses: taiki-e/install-action@v2.23.7 with: tool: cargo-llvm-cov diff --git a/actix-files/CHANGES.md b/actix-files/CHANGES.md index 15c2958f0..ac0fbfedc 100644 --- a/actix-files/CHANGES.md +++ b/actix-files/CHANGES.md @@ -2,6 +2,9 @@ ## Unreleased +## 0.6.4 + +- Fix handling of newlines in filenames. - Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency. ## 0.6.3 diff --git a/actix-files/Cargo.toml b/actix-files/Cargo.toml index efecb0889..b7a272515 100644 --- a/actix-files/Cargo.toml +++ b/actix-files/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-files" -version = "0.6.3" +version = "0.6.4" authors = [ "Nikolay Kim ", "Rob Ede ", diff --git a/actix-files/README.md b/actix-files/README.md index 3e656c431..d8d9e4f1f 100644 --- a/actix-files/README.md +++ b/actix-files/README.md @@ -3,11 +3,11 @@ > Static file serving for Actix Web [![crates.io](https://img.shields.io/crates/v/actix-files?label=latest)](https://crates.io/crates/actix-files) -[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.3)](https://docs.rs/actix-files/0.6.3) +[![Documentation](https://docs.rs/actix-files/badge.svg?version=0.6.4)](https://docs.rs/actix-files/0.6.4) ![Version](https://img.shields.io/badge/rustc-1.68+-ab6000.svg) ![License](https://img.shields.io/crates/l/actix-files.svg)
-[![dependency status](https://deps.rs/crate/actix-files/0.6.3/status.svg)](https://deps.rs/crate/actix-files/0.6.3) +[![dependency status](https://deps.rs/crate/actix-files/0.6.4/status.svg)](https://deps.rs/crate/actix-files/0.6.4) [![Download](https://img.shields.io/crates/d/actix-files.svg)](https://crates.io/crates/actix-files) [![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x) diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 943130e16..8ceb59bef 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -568,6 +568,27 @@ mod tests { assert_eq!(bytes, data); } + #[actix_rt::test] + async fn test_static_files_with_newlines() { + // Create the file we want to test against ad-hoc. We can't check it in as otherwise + // Windows can't even checkout this repository. + let temp_dir = tempfile::tempdir().unwrap(); + let file_with_newlines = temp_dir.path().join("test\nnewline.text"); + fs::write(&file_with_newlines, "Look at my newlines").unwrap(); + + let srv = test::init_service( + App::new().service(Files::new("/", temp_dir.path()).index_file("Cargo.toml")), + ) + .await; + let request = TestRequest::get().uri("/test%0Anewline.text").to_request(); + let response = test::call_service(&srv, request).await; + assert_eq!(response.status(), StatusCode::OK); + + let bytes = test::read_body(response).await; + let data = web::Bytes::from(fs::read(file_with_newlines).unwrap()); + assert_eq!(bytes, data); + } + #[actix_rt::test] async fn test_files_not_allowed() { let srv = test::init_service(App::new().service(Files::new("/", "."))).await; @@ -840,9 +861,9 @@ mod tests { #[actix_rt::test] async fn test_percent_encoding_2() { - let tmpdir = tempfile::tempdir().unwrap(); + let temp_dir = tempfile::tempdir().unwrap(); let filename = match cfg!(unix) { - true => "ض:?#[]{}<>()@!$&'`|*+,;= %20.test", + true => "ض:?#[]{}<>()@!$&'`|*+,;= %20\n.test", false => "ض#[]{}()@!$&'`+,;= %20.test", }; let filename_encoded = filename @@ -852,9 +873,9 @@ mod tests { write!(&mut buf, "%{:02X}", c).unwrap(); buf }); - std::fs::File::create(tmpdir.path().join(filename)).unwrap(); + std::fs::File::create(temp_dir.path().join(filename)).unwrap(); - let srv = test::init_service(App::new().service(Files::new("", tmpdir.path()))).await; + let srv = test::init_service(App::new().service(Files::new("/", temp_dir.path()))).await; let req = TestRequest::get() .uri(&format!("/{}", filename_encoded)) diff --git a/actix-files/src/named.rs b/actix-files/src/named.rs index d7795ba73..02dc701ea 100644 --- a/actix-files/src/named.rs +++ b/actix-files/src/named.rs @@ -24,7 +24,6 @@ use bitflags::bitflags; use derive_more::{Deref, DerefMut}; use futures_core::future::LocalBoxFuture; use mime::Mime; -use mime_guess::from_path; use crate::{encoding::equiv_utf8_text, range::HttpRange}; @@ -128,7 +127,7 @@ impl NamedFile { } }; - let ct = from_path(&path).first_or_octet_stream(); + let ct = mime_guess::from_path(&path).first_or_octet_stream(); let disposition = match ct.type_() { mime::IMAGE | mime::TEXT | mime::AUDIO | mime::VIDEO => DispositionType::Inline, @@ -140,7 +139,9 @@ impl NamedFile { _ => DispositionType::Attachment, }; - let mut parameters = vec![DispositionParam::Filename(String::from(filename.as_ref()))]; + // Replace newlines in filenames which could occur on some filesystems. + let filename_s = filename.replace('\n', "%0A"); + let mut parameters = vec![DispositionParam::Filename(filename_s)]; if !filename.is_ascii() { parameters.push(DispositionParam::FilenameExt(ExtendedValue {