fix: strip runnable cors headers when the routers are unavailable

This commit is contained in:
hugocasa
2026-08-28 19:37:57 +02:00
parent ca447a0d4e
commit 2a7f03c86f
3 changed files with 25 additions and 7 deletions
@@ -255,9 +255,12 @@ async fn conditional_cors_middleware(
}
// Whether this path is restricted could not be determined, and
// `route_job` may still have served a restricted route behind this
// middleware. Emitting the permissive default would hand that response
// to any origin, so emit nothing at all.
CorsDecision::Unavailable => {}
// middleware. Emitting nothing is not enough: the runnable may have set
// its own `Access-Control-Allow-Origin` through `wm_headers`, and
// leaving that in place would hand the response to whatever it names.
CorsDecision::Unavailable => {
headers.remove(http::header::ACCESS_CONTROL_ALLOW_ORIGIN);
}
CorsDecision::Unrestricted => {
if !not_insert_origin {
headers.insert(
@@ -352,10 +352,11 @@ pub fn parse_allowed_origins_setting(
HTTP_ROUTE_DEFAULT_ALLOWED_ORIGINS_SETTING
))),
})
.collect::<crate::error::Result<Vec<_>>>()?
.into_iter()
.filter(|origin| !origin.is_empty())
.collect(),
// Not filtered for empties, unlike the string form: there a
// trailing separator naturally yields an empty token, whereas an
// empty array entry is something the caller wrote and validation
// should reject rather than silently drop.
.collect::<crate::error::Result<Vec<_>>>()?,
Some(_) => {
return Err(crate::error::Error::BadRequest(format!(
"{} expected to be a comma-separated string or an array of strings",
+14
View File
@@ -744,6 +744,20 @@ mod tests {
assert!(validate_allowed_origins(&[]).is_ok());
}
#[test]
fn test_parse_allowed_origins_setting_rejects_empty_array_entries() {
use windmill_common::global_settings::parse_allowed_origins_setting;
// A trailing separator in the string form is a typing artifact and is
// dropped; an empty array entry is something the caller wrote, so it
// must reach validation rather than be filtered away into an empty
// (and therefore unrestricted) default.
assert!(parse_allowed_origins_setting(Some(&serde_json::json!("https://a.com,"))).is_ok());
assert!(parse_allowed_origins_setting(Some(&serde_json::json!([""]))).is_err());
assert!(
parse_allowed_origins_setting(Some(&serde_json::json!(["https://a.com", ""]))).is_err()
);
}
#[test]
fn test_validate_allowed_origins_bounds_the_list() {
// An allowlist is scanned on every request to a restricted route, the