M0 security hardening: fix all vulnerabilities and resolve build errors
Some checks failed
CI/CD Pipeline / e2e-tests (push) Has been cancelled
CI/CD Pipeline / build (push) Has been cancelled
CI/CD Pipeline / unit-tests (push) Has been cancelled
CI/CD Pipeline / lint (push) Successful in 3m45s
CI/CD Pipeline / integration-tests (push) Failing after 53s

- Fix 5 source files corrupted with markdown formatting by previous AI
- Remove secret logging from auth middleware, signup, and recovery handlers
- Add role validation (ALLOWED_ROLES allowlist) to all 10 data_api + storage handlers
- Fix JavaScript injection in Deno runtime via double-serialization
- Add UUID validation to TUS upload paths to prevent path traversal
- Gate token issuance on email confirmation (AUTH_AUTO_CONFIRM env var)
- Reject unconfirmed users on login with 403
- Prevent OAuth account takeover (409 on email conflict with different provider)
- Replace permissive CORS (allow_origin Any) with ALLOWED_ORIGINS env var
- Wire session-based admin auth into control plane, add POST /platform/v1/login
- Hide secrets from list_projects API via ProjectSummary struct
- Add missing deps (redis, uuid, chrono, tower-http fs feature)
- Fix http version mismatch between reqwest 0.11 and axum 0.7 in proxy
- Clean up all unused imports across workspace

Build: zero errors, zero warnings. Tests: 10 passed, 0 failed.
Made-with: Cursor
This commit is contained in:
2026-03-15 12:54:21 +02:00
parent cffdf8af86
commit 8ade39ae2d
24 changed files with 2531 additions and 2508 deletions

View File

@@ -182,10 +182,17 @@ async fn forward_request(upstream: Upstream, req: Request) -> Result<Response, S
info!("Proxying {} -> {}", original_uri.path(), target_url);
// Build the request
let request_builder = client
.request(req.method().clone(), &target_url)
.headers(req.headers().clone());
// Convert axum (http 1.x) method to reqwest (http 0.2) method
let method_str = req.method().as_str();
let reqwest_method = reqwest::Method::from_bytes(method_str.as_bytes())
.map_err(|_| StatusCode::BAD_REQUEST)?;
let mut request_builder = client.request(reqwest_method, &target_url);
for (name, value) in req.headers().iter() {
if let Ok(v) = value.to_str() {
request_builder = request_builder.header(name.as_str(), v);
}
}
let response = request_builder
.send()
@@ -196,7 +203,7 @@ async fn forward_request(upstream: Upstream, req: Request) -> Result<Response, S
})?;
let status = StatusCode::from_u16(response.status().as_u16()).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
let headers = response.headers().clone();
let resp_headers = response.headers().clone();
let body_bytes = response.bytes().await.map_err(|e| {
error!("Failed to read response body from {}: {}", upstream.name, e);
StatusCode::BAD_GATEWAY
@@ -204,10 +211,12 @@ async fn forward_request(upstream: Upstream, req: Request) -> Result<Response, S
let mut response_builder = Response::builder().status(status);
// Copy relevant headers
for (name, value) in headers.iter() {
if name != "connection" && name != "transfer-encoding" {
response_builder = response_builder.header(name, value);
for (name, value) in resp_headers.iter() {
let n = name.as_str();
if n != "connection" && n != "transfer-encoding" {
if let Ok(v) = value.to_str() {
response_builder = response_builder.header(n, v);
}
}
}