{"record":{"id":"0f56fcd93ff14f84","repo":"Hmbown/CodeWhale","slug":"generated-mobile-fragment-is-a-valid-header","errorCode":null,"errorMessage":"generated mobile fragment is a valid header","messagePattern":"generated mobile fragment is a valid header","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_api.rs","lineNumber":1402,"sourceCode":"    let session = match mobile_state.consume_bootstrap(&nonce, peer.ip()) {\n        Ok(session) => session,\n        Err(mobile::BootstrapError::NonLoopback) => {\n            return secured_mobile_text(StatusCode::FORBIDDEN, \"bootstrap unavailable\");\n        }\n        Err(mobile::BootstrapError::Invalid | mobile::BootstrapError::Expired) => {\n            return secured_mobile_text(StatusCode::UNAUTHORIZED, \"bootstrap unavailable\");\n        }\n    };\n\n    let location = format!(\n        \"/mobile#request_proof={}&stream_ticket={}\",\n        session.request_proof, session.stream_ticket\n    );\n    let cookie = mobile::mobile_session_cookie(&session.session_cookie);\n    let mut response = (StatusCode::SEE_OTHER, \"\").into_response();\n    response.headers_mut().insert(\n        header::LOCATION,\n        HeaderValue::from_str(&location).expect(\"generated mobile fragment is a valid header\"),\n    );\n    response.headers_mut().insert(\n        header::SET_COOKIE,\n        HeaderValue::from_str(&cookie).expect(\"generated mobile cookie is a valid header\"),\n    );\n    secure_mobile_response(&mut response);\n    response\n}\n\nasync fn exchange_mobile_session(State(state): State<RuntimeApiState>, req: Request) -> Response {\n    let Some(mobile_state) = state.mobile.as_ref() else {\n        return mobile_not_found();\n    };\n    let Some(expected) = state.runtime_token.as_deref() else {\n        return mobile_not_found();\n    };\n    if !auth::request_has_header_runtime_token(&req, expected) {\n        return mobile_unauthorized();","sourceCodeStart":1384,"sourceCodeEnd":1420,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/runtime_api.rs#L1384-L1420","documentation":"The mobile login handler builds a redirect Location header from a session fragment and calls `HeaderValue::from_str(...).expect(...)`. `from_str` only fails for bytes outside visible HTTP header range (control chars), so this expect asserts the internally generated URL never contains such characters. A panic means the location string (base URL/path/fragment construction) produced an invalid header value.","triggerScenarios":"The generated mobile fragment URL contains control characters or non-visible bytes — e.g. a configured external base URL with a newline/CR, unvalidated query parameters interpolated into the fragment, or corrupted session data feeding the location.","commonSituations":"Misconfigured PUBLIC/base URL containing whitespace or CRLF, user-controlled fragments concatenated into the redirect target, template bugs introducing `%0A`/raw newlines.","solutions":["Audit where `location` is built and percent-encode or strip non-visible characters before insertion","Validate the configured base URL at startup (reject CRLF/control chars)","Replace expect with graceful handling that returns a 500 instead of panicking the handler","Add a unit test that the fragment builder output passes HeaderValue::from_str"],"exampleFix":"// before\nHeaderValue::from_str(&location).expect(\"generated mobile fragment is a valid header\")\n// after\nHeaderValue::from_str(&location).unwrap_or_else(|_| {\n    tracing::error!(\"invalid mobile location header\");\n    HeaderValue::from_static(\"/\")\n})","handlingStrategy":"validation","validationCode":"// rust\nfn is_valid_header_value(s: &str) -> bool {\n    s.bytes().all(|b| (32..=126).contains(&b) || b == b'\\t')\n}\n// call before building the response:\nassert!(is_valid_header_value(&location));","typeGuard":"// rust\nfn valid_header(s: &str) -> Option<HeaderValue> {\n    HeaderValue::from_str(s).ok()\n}","tryCatchPattern":"// rust\nmatch HeaderValue::from_str(&location) {\n    Ok(v) => response.headers_mut().insert(header::LOCATION, v),\n    Err(e) => { tracing::error!(\"bad location header: {e}\"); return StatusCode::INTERNAL_SERVER_ERROR.into_response(); }\n}","preventionTips":["Percent-encode any user input interpolated into redirect URLs","Validate configured base URLs reject control characters at startup","Unit-test header generation with CRLF inputs"],"tags":["http","header","panic","axum","rust"],"backgroundTag":"invalid-header-value","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}