{"record":{"id":"43664699a506b06e","repo":"Hmbown/CodeWhale","slug":"outbound-origin-is-empty-or-oversized","errorCode":null,"errorMessage":"outbound origin is empty or oversized","messagePattern":"outbound origin is empty or oversized","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/cloud_dispatch.rs","lineNumber":1260,"sourceCode":"    pub patch: String,\n}\n\n/// Validate an outbound origin for credential-bearing HTTP calls.\n///\n/// Rules:\n/// - `https` only for public hosts.\n/// - explicit loopback hosts (`localhost`, `127.0.0.1`, `::1`) are allowed\n///   only in debug builds, as the escape hatch for local smoke tests against\n///   a self-hosted sandbox service; release builds reject them outright.\n/// - the host must not be a private / link-local / reserved / multicast\n///   address or a `.local` / `.internal` name, and no userinfo may ride\n///   along.\n///\n/// DNS-resolved rebinding is out of scope and documented as such.\npub fn validate_outbound_origin(raw: &str) -> Result<reqwest::Url> {\n    let trimmed = raw.trim();\n    if trimmed.is_empty() || trimmed.len() > MAX_REMOTE_BYTES {\n        bail!(\"outbound origin is empty or oversized\");\n    }\n    let url = reqwest::Url::parse(trimmed).context(\"outbound origin is not a valid URL\")?;\n    if !matches!(url.scheme(), \"http\" | \"https\") {\n        bail!(\"outbound origin must be http or https\");\n    }\n    if !url.username().is_empty() || url.password().is_some() {\n        bail!(\"outbound origin must not embed credentials\");\n    }\n    let host = url\n        .host_str()\n        .context(\"outbound origin has no host\")?\n        .trim_end_matches('.')\n        .to_ascii_lowercase();\n    // `Url::host_str` keeps IPv6 brackets; strip them for the checks below.\n    let host = host\n        .strip_prefix('[')\n        .and_then(|inner| inner.strip_suffix(']'))\n        .map(str::to_string)","sourceCodeStart":1242,"sourceCodeEnd":1278,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/cloud_dispatch.rs#L1242-L1278","documentation":"validate_outbound_origin is the SSRF guard applied to credential-bearing outbound URLs (e.g. remote/cloud endpoints). This check rejects an origin that is empty after trimming or exceeds MAX_REMOTE_BYTES. It runs first, before URL parsing, so blank or absurdly long inputs fail with a clear message.","triggerScenarios":"Passing \"\", a whitespace-only string, or a URL longer than MAX_REMOTE_BYTES to validate_outbound_origin — e.g. an unset DAYTONA_API_URL / CWC_DAYTONA_ENDPOINT env var that was filtered to empty elsewhere, or a misconfigured toolbox_url.","commonSituations":"Missing remote-endpoint env var producing an empty string; pasting a URL with a trailing newline plus huge payload; config field accidentally set to the whole JSON blob.","solutions":["Set the remote origin env var (e.g. DAYTONA_API_URL) to a non-empty URL and re-run.","Trim and check length before passing the origin; ensure config loaders do not feed empty strings through.","Fix the config field holding an oversized value back to a normal origin string."],"exampleFix":"// before\nlet url = validate_outbound_origin(&env_val).unwrap();\n// after\nlet trimmed = env_val.trim();\nanyhow::ensure!(!trimmed.is_empty(), \"DAYTONA_API_URL must not be empty\");\nlet url = validate_outbound_origin(trimmed)?;","handlingStrategy":"validation","validationCode":"let raw = raw.trim();\nif raw.is_empty() || raw.len() > 2048 { return Err(\"origin must be a non-empty, reasonably sized URL\"); }","typeGuard":null,"tryCatchPattern":"match validate_outbound_origin(raw) {\n    Err(e) if e.to_string().contains(\"empty or oversized\") => eprintln!(\"remote origin unset or invalid: {e}\"),\n    Err(e) => return Err(e),\n    Ok(url) => url,\n}","preventionTips":["Never leave the remote-endpoint env var unset/empty in packaged environments.","Trim config values on load.","Fail loud at startup if the origin is missing rather than at request time."],"tags":["validation","ssrf","url","config"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}