{"id":"53941aa976395a82","repo":"actix/actix-web","slug":"invalid-character-in-chunk-extension","errorCode":null,"errorMessage":"Invalid character in chunk extension","messagePattern":"Invalid character in chunk extension","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"actix-http/src/h1/chunked.rs","lineNumber":105,"sourceCode":"    }\n\n    fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            // LWS can follow the chunk size, but no more digits can come\n            b'\\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),\n            b';' => Poll::Ready(Ok(ChunkedState::Extension)),\n            b'\\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk size linear white space\",\n            ))),\n        }\n    }\n    fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),\n            // strictly 0x20 (space) should be disallowed but we don't parse quoted strings here\n            0x00..=0x08 | 0x0a..=0x1f | 0x7f => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid character in chunk extension\",\n            ))),\n            _ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions\n        }\n    }\n    fn read_size_lf(rdr: &mut BytesMut, size: u64) -> Poll<Result<ChunkedState, io::Error>> {\n        match byte!(rdr) {\n            b'\\n' if size > 0 => Poll::Ready(Ok(ChunkedState::Body)),\n            b'\\n' if size == 0 => Poll::Ready(Ok(ChunkedState::EndCr)),\n            _ => Poll::Ready(Err(io::Error::new(\n                io::ErrorKind::InvalidInput,\n                \"Invalid chunk size LF\",\n            ))),\n        }\n    }\n\n    fn read_body(","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/actix/actix-web/blob/937960ca67f20e14ffe2a075bf6d4593502be12c/actix-http/src/h1/chunked.rs#L87-L123","documentation":"io::Error(InvalidInput, \"Invalid character in chunk extension\") (chunked.rs:105-108) is returned by read_extension when a chunk extension (the part after ';' on the size line) contains a control character outside the permitted set. Allowed: printable ASCII plus tab; rejected: 0x00-0x08, 0x0a-0x1f, 0x7f. The hrs_chunk_extension_invalid test (chunked.rs:385-404) reproduces it.","triggerScenarios":"A chunk size line like '2;x\\nx\\r\\n' places a raw LF inside the extension, which read_extension flags. Quoted-string extension values with spaces are tolerated, but raw control bytes are not.","commonSituations":"A client injecting unescaped control bytes into a chunk extension, or an HTTP Request Smuggling attempt exploiting extension parsing.","solutions":["Sanitise or omit chunk extensions; they are optional and ignored by actix.","Reject or normalise Transfer-Encoding at a front proxy.","Update the peer encoder to quote/escape extension values per RFC 7230 §4.1.1."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Reject requests whose chunked body contains invalid extension bytes.\nuse actix_http::error::PayloadError;\nmatch payload.next().await {\n    Some(Err(PayloadError::Io(e))) if e.kind() == io::ErrorKind::InvalidInput => {\n        return HttpResponse::BadRequest().finish();\n    }\n    _ => {}\n}","preventionTips":["Omit chunk extensions unless you actually need them; actix ignores them anyway.","Reject HTTP Request Smuggling-style payloads at the edge.","Ensure proxies do not inject control bytes into extensions."],"tags":["http","transfer-encoding","chunked","actix-http","security"],"analyzedSha":"937960ca67f20e14ffe2a075bf6d4593502be12c","analyzedAt":"2026-08-06T01:15:46.978Z","schemaVersion":2}