Skip to content

Commit 6218b5e

Browse files
authored
Security Improvements (#148)
* improved security for static files * rate limiting security * security improvements for decompression and rate limiting * bumped version; improved xff and fwd headers parsing + limits and caps; optimized decompression limiting * Preserve static file fallback on missing asset * Fold static file fallback into single match * Move path sanitization into file/dir handler * changelog updates, added a few more unit tests * fixed a small warning
1 parent 6f45cb3 commit 6218b5e

File tree

21 files changed

+1479
-135
lines changed

21 files changed

+1479
-135
lines changed

CHANGELOG.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,13 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/).
77

8-
## Unreleased
9-
10-
- Updated `HttpBody::json`/`form` to return Result so serialization errors surface instead of being embedded into the body, and added an Error conversion for `serde_urlencoded` failures.
11-
- Response helper macros now propagate JSON/form serialization errors, and problem-details responses match on `HttpBody::json` to return `Err` on failure.
12-
- Added explicit `ok!(text: ... )`/`ok!(fmt: ...)` and `status!(text: ... )`/`status!(fmt: ... )` macros for `text/plain` responses and documented them alongside existing `ok!`/`status!` examples.
13-
- Removed unnecessary boxing for `HttpBody::full`, `HttpBody::json` and `HttpBody::form`
14-
- Added additional methods: `HttpBody::text`, `HttpBody::from_static`, `HttpBody::from_static_text`, `HttpBody::from_str` and `HttpBody::from_slice` for better control.
15-
- Improved performance for all producing response macros and `IntoResponse` implementations.
16-
- Improved overall extractors' performance + added additional validation for the cases when mixed Path with NamedPath and positional arguments.
17-
- Added cached query-like path string handling for PathArgs, including into_parts/from_parts, and preserved the cache when cloning to reduce recomputation in extractor/middleware flows.
18-
- Switched route/allow header internals to Arc<str> and cached Allow values on route nodes to avoid recomputing 405 headers, plus simplified HSTS formatting to avoid intermediate allocation.
19-
- Added cached query-arg parsing in HttpRequest/HttpRequestMut to reduce repeated query parsing across extractors.
8+
## 0.8.1
9+
10+
### Changed
11+
- HTTP/RFC compliance (#145)
12+
- `HttpBody` improvements (#146)
13+
- Performance Improvements (#147)
14+
- Security Improvements (#148)
2015

2116
## 0.8.0
2217

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Fast, simple, and high-performance web framework for Rust, built on top of
55
Volga is designed to make building HTTP services straightforward and explicit,
66
while keeping performance predictable and overhead minimal.
77

8-
[![latest](https://img.shields.io/badge/latest-0.8.0-blue)](https://crates.io/crates/volga)
8+
[![latest](https://img.shields.io/badge/latest-0.8.1-blue)](https://crates.io/crates/volga)
99
[![latest](https://img.shields.io/badge/rustc-1.90+-964B00)](https://crates.io/crates/volga)
1010
[![License: MIT](https://img.shields.io/badge/License-MIT-violet.svg)](https://github.com/RomanEmreis/volga/blob/main/LICENSE)
1111
[![Build](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml/badge.svg)](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml)
@@ -46,7 +46,7 @@ Volga is a good fit if you:
4646
### Dependencies
4747
```toml
4848
[dependencies]
49-
volga = "0.8.0"
49+
volga = "0.8.1"
5050
tokio = { version = "1", features = ["full"] }
5151
```
5252
### Simple request handler

volga-dev-cert/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ categories.workspace = true
1414
keywords = ["volga", "server", "https", "tls"]
1515

1616
[dependencies]
17-
rcgen = "0.14.6"
17+
rcgen = "0.14.7"
1818

1919
[dev-dependencies]
2020
serial_test = "3.3.1"

volga-dev-cert/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Volga Development Certificates
22
A Rust library for generating self-signed TLS certificates for local development.
33

4-
[![latest](https://img.shields.io/badge/latest-0.8.0-blue)](https://crates.io/crates/volga)
4+
[![latest](https://img.shields.io/badge/latest-0.8.1-blue)](https://crates.io/crates/volga)
55
[![latest](https://img.shields.io/badge/rustc-1.90+-964B00)](https://crates.io/crates/volga)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-violet.svg)](https://github.com/RomanEmreis/volga/blob/main/LICENSE)
77
[![Build](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml/badge.svg)](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml)

volga-di/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Volga DI
22
A standalone, flexible, and easy-to-configure DI container.
33

4-
[![latest](https://img.shields.io/badge/latest-0.8.0-blue)](https://crates.io/crates/volga)
4+
[![latest](https://img.shields.io/badge/latest-0.8.1-blue)](https://crates.io/crates/volga)
55
[![latest](https://img.shields.io/badge/rustc-1.90+-964B00)](https://crates.io/crates/volga)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-violet.svg)](https://github.com/RomanEmreis/volga/blob/main/LICENSE)
77
[![Build](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml/badge.svg)](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml)
@@ -15,12 +15,12 @@ A standalone, flexible, and easy-to-configure DI container.
1515
#### Standalone
1616
```toml
1717
[dependencies]
18-
volga-di = "0.8.0"
18+
volga-di = "0.8.1"
1919
```
2020
#### Part of Volga Web Framework
2121
```toml
2222
[dependencies]
23-
volga = { version = "0.8.0", features = ["di"] }
23+
volga = { version = "0.8.1", features = ["di"] }
2424
```
2525

2626
### Example

volga-macros/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ keywords = ["volga", "http", "web", "framework"]
1717
proc-macro = true
1818

1919
[dependencies]
20-
quote = "1.0.43"
20+
quote = "1.0.44"
2121
syn = { version = "2.0.114", features = ["full"] }
22-
proc-macro2 = "1.0.105"
22+
proc-macro2 = "1.0.106"
2323

2424
[features]
2525
default = []

volga-macros/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Volga Macros
22
Macros library for Volga Web Framework
33

4-
[![latest](https://img.shields.io/badge/latest-0.8.0-blue)](https://crates.io/crates/volga)
4+
[![latest](https://img.shields.io/badge/latest-0.8.1-blue)](https://crates.io/crates/volga)
55
[![latest](https://img.shields.io/badge/rustc-1.90+-964B00)](https://crates.io/crates/volga)
66
[![License: MIT](https://img.shields.io/badge/License-MIT-violet.svg)](https://github.com/RomanEmreis/volga/blob/main/LICENSE)
77
[![Build](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml/badge.svg)](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml)
@@ -13,6 +13,6 @@ Macros library for Volga Web Framework
1313
## Dependencies
1414
```toml
1515
[dependencies]
16-
volga = { version = "0.8.0", features = ["macros"] }
16+
volga = { version = "0.8.1", features = ["macros"] }
1717
```
1818

volga-rate-limiter/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A lightweight and efficient rate-limiting library for Rust.
55
This crate provides in-memory rate limiting algorithms designed
66
for high-performance HTTP services and middleware.
77

8-
[![latest](https://img.shields.io/badge/latest-0.8.0-blue)](https://crates.io/crates/volga)
8+
[![latest](https://img.shields.io/badge/latest-0.8.1-blue)](https://crates.io/crates/volga)
99
[![latest](https://img.shields.io/badge/rustc-1.90+-964B00)](https://crates.io/crates/volga)
1010
[![License: MIT](https://img.shields.io/badge/License-MIT-violet.svg)](https://github.com/RomanEmreis/volga/blob/main/LICENSE)
1111
[![Build](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml/badge.svg)](https://github.com/RomanEmreis/volga/actions/workflows/rust.yml)

volga/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ tokio-rustls = { version = "0.26.4", default-features = false, features = ["tls1
4949
tokio-tungstenite = { version = "0.28.0", optional = true }
5050
tracing = { version = "0.1.44", default-features = false, optional = true }
5151
twox-hash = { version = "2.1.2", optional = true }
52-
uuid = { version = "1.19.0", features = ["v4"], optional = true }
52+
uuid = { version = "1.20.0", features = ["v4"], optional = true }
5353

5454
# volga
5555
volga-dev-cert = { path = "../volga-dev-cert", version = "0.8.1", optional = true }

volga/src/app.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,28 @@ use crate::http::cors::CorsRegistry;
4545
use crate::auth::bearer::{BearerAuthConfig, BearerTokenService};
4646

4747
#[cfg(feature = "rate-limiting")]
48-
use crate::rate_limiting::GlobalRateLimiter;
48+
use {
49+
crate::rate_limiting::GlobalRateLimiter,
50+
std::collections::HashSet
51+
};
4952

5053
#[cfg(feature = "static-files")]
5154
pub use self::env::HostEnv;
5255

5356
#[cfg(feature = "http2")]
5457
pub use crate::limits::Http2Limits;
5558

59+
#[cfg(any(
60+
feature = "decompression-brotli",
61+
feature = "decompression-gzip",
62+
feature = "decompression-zstd",
63+
feature = "decompression-full"
64+
))]
65+
use crate::middleware::decompress::{
66+
ResolvedDecompressionLimits,
67+
DecompressionLimits
68+
};
69+
5670
#[cfg(feature = "static-files")]
5771
pub mod env;
5872
pub mod router;
@@ -121,6 +135,10 @@ pub struct App {
121135
#[cfg(feature = "rate-limiting")]
122136
pub(super) rate_limiter: Option<GlobalRateLimiter>,
123137

138+
/// Trusted proxies for rate limiting IP extraction
139+
#[cfg(feature = "rate-limiting")]
140+
pub(super) trusted_proxies: Option<HashSet<IpAddr>>,
141+
124142
/// Request/Middleware pipeline builder
125143
pub(super) pipeline: PipelineBuilder,
126144

@@ -133,6 +151,15 @@ pub struct App {
133151
#[cfg(feature = "http2")]
134152
pub(super) http2_limits: Http2Limits,
135153

154+
/// Limits for decompression middleware
155+
#[cfg(any(
156+
feature = "decompression-brotli",
157+
feature = "decompression-gzip",
158+
feature = "decompression-zstd",
159+
feature = "decompression-full"
160+
))]
161+
pub(super) decompression_limits: DecompressionLimits,
162+
136163
/// TCP connection parameters
137164
connection: Connection,
138165

@@ -234,6 +261,10 @@ pub(crate) struct AppInstance {
234261
#[cfg(feature = "rate-limiting")]
235262
pub(super) rate_limiter: Option<Arc<GlobalRateLimiter>>,
236263

264+
/// Trusted proxies for rate limiting IP extraction
265+
#[cfg(feature = "rate-limiting")]
266+
pub(super) trusted_proxies: Option<Arc<HashSet<IpAddr>>>,
267+
237268
/// HSTS configuration options
238269
#[cfg(feature = "tls")]
239270
pub(super) hsts: Option<HstsHeader>,
@@ -258,6 +289,15 @@ pub(crate) struct AppInstance {
258289
#[cfg(feature = "http2")]
259290
pub(super) http2_limits: Http2Limits,
260291

292+
/// Limits for decompression middleware
293+
#[cfg(any(
294+
feature = "decompression-brotli",
295+
feature = "decompression-gzip",
296+
feature = "decompression-zstd",
297+
feature = "decompression-full"
298+
))]
299+
pub(super) decompression_limits: ResolvedDecompressionLimits,
300+
261301
/// Default `Cache-Control` header value
262302
pub(super) cache_control: Option<HeaderValue>,
263303

@@ -305,12 +345,21 @@ impl TryFrom<App> for AppInstance {
305345
cors: app.cors,
306346
#[cfg(feature = "http2")]
307347
http2_limits: app.http2_limits,
348+
#[cfg(any(
349+
feature = "decompression-brotli",
350+
feature = "decompression-gzip",
351+
feature = "decompression-zstd",
352+
feature = "decompression-full"
353+
))]
354+
decompression_limits: app.decompression_limits.resolved(),
308355
#[cfg(feature = "static-files")]
309356
host_env: app.host_env,
310357
#[cfg(feature = "di")]
311358
container: app.container.build(),
312359
#[cfg(feature = "rate-limiting")]
313360
rate_limiter: app.rate_limiter.map(Arc::new),
361+
#[cfg(feature = "rate-limiting")]
362+
trusted_proxies: app.trusted_proxies.map(Arc::new),
314363
#[cfg(feature = "jwt-auth")]
315364
bearer_token_service,
316365
#[cfg(feature = "tracing")]
@@ -373,6 +422,8 @@ impl App {
373422
auth_config: None,
374423
#[cfg(feature = "rate-limiting")]
375424
rate_limiter: None,
425+
#[cfg(feature = "rate-limiting")]
426+
trusted_proxies: None,
376427
pipeline: PipelineBuilder::new(),
377428
connection: Default::default(),
378429
body_limit: Default::default(),
@@ -384,6 +435,13 @@ impl App {
384435
cache_control: None,
385436
#[cfg(feature = "http2")]
386437
http2_limits: Default::default(),
438+
#[cfg(any(
439+
feature = "decompression-brotli",
440+
feature = "decompression-gzip",
441+
feature = "decompression-zstd",
442+
feature = "decompression-full"
443+
))]
444+
decompression_limits: Default::default(),
387445
#[cfg(debug_assertions)]
388446
show_greeter: true,
389447
#[cfg(not(debug_assertions))]
@@ -938,4 +996,14 @@ mod tests {
938996

939997
assert_eq!(format!("{connection:?}"), "Connection { socket: 127.0.0.1:5000 }");
940998
}
999+
1000+
#[test]
1001+
fn it_sets_default_connection_if_ip_is_invalid() {
1002+
let connection: Connection = "invalid_ip".into();
1003+
1004+
#[cfg(target_os = "windows")]
1005+
assert_eq!(connection.socket, SocketAddr::from(([127, 0, 0, 1], 7878)));
1006+
#[cfg(not(target_os = "windows"))]
1007+
assert_eq!(connection.socket, SocketAddr::from(([0, 0, 0, 0], 7878)));
1008+
}
9411009
}

0 commit comments

Comments
 (0)