From 3b7614a8cdbd4a746ecf2f428e82e6d7c06ea97a Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 12 Jun 2026 21:46:25 -0400 Subject: [PATCH] more tests --- crates/lib/actix_cors_configs/src/cors.rs | 37 ++++++++++++++++++++ crates/lib/actix_cors_configs/src/testing.rs | 16 +++++++++ 2 files changed, 53 insertions(+) diff --git a/crates/lib/actix_cors_configs/src/cors.rs b/crates/lib/actix_cors_configs/src/cors.rs index 3587528a48..59c3fb9e7e 100644 --- a/crates/lib/actix_cors_configs/src/cors.rs +++ b/crates/lib/actix_cors_configs/src/cors.rs @@ -88,6 +88,7 @@ fn do_build_cors_config(is_production: bool) -> Cors { mod tests { use server_environment::ServerEnvironment; + use crate::testing::assert_no_origin_header_ok; use crate::testing::assert_origin_invalid; use crate::testing::assert_origin_ok; use crate::testing::assert_preflight_method_invalid; @@ -95,6 +96,42 @@ mod tests { use super::build_cors_config; + /// Every production origin that hosts a product (or IS a backend that + /// serves same-origin pages like swagger) must be allowed. This is the + /// guard against `block_on_origin_mismatch(true)` locking out a real + /// frontend after a CORS-crate upgrade. + #[actix_rt::test] + async fn test_production_product_and_backend_origins_are_allowed() { + let production_cors = build_cors_config(ServerEnvironment::Production); + + let origins = [ + // Backends (same-origin browser requests still carry an Origin header). + "https://api.fakeyou.com", + "https://api.storyteller.ai", + // Products + "https://fakeyou.com", + "https://storyteller.ai", + "https://app.getartcraft.com", + "https://getartcraft.com", + "https://www.getartcraft.com", + "https://artcraft.ai", + "https://www.artcraft.ai", + "https://artcraft-dashboard.netlify.app", + ]; + for origin in origins { + assert_origin_ok(&production_cors, origin).await; + } + } + + /// Non-browser clients (curl, python API clients, server-to-server) send + /// no Origin header and must never be blocked, even with + /// `block_on_origin_mismatch(true)`. + #[actix_rt::test] + async fn test_requests_without_origin_header_are_not_blocked() { + let production_cors = build_cors_config(ServerEnvironment::Production); + assert_no_origin_header_ok(&production_cors).await; + } + #[actix_rt::test] async fn test_preflight_allows_every_supported_method() { let production_cors = build_cors_config(ServerEnvironment::Production); diff --git a/crates/lib/actix_cors_configs/src/testing.rs b/crates/lib/actix_cors_configs/src/testing.rs index e0fb2de907..83a6eb73cd 100644 --- a/crates/lib/actix_cors_configs/src/testing.rs +++ b/crates/lib/actix_cors_configs/src/testing.rs @@ -38,6 +38,22 @@ pub (crate) async fn assert_preflight_method_invalid(cors: &Cors, hostname: &str .is_equal_to(StatusCode::BAD_REQUEST); } +/// Assert that a request WITHOUT an Origin header (curl, server-to-server, +/// native API clients) is never blocked by CORS, regardless of +/// `block_on_origin_mismatch`. +pub (crate) async fn assert_no_origin_header_ok(cors: &Cors) { + let cors = cors.new_transform(test::ok_service()) + .await + .unwrap(); + + let request = TestRequest::default().to_srv_request(); + let response = test::call_service(&cors, request).await; + + asserting("Requests without an Origin header are not blocked") + .that(&response.status()) + .is_equal_to(StatusCode::OK); +} + async fn make_test_request(cors: &Cors, hostname: &str) -> ServiceResponse> { let cors= cors.new_transform(test::ok_service()) .await