{"record":{"id":"9b3e5c2c4ccba7e4","repo":"Hmbown/CodeWhale","slug":"generated-mobile-cookie-is-a-valid-header","errorCode":null,"errorMessage":"generated mobile cookie is a valid header","messagePattern":"generated mobile cookie is a valid header","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_api.rs","lineNumber":1406,"sourceCode":"        }\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();\n    }\n    mobile_session_response(mobile_state.issue_session())\n}\n","sourceCodeStart":1388,"sourceCodeEnd":1424,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/runtime_api.rs#L1388-L1424","documentation":"Same family as the Location expect: the handler sets a Set-Cookie header from `mobile::mobile_session_cookie(...)` and asserts via expect that the cookie string is a valid header value. `HeaderValue::from_str` fails only on control characters, so a panic means the generated cookie (session cookie content) contains illegal bytes such as newline or carriage return.","triggerScenarios":"`mobile_session_cookie` returns a cookie containing control characters — usually because the underlying session cookie value was corrupted, truncated mid-token, or constructed by concatenating unvalidated input.","commonSituations":"Upstream session store returning tainted values, manual cookie string manipulation, CRLF injection attempts reaching cookie construction, encoding bugs after a cookie format change.","solutions":["Sanitize/validate the session cookie value inside mobile_session_cookie before formatting","Reject control characters when loading the session cookie from the store","Swap expect for a controlled 500 response so a bad cookie cannot panic the worker","Test mobile_session_cookie against values containing \\r, \\n, and non-ASCII bytes"],"exampleFix":"// before\nHeaderValue::from_str(&cookie).expect(\"generated mobile cookie is a valid header\")\n// after\nHeaderValue::from_str(&cookie).unwrap_or_else(|_| {\n    tracing::error!(\"invalid mobile cookie header\");\n    HeaderValue::from_static(\"codewhale_mobile=\")\n})","handlingStrategy":"validation","validationCode":"// rust\nfn cookie_is_safe(c: &str) -> bool {\n    !c.is_empty() && c.bytes().all(|b| (33..=126).contains(&b))\n}\n// check before insert:\nassert!(cookie_is_safe(&cookie));","typeGuard":"// rust\nfn safe_cookie(s: &str) -> Option<HeaderValue> {\n    HeaderValue::from_str(s).ok()\n}","tryCatchPattern":"// rust\nif let Ok(v) = HeaderValue::from_str(&cookie) {\n    response.headers_mut().insert(header::SET_COOKIE, v);\n} else {\n    tracing::error!(\"invalid mobile cookie\");\n    return StatusCode::INTERNAL_SERVER_ERROR.into_response();\n}","preventionTips":["Validate session cookie values are visible ASCII when loading from the store","Never concatenate raw user input into cookie strings","Test cookie builders against \\r/\\n injection"],"tags":["http","cookie","header","panic","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"}