{"record":{"id":"a0475a917c8d30c9","repo":"can1357/oh-my-pi","slug":"invalid-template-contains-directory-separator","errorCode":null,"errorMessage":"invalid template, {}, contains directory separator","messagePattern":"invalid template, (.+?), contains directory separator","errorType":"error_code","errorClass":"MkTempError","httpStatus":null,"severity":"error","filePath":"crates/pi-builtins/src/mktemp.rs","lineNumber":63,"sourceCode":"#[cfg(not(windows))]\nconst TMPDIR_ENV_VAR: &str = \"TMPDIR\";\n#[cfg(windows)]\nconst TMPDIR_ENV_VAR: &str = \"TMP\";\n\nconst FALLBACK_TMPDIR: &str = \"/tmp\";\n\n#[derive(Error, Debug)]\nenum MkTempError {\n\t#[error(\"could not persist file {}\", .0.quote())]\n\tPersist(PathBuf),\n\n\t#[error(\"with --suffix, template {} must end in X\", .0.quote())]\n\tMustEndInX(String),\n\n\t#[error(\"too few X's in template {}\", .0.quote())]\n\tTooFewXs(String),\n\n\t#[error(\"invalid template, {}, contains directory separator\", .0.quote())]\n\tPrefixContainsDirSeparator(String),\n\n\t#[error(\"invalid suffix {}, contains directory separator\", .0.quote())]\n\tSuffixContainsDirSeparator(String),\n\n\t#[error(\"invalid template, {}; with --tmpdir, it may not be absolute\", .0.quote())]\n\tInvalidTemplate(OsString),\n\n\t#[error(\"too many templates\")]\n\tTooManyTemplates,\n\n\t#[error(\"failed to create {} via template {}: No such file or directory\", .0, .1.quote())]\n\tNotFound(String, PathBuf),\n\n\t#[error(transparent)]\n\tIo(#[from] io::Error),\n}\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/crates/pi-builtins/src/mktemp.rs#L45-L81","documentation":"MkTempError::PrefixContainsDirSeparator is raised when the template portion of a mktemp argument contains a directory separator ('/'). The template's fixed part is treated as a filename prefix within the target directory; embedding a path separator would make the prefix attempt to traverse directories, which the tool forbids.","triggerScenarios":"`mktemp dir/tmpXXXXXX` without --tmpdir semantics — pass directories only via --tmpdir, not inside the template; templates built by string-concatenating a directory path onto a filename.","commonSituations":"Scripts doing `mktemp \"$dir/tmpXXXX\"` instead of `mktemp -p \"$dir\" tmpXXXX`; user-supplied paths pasted wholesale into the template; Windows-style separators slipping in.","solutions":["Move the directory portion to --tmpdir: `mktemp --tmpdir=\"$dir\" tmpXXXXXX`","Strip leading/trailing path components from the template before passing it","If a subdirectory structure is needed, create it separately (mkdir) and use --tmpdir pointing into it","Sanitize user input used as a template: reject or split on '/'"],"exampleFix":"// before\nmktemp \"$dir/tmpXXXXXX\"\n// after\nmktemp --tmpdir=\"$dir\" tmpXXXXXX","handlingStrategy":"validation","validationCode":"if (template.includes('/')) {\n  const dir = path.dirname(template);\n  const base = path.basename(template);\n  // use: mktemp --tmpdir=\"${dir}\" \"${base}\"\n  throw new RangeError(`template '${template}' must not contain '/'; pass the dir via --tmpdir`);\n}","typeGuard":"const isPlainFilenameTemplate = (t: string): boolean => !t.includes('/');","tryCatchPattern":"try {\n  await Bun.$`mktemp ${tmpl}`.quiet();\n} catch (e) {\n  if (String(e).includes('contains directory separator')) {\n    const dir = path.dirname(tmpl), base = path.basename(tmpl);\n    await Bun.$`mktemp --tmpdir=${dir} ${base}`.quiet();\n  } else throw e;\n}","preventionTips":["Never concatenate directories into mktemp templates","Always route directory selection through --tmpdir","Sanitize user-supplied templates by splitting off path components","Add a wrapper function around mktemp that performs this split automatically"],"tags":["cli","path-validation","mktemp"],"backgroundTag":"invalid-template-argument","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}