fix(axum): enforce mutation request boundaries
req: security/003
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
use axum::extract::State;
|
||||
use axum::http::{header, HeaderMap};
|
||||
use axum::extract::{DefaultBodyLimit, State};
|
||||
use axum::http::{header, HeaderMap, Request, StatusCode};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::{body::Body, routing::post, Router};
|
||||
use hemx_axum::{
|
||||
interactions, runtime_js, runtime_js_hash, runtime_js_path, runtime_js_route_path,
|
||||
runtime_js_script_src, runtime_js_source, DispatchRejection, EffectResponse, Form,
|
||||
@@ -10,6 +11,65 @@ use hemx_axum::{
|
||||
};
|
||||
use hemx_core::{push, BuildFingerprint, Handle, IntoEffect, SafeHtml, Slot};
|
||||
use scraper::{Html, Selector};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use tower::ServiceExt;
|
||||
|
||||
static MUTATION_CALLS: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
async fn bounded_mutation(_: InteractionRequest) -> StatusCode {
|
||||
MUTATION_CALLS.fetch_add(1, Ordering::SeqCst);
|
||||
StatusCode::NO_CONTENT
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn interaction_boundary_honors_media_type_and_host_body_limit() {
|
||||
// test req: security/003
|
||||
MUTATION_CALLS.store(0, Ordering::SeqCst);
|
||||
let app = Router::new()
|
||||
.route("/mutate", post(bounded_mutation))
|
||||
.layer(DefaultBodyLimit::max(32));
|
||||
|
||||
let unsupported = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::post("/mutate")
|
||||
.header(header::CONTENT_TYPE, "application/json")
|
||||
.body(Body::from(r#"{"__h":"1"}"#))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(unsupported.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
|
||||
assert_eq!(MUTATION_CALLS.load(Ordering::SeqCst), 0);
|
||||
|
||||
let oversized = app
|
||||
.clone()
|
||||
.oneshot(
|
||||
Request::post("/mutate")
|
||||
.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
|
||||
.body(Body::from(format!("__h=1&value={}", "x".repeat(64))))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(oversized.status(), StatusCode::PAYLOAD_TOO_LARGE);
|
||||
assert_eq!(MUTATION_CALLS.load(Ordering::SeqCst), 0);
|
||||
|
||||
let accepted = app
|
||||
.oneshot(
|
||||
Request::post("/mutate")
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
"application/x-www-form-urlencoded; charset=utf-8",
|
||||
)
|
||||
.body(Body::from("__h=1"))
|
||||
.unwrap(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(accepted.status(), StatusCode::NO_CONTENT);
|
||||
assert_eq!(MUTATION_CALLS.load(Ordering::SeqCst), 1);
|
||||
}
|
||||
|
||||
fn selector(value: &str) -> Selector {
|
||||
Selector::parse(value).expect("test selector parses")
|
||||
|
||||
Reference in New Issue
Block a user