{"record":{"id":"d3e26d260deb1ec5","repo":"denoland/deno","slug":"argv-contains-nul","errorCode":null,"errorMessage":"argv contains NUL","messagePattern":"argv contains NUL","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/tools/desktop.rs","lineNumber":5737,"sourceCode":"\n  /// Flattened `(program, argv, envp, cwd)` for `posix_spawn`.\n  type SpawnArgs = (CString, Vec<CString>, Vec<CString>, Option<CString>);\n\n  /// Convert a std::process::Command into the argv/envp/cwd tuple posix_spawn\n  /// needs. Inherits the parent's env, then applies Command::env() overrides\n  /// (matching what std::process::Command does internally).\n  fn flatten(cmd: &std::process::Command) -> std::io::Result<SpawnArgs> {\n    let program = CString::new(cmd.get_program().as_bytes()).map_err(|_| {\n      std::io::Error::new(\n        std::io::ErrorKind::InvalidInput,\n        \"program path contains NUL\",\n      )\n    })?;\n    let mut argv: Vec<CString> = Vec::with_capacity(cmd.get_args().len() + 1);\n    argv.push(program.clone());\n    for a in cmd.get_args() {\n      argv.push(CString::new(a.as_bytes()).map_err(|_| {\n        std::io::Error::new(\n          std::io::ErrorKind::InvalidInput,\n          \"argv contains NUL\",\n        )\n      })?);\n    }\n    let mut env_map: std::collections::BTreeMap<OsString, OsString> =\n      std::env::vars_os().collect();\n    for (k, v) in cmd.get_envs() {\n      match v {\n        Some(v) => {\n          env_map.insert(k.to_os_string(), v.to_os_string());\n        }\n        None => {\n          env_map.remove(k);\n        }\n      }\n    }\n    let envp: Vec<CString> = env_map","sourceCodeStart":5719,"sourceCodeEnd":5755,"githubUrl":"https://github.com/denoland/deno/blob/f7822238cab635a3a19f99f493f675fa81a7f9d8/cli/tools/desktop.rs#L5719-L5755","documentation":"Same `flatten()` conversion for posix_spawn, applied to each argument in `cmd.get_args()`: any argv entry containing an interior NUL byte makes `CString::new` fail, mapped to `io::ErrorKind::InvalidInput` (\"argv contains NUL\"). C argv entries are NUL-terminated strings, so a 0x00 inside an argument is unrepresentable.","triggerScenarios":"A `Command::arg(...)` value containing 0x00 — commonly a string built from user input, a network payload, or a mis-decoded buffer — reaching the posix_spawn flattening path.","commonSituations":"CLI wrappers forwarding web/form input as arguments; data pipelines where a length-prefixed buffer was decoded as a plain string leaving trailing NULs; test fixtures with embedded control bytes.","solutions":["Run every externally sourced argument through a NUL check before adding it to the Command.","Reject (preferred) or strip 0x00 at the input boundary — argument values containing NUL are almost always corrupt input.","Add a unit test that feeds control bytes into your spawn wrapper to prove it fails closed."],"exampleFix":"// before\nfor arg in user_args { cmd.arg(arg); }\n\n// after\nfn reject_nul(bytes: &[u8], what: &str) -> std::io::Result<()> {\n  if bytes.contains(&0) {\n    return Err(std::io::Error::new(\n      std::io::ErrorKind::InvalidInput,\n      format!(\"{what} contains NUL\"),\n    ));\n  }\n  Ok(())\n}\nfor arg in &user_args {\n  reject_nul(arg.as_bytes(), \"argv\")?;\n  cmd.arg(arg);\n}","handlingStrategy":"validation","validationCode":"fn reject_nul(bytes: &[u8], what: &str) -> std::io::Result<()> {\n  if bytes.contains(&0) {\n    return Err(std::io::Error::new(\n      std::io::ErrorKind::InvalidInput,\n      format!(\"{what} contains NUL\"),\n    ));\n  }\n  Ok(())\n}\nfor a in cmd.get_args() {\n  reject_nul(a.as_bytes(), \"argv\")?;\n}","typeGuard":"fn is_nul_free(s: &std::ffi::OsStr) -> bool {\n  !s.as_bytes().contains(&0)\n}","tryCatchPattern":"match spawn(cmd) {\n  Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {\n    // identify which argument had 0x00 and reject the request\n  }\n  other => other,\n}","preventionTips":["Validate externally sourced arguments before adding them to Command","Encode binary payloads (base64/hex) instead of passing raw bytes as argv","Fail closed on control bytes in user-supplied command data"],"tags":["subprocess","posix-spawn","nul-byte","command-arguments","invalid-input"],"backgroundTag":"nul-byte-in-command-args","analyzedSha":"f7822238cab635a3a19f99f493f675fa81a7f9d8","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}