{"record":{"id":"e442133f5c619368","repo":"affaan-m/ECC","slug":"executable-must-be-one-argv-entry-not-an-interpol","errorCode":null,"errorMessage":"Executable must be one argv entry, not an interpolated shell command string.","messagePattern":"Executable must be one argv entry, not an interpolated shell command string\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/terminal-opener/scripts/open-terminal.js","lineNumber":63,"sourceCode":"\nfunction validateCwd(value) {\n  if (value.includes('\\0')) throw new Error('--cwd must not contain a NUL byte.');\n  if (!isAbsolutePath(value)) throw new Error('--cwd must be an absolute path.');\n}\n\nfunction validateExecutable(value) {\n  if (!value || /[\\0\\r\\n]/.test(value)) {\n    throw new Error('Executable must be a non-empty argv entry without control bytes.');\n  }\n\n  const whitespaceIndex = value.search(/\\s/);\n  const separatorIndexes = [value.indexOf('/'), value.indexOf('\\\\')].filter(index => index >= 0);\n  const firstSeparatorIndex = separatorIndexes.length > 0 ? Math.min(...separatorIndexes) : -1;\n  const resemblesExecutablePath = isAbsolutePath(value)\n    || (firstSeparatorIndex >= 0 && (whitespaceIndex < 0 || firstSeparatorIndex < whitespaceIndex));\n\n  if (whitespaceIndex >= 0 && !resemblesExecutablePath) {\n    throw new Error(\n      'Executable must be one argv entry, not an interpolated shell command string.'\n    );\n  }\n  if (!resemblesExecutablePath && /[;&|<>`$]/.test(value)) {\n    throw new Error(\n      'Executable must be one argv entry, not an interpolated shell command string.'\n    );\n  }\n}\n\nfunction validateArgv(argv) {\n  for (const argument of argv) {\n    if (argument.includes('\\0')) throw new Error('Arguments must not contain NUL bytes.');\n  }\n}\n\nfunction readValue(argv, index, option) {\n  const value = argv[index + 1];","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/terminal-opener/scripts/open-terminal.js#L45-L81","documentation":"validateExecutable rejects values that look like an interpolated shell command string (contain whitespace but don't resemble a path) to enforce that --executable is exactly one argv entry. The script spawns the executable directly without a shell, so 'wezterm start --cwd x' as one string would be interpreted as a single program name and silently fail or be a shell-injection vector.","triggerScenarios":"Passing `--executable \"wezterm start\"` or `--executable \"sh -c 'ls'\"` — strings with spaces that aren't absolute/multi-segment paths.","commonSituations":"Users copying a full shell command line into --executable; scripts interpolating multiple args into one string instead of splitting them into separate argv entries after `--`.","solutions":["Split the command: pass only the program in --executable and the remaining words as arguments after `--`.","If you truly mean a path-like value, use an absolute path containing `/` before any whitespace (e.g. `/usr/local/bin/my exe` quoted).","Avoid shell metacharacters and interpolation; construct argv arrays programmatically instead of strings."],"exampleFix":"// before\nnode open-terminal.js --executable \"wezterm start --always-new-process\"\n// after\nnode open-terminal.js --executable wezterm -- start --always-new-process","handlingStrategy":"validation","validationCode":"if (typeof exe === 'string' && /\\s/.test(exe) && !exe.includes('/') && !require('path').isAbsolute(exe)) {\n  throw new Error('split the command: pass program via --executable and args after --');\n}","typeGuard":"function isSingleArgvProgram(v) {\n  if (typeof v !== 'string' || !v.trim()) return false;\n  const ws = v.search(/\\s/);\n  const sep = Math.min(...['/', '\\\\'].map(c => v.indexOf(c)).filter(i => i >= 0), Infinity);\n  if (ws < 0) return true;\n  return require('path').isAbsolute(v) || (sep !== Infinity && sep < ws);\n}","tryCatchPattern":"try {\n  parseArgs(process.argv);\n} catch (e) {\n  if (/one argv entry/.test(e.message)) {\n    console.error('Put extra words after -- instead of embedding them in --executable');\n  } else throw e;\n}","preventionTips":["Build argv arrays, never interpolated command strings.","Keep the program in --executable and arguments after `--`.","Lint scripts for string-concatenated commands.","Educate users that the tool spawns without a shell."],"tags":["cli","validation","shell-injection","argv"],"backgroundTag":"invalid-argument-format","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}