{"record":{"id":"fb99ede2641f6b6a","repo":"denoland/deno","slug":"batch-file-arguments-are-invalid","errorCode":null,"errorMessage":"batch file arguments are invalid","messagePattern":"batch file arguments are invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"runtime/subprocess_windows/src/process.rs","lineNumber":1504,"sourceCode":"  if script.contains(&(b'\"' as u16)) || script.last() == Some(&(b'\\\\' as u16)) {\n    return Err(std::io::Error::new(\n      io::ErrorKind::InvalidInput,\n      \"Windows file names may not contain `\\\"` or end with `\\\\`\",\n    ));\n  }\n  cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script));\n  cmd.push(b'\"' as u16);\n\n  // Append the arguments.\n  // FIXME: This needs tests to ensure that the arguments are properly\n  // reconstructed by the batch script by default.\n  for arg in args.iter().skip(1) {\n    cmd.push(' ' as u16);\n    let arg_bytes = arg.as_encoded_bytes();\n    // Disallow \\r and \\n as they may truncate the arguments.\n    const DISALLOWED: &[u8] = b\"\\r\\n\";\n    if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) {\n      return Err(std::io::Error::new(\n        io::ErrorKind::InvalidInput,\n        r#\"batch file arguments are invalid\"#,\n      ));\n    }\n    append_bat_arg(&mut cmd, arg, force_quotes)?;\n  }\n\n  // Close the quote we left opened earlier.\n  cmd.push(b'\"' as u16);\n\n  Ok(cmd)\n}\n\n// lifted from https://github.com/rust-lang/rust/blob/bc1d7273dfbc6f8a11c0086fa35f6748a13e8d3c/library/std/src/sys/args/windows.rs#L220C1-L291C2\n// Copyright The Rust Project Contributors - MIT\nfn append_bat_arg(\n  cmd: &mut Vec<u16>,\n  arg: &OsStr,","sourceCodeStart":1486,"sourceCodeEnd":1522,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/runtime/subprocess_windows/src/process.rs#L1486-L1522","documentation":"Arguments to a .bat/.cmd child are appended into a single cmd.exe command line after the script name. A carriage return or newline inside an argument would terminate the current command and let the remainder execute as a new cmd.exe command, so any batch-file argument containing \\r or \\n is rejected with InvalidInput to prevent argument/command injection.","triggerScenarios":"new Deno.Command(\"script.bat\", { args: [multilineText] }) - or spawning an npm/npx .cmd wrapper on Windows - where any argument after the script name contains a carriage return or line feed, such as commit messages, prompt bodies, or file contents passed as argv.","commonSituations":"Forwarding user-typed multi-line text (git commit -m, AI prompts) to a .cmd wrapper; CI scripts passing heredoc-like strings; data read from CRLF files and passed verbatim as arguments on Windows.","solutions":["Pass multi-line data through stdin or a temp file instead of argv","Strip or replace \\r and \\n in arguments whenever the target is a .bat/.cmd","Invoke the underlying .exe directly (node.exe instead of npm.cmd) so batch quoting rules do not apply"],"exampleFix":"// before\nconst c = new Deno.Command(\"wrap.cmd\", { args: [commitMsg] }); // commitMsg contains \\n\n\n// after - send multi-line data via stdin\nconst c = new Deno.Command(\"wrap.cmd\", { stdin: \"piped\" });\nconst child = c.spawn();\nconst w = child.stdin.getWriter();\nawait w.write(new TextEncoder().encode(commitMsg));\nawait w.close();","handlingStrategy":"validation","validationCode":"const isBatch = (f: string) => /\\.(bat|cmd)$/i.test(f);\nif (isBatch(prog) && args.some((a) => /[\\r\\n]/.test(a))) {\n  throw new Error(\"multi-line arguments cannot be passed to batch files; use stdin\");\n}","typeGuard":"const isSingleLine = (s: string): s is string => !/[\\r\\n]/.test(s);","tryCatchPattern":null,"preventionTips":["Treat any argument bound for a .bat/.cmd as untrusted: reject CR/LF","Default to stdin or temp files for multi-line payloads","Call the underlying .exe directly to sidestep batch quoting rules"],"tags":["windows","subprocess","batch-file","command-injection","arguments"],"backgroundTag":"newline-in-command-argument","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}