{"record":{"id":"e257248677790e31","repo":"GitoxideLabs/gitoxide","slug":"git-daemon-virtual-hosts-must-not-contain-nul-or-l","errorCode":null,"errorMessage":"git daemon virtual hosts must not contain NUL or LF","messagePattern":"git daemon virtual hosts must not contain NUL or LF","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-transport/src/client/git/mod.rs","lineNumber":44,"sourceCode":"    use crate::{Protocol, Service};\n\n    pub fn connect(\n        service: Service,\n        desired_version: Protocol,\n        path: &[u8],\n        virtual_host: Option<&(String, Option<u16>)>,\n        extra_parameters: &[(&str, Option<&str>)],\n    ) -> std::io::Result<BString> {\n        let path = gix_url::expand_path::for_shell(path.into());\n        let is_forbidden = |byte| matches!(byte, b'\\0' | b'\\n');\n        if path.iter().copied().any(is_forbidden) {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                \"git daemon repository paths must not contain NUL or LF\",\n            ));\n        }\n        if virtual_host.is_some_and(|(host, _)| host.bytes().any(is_forbidden)) {\n            return Err(std::io::Error::new(\n                std::io::ErrorKind::InvalidInput,\n                \"git daemon virtual hosts must not contain NUL or LF\",\n            ));\n        }\n\n        let mut out = bstr::BString::from(service.as_str());\n        out.push(b' ');\n        out.extend_from_slice(&path);\n        out.push(0);\n        if let Some((host, port)) = virtual_host {\n            out.push_str(\"host=\");\n            out.extend_from_slice(host.as_bytes());\n            if let Some(port) = port {\n                out.push_byte(b':');\n                out.push_str(format!(\"{port}\"));\n            }\n            out.push(0);\n        }","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-transport/src/client/git/mod.rs#L26-L62","documentation":"Like the path check, the `git://` transport rejects virtual host names (the `virtual_host` argument) containing NUL (`\\0`) or LF (`\\n`), since these bytes cannot appear in the protocol's `host=` extra parameter. Fails with `InvalidInput` before any network I/O.","triggerScenarios":"Calling `gix::connect()` with `Some((host, port))` as virtual host where `host` contains a NUL or newline byte.","commonSituations":"Parsing a virtual host from raw input (config lines, files) without trimming; string splitting bugs that keep a trailing newline in the host part.","solutions":["Trim the host string before passing it as a virtual host","Validate the host contains only printable characters (no NUL/LF) at parse time","Fix parsers that fail to strip line endings from config/input"],"exampleFix":"// before\nlet vhost = Some((line.to_string(), None)); // line ends with '\\n'\n// after\nlet vhost = Some((line.trim().to_string(), None));","handlingStrategy":"validation","validationCode":"fn validate_vhost(host: &str) -> Result<(), String> {\n    if host.bytes().any(|b| b == b'\\0' || b == b'\\n') {\n        Err(\"virtual host contains NUL or LF\".into())\n    } else { Ok(()) }\n}","typeGuard":null,"tryCatchPattern":"match gix::connect(url, gix::protocol::transport::Protocol::Git) {\n    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {\n        eprintln!(\"check virtual host input: {e}\");\n    }\n    other => other?,\n}","preventionTips":["Trim line endings from hosts parsed from config files","Validate hostnames against a printable-ASCII pattern before use","Never pass user-supplied raw lines as virtual host names"],"tags":["git","protocol","url-validation","input-validation"],"backgroundTag":"invalid-url-format","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}