fix(saas): 安全修复 — IDOR防护、SSRF防护、JWT密钥强制、错误信息脱敏、CORS配置化

- account: admin 权限守卫 (list_accounts/get_account/update_status/list_logs)
- relay: SSRF 防护 (禁止内网地址、限制 http scheme、30s 超时)
- config: 生产环境强制 ZCLAW_SAAS_JWT_SECRET 环境变量
- error: 500 错误不再泄露内部细节给客户端
- main: CORS 支持配置白名单 origins
- 全部 21 个测试通过 (7 unit + 14 integration)
This commit is contained in:
iven
2026-03-27 13:07:20 +08:00
parent 00a08c9f9b
commit 94bf387aee
9 changed files with 134 additions and 31 deletions

View File

@@ -18,7 +18,7 @@ async fn main() -> anyhow::Result<()> {
let db = init_db(&config.database.url).await?;
info!("Database initialized");
let state = AppState::new(db, config.clone());
let state = AppState::new(db, config.clone())?;
let app = build_router(state);
let listener = tokio::net::TcpListener::bind(format!("{}:{}", config.server.host, config.server.port))
@@ -34,10 +34,24 @@ fn build_router(state: AppState) -> axum::Router {
use tower_http::cors::{Any, CorsLayer};
use tower_http::trace::TraceLayer;
let cors = CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any);
use axum::http::HeaderValue;
let cors = {
let config = state.config.blocking_read();
if config.server.cors_origins.is_empty() {
CorsLayer::new()
.allow_origin(Any)
.allow_methods(Any)
.allow_headers(Any)
} else {
let origins: Vec<HeaderValue> = config.server.cors_origins.iter()
.filter_map(|o: &String| o.parse::<HeaderValue>().ok())
.collect();
CorsLayer::new()
.allow_origin(origins)
.allow_methods(Any)
.allow_headers(Any)
}
};
let public_routes = zclaw_saas::auth::routes();