{"record":{"id":"677f723a651157e3","repo":"vercel/turborepo","slug":"port-validation-failed-e","errorCode":null,"errorMessage":"Port validation failed: {e}","messagePattern":"Port validation failed: (.+?)","errorType":"exception","errorClass":"std::io::Error","httpStatus":null,"severity":"error","filePath":"crates/turborepo-microfrontends-proxy/src/http.rs","lineNumber":51,"sourceCode":"    .await;\n\n    handle_forward_result(result, path, route_match, remote_addr, http_client, \"HTTP\").await\n}\n\npub(crate) async fn forward_request(\n    mut req: Request<Incoming>,\n    app_name: &str,\n    port: u16,\n    remote_addr: SocketAddr,\n    http_client: HttpClient,\n) -> Result<Response<Incoming>, Box<dyn std::error::Error + Send + Sync>> {\n    // Validate port to prevent SSRF attacks\n    validate_port(port).map_err(|e| {\n        warn!(\n            \"Port validation failed for {} (port {}): {}\",\n            app_name, port, e\n        );\n        Box::new(std::io::Error::new(\n            std::io::ErrorKind::PermissionDenied,\n            format!(\"Port validation failed: {e}\"),\n        )) as Box<dyn std::error::Error + Send + Sync>\n    })?;\n\n    let target_uri = format!(\n        \"http://localhost:{}{}\",\n        port,\n        req.uri()\n            .path_and_query()\n            .map(|pq| pq.as_str())\n            .unwrap_or(\"/\")\n    );\n\n    let original_host = validated_host_header(&req)?.to_string();\n\n    let headers = req.headers_mut();\n    headers.insert(\"Host\", format!(\"localhost:{port}\").parse()?);","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/crates/turborepo-microfrontends-proxy/src/http.rs#L33-L69","documentation":"Before proxying a request to an app's dev server, handle_request (http.rs:51) runs validate_port from ports.rs: only ports 3000-9999 are allowed, and a blocklist (22, 23, 25, 110, 143, 443, 3306, 5432, 6379, 27017) is checked first even inside that range. The failure is wrapped as PermissionDenied 'Port validation failed: {e}' with the concrete reason, as an SSRF guard so the proxy can never be pointed at system services.","triggerScenarios":"A microfrontends app configured with a dev-server port of 80/443/8080? (8080 is fine) — concretely: port < 3000 (e.g. 80, 300), port > 9999 (e.g. 10000, 30000 for HMR), or a blocked service port like 3306 (MySQL) or 6379 (Redis) that sits inside the allowed range.","commonSituations":"Apps started with --port 80 or 443 in containers HMR/websocket ports configured above 9999 Someone pointing the proxy at a local database 'just to see'","solutions":["Move the app's dev server into 3000-9999 (e.g. 3306 -> 5433-style change; 80 -> 3000) and update the microfrontends config","Keep HMR/WS ports inside the range too","If you genuinely need another port, file a feature request — the range is a deliberate security policy, not a bug"],"exampleFix":"# before (turbo.json microfrontends proxy target)\n\"dev\": { \"port\": 3306 }\n# after\n\"dev\": { \"port\": 5433 }","handlingStrategy":"validation","validationCode":"// validate before assigning the proxy target port\nuse turborepo_microfrontends_proxy::ports::validate_port; // or replicate:\nfn port_ok(p: u16) -> bool { (3000..=9999).contains(&p) && ![22,23,25,110,143,443,3306,5432,6379,27017].contains(&p) }\nassert!(port_ok(app_port));","typeGuard":"fn is_allowed_dev_port(p: u16) -> bool {\n    (3000..=9999).contains(&p) && !BLOCKED_PORTS.contains(&p)\n}","tryCatchPattern":"// surfaced as Box<dyn Error> from handle_request — match on the PermissionDenied inner io::Error\nif let Some(io) = err.downcast_ref::<std::io::Error>() {\n    if io.kind() == std::io::ErrorKind::PermissionDenied && io.to_string().contains(\"Port validation\") {\n        return respond_502_with_config_hint(app_name); // tell user to fix the port\n    }\n}","preventionTips":["Standardize dev-server ports to 3000-9999 in repo tooling","Include HMR/WS ports in config review","Treat blocklist hits as config bugs, never bypass them"],"tags":["proxy","microfrontends","ssrf","port","security"],"backgroundTag":"ssrf-port-blocked","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}