release(secrets-mcp): 0.5.5 — 生产 CORS 显式 allow_methods,修复 tower-http 启动 panic
credentials + wildcard methods/headers 被 tower-http 禁止;生产环境改为 GET/POST/PATCH/DELETE/OPTIONS 白名单。
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2066,7 +2066,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secrets-mcp"
|
name = "secrets-mcp"
|
||||||
version = "0.5.4"
|
version = "0.5.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"askama",
|
"askama",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "secrets-mcp"
|
name = "secrets-mcp"
|
||||||
version = "0.5.4"
|
version = "0.5.5"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|||||||
@@ -250,12 +250,26 @@ fn production_allowed_headers() -> [axum::http::HeaderName; 5] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Production CORS allowed methods.
|
||||||
|
///
|
||||||
|
/// Keep this list explicit because tower-http rejects
|
||||||
|
/// `allow_credentials(true)` together with `allow_methods(Any)`.
|
||||||
|
fn production_allowed_methods() -> [axum::http::Method; 5] {
|
||||||
|
[
|
||||||
|
axum::http::Method::GET,
|
||||||
|
axum::http::Method::POST,
|
||||||
|
axum::http::Method::PATCH,
|
||||||
|
axum::http::Method::DELETE,
|
||||||
|
axum::http::Method::OPTIONS,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the CORS layer for the application.
|
/// Build the CORS layer for the application.
|
||||||
///
|
///
|
||||||
/// In production mode the origin is restricted to the BASE_URL origin
|
/// In production mode the origin is restricted to the BASE_URL origin
|
||||||
/// (scheme://host:port, path stripped) and credentials are allowed.
|
/// (scheme://host:port, path stripped) and credentials are allowed.
|
||||||
/// `allow_headers` uses an explicit whitelist to avoid the tower-http
|
/// `allow_headers` and `allow_methods` use explicit whitelists to avoid the
|
||||||
/// restriction on `allow_credentials(true)` + `allow_headers(Any)`.
|
/// tower-http restriction on `allow_credentials(true)` + wildcards.
|
||||||
///
|
///
|
||||||
/// In development mode all origins, methods and headers are allowed.
|
/// In development mode all origins, methods and headers are allowed.
|
||||||
fn build_cors_layer(base_url: &str, is_production: bool) -> CorsLayer {
|
fn build_cors_layer(base_url: &str, is_production: bool) -> CorsLayer {
|
||||||
@@ -272,7 +286,7 @@ fn build_cors_layer(base_url: &str, is_production: bool) -> CorsLayer {
|
|||||||
};
|
};
|
||||||
CorsLayer::new()
|
CorsLayer::new()
|
||||||
.allow_origin(allowed_origin)
|
.allow_origin(allowed_origin)
|
||||||
.allow_methods(Any)
|
.allow_methods(production_allowed_methods())
|
||||||
.allow_headers(production_allowed_headers())
|
.allow_headers(production_allowed_headers())
|
||||||
.allow_credentials(true)
|
.allow_credentials(true)
|
||||||
} else {
|
} else {
|
||||||
@@ -304,6 +318,16 @@ mod tests {
|
|||||||
assert!(names.contains(&"x-mcp-session"));
|
assert!(names.contains(&"x-mcp-session"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn production_cors_methods_include_all_required() {
|
||||||
|
let methods = production_allowed_methods();
|
||||||
|
assert!(methods.contains(&axum::http::Method::GET));
|
||||||
|
assert!(methods.contains(&axum::http::Method::POST));
|
||||||
|
assert!(methods.contains(&axum::http::Method::PATCH));
|
||||||
|
assert!(methods.contains(&axum::http::Method::DELETE));
|
||||||
|
assert!(methods.contains(&axum::http::Method::OPTIONS));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn production_cors_normalizes_base_url_with_path() {
|
fn production_cors_normalizes_base_url_with_path() {
|
||||||
let url = url::Url::parse("https://secrets.example.com/secrets/app").unwrap();
|
let url = url::Url::parse("https://secrets.example.com/secrets/app").unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user