{"record":{"id":"398806495de86eb2","repo":"vercel/turborepo","slug":"invalid-description-path-must-be-an-absolute","errorCode":null,"errorMessage":"Invalid ${description}: path must be an absolute, non-empty string without NUL or newline characters and cannot start with \"-\"","messagePattern":"Invalid (.+?): path must be an absolute, non-empty string without NUL or newline characters and cannot start with \"-\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/turbo-utils/src/examples.ts","lineNumber":493,"sourceCode":"/**\n * Validates that a path is safe to use as a git CLI argument.\n * Prevents argument injection by rejecting paths that:\n * - Are empty or not a string\n * - Contain NUL bytes (could truncate the argument)\n * - Start with \"-\" (could be interpreted as a git option)\n * - Are not absolute filesystem paths (helps ensure they cannot be mistaken\n *   for URLs or additional git options like \"--upload-pack\")\n */\nfunction assertSafeGitArgument(value: string, description: string): void {\n  if (\n    !value ||\n    typeof value !== \"string\" ||\n    value.includes(\"\\0\") ||\n    value.includes(\"\\n\") ||\n    value.startsWith(\"-\") ||\n    !isAbsolute(value)\n  ) {\n    throw new Error(\n      `Invalid ${description}: path must be an absolute, non-empty string without NUL or newline characters and cannot start with \"-\"`\n    );\n  }\n}\n\nfunction formatError(error: unknown): string {\n  if (error && typeof error === \"object\" && \"stderr\" in error) {\n    const { stderr } = error as { stderr?: unknown };\n    if (typeof stderr === \"string\" || Buffer.isBuffer(stderr)) {\n      const output = stderr.toString().trim();\n      if (output) {\n        return output;\n      }\n    }\n  }\n\n  return error instanceof Error ? error.message : String(error);\n}","sourceCodeStart":475,"sourceCodeEnd":511,"githubUrl":"https://github.com/vercel/turborepo/blob/f9245100cf0d31d96628804ead485f6bf226e55a/packages/turbo-utils/src/examples.ts#L475-L511","documentation":"A defensive guard (assertSafeGitArgument) in @turbo-utils/examples.ts that validates any path handed to a git subprocess. It rejects values that are empty, non-strings, contain NUL or newline bytes, start with '-', or are not absolute filesystem paths. This blocks argument injection (e.g. '--upload-pack=...') and option smuggling into git commands.","triggerScenarios":"Calling downloadAndExtractExample(root, name) with a relative root (like '.' or 'my-app'), an empty root, or a root string containing \\0 or \\n or leading with '-'. The resolved project root and the derived .turbo-clone-temp directory are both validated before runGit executes.","commonSituations":"Scripts calling createProject/downloadAndExtractExample with process.cwd() assumed absolute (it can be relative via execa cwd), Windows paths that were not resolved, user-supplied install targets taken straight from argv, or malicious/odd input passed as a destination.","solutions":["Pass an absolute path: wrap the root with path.resolve(root) before calling the API","If the path comes from user input, validate/normalize it before use (reject NUL/newlines/'-' prefixes)","Ensure you are not passing an empty string for the root directory","Check for stray control characters if the path was assembled from env vars or config files"],"exampleFix":"// before\nimport { downloadAndExtractExample } from \"@turbo-utils/examples\";\nawait downloadAndExtractExample(\".\", \"basic\"); // throws: relative path\n\n// after\nimport { resolve } from \"node:path\";\nimport { downloadAndExtractExample } from \"@turbo-utils/examples\";\nawait downloadAndExtractExample(resolve(\".\"), \"basic\");","handlingStrategy":"validation","validationCode":"import { isAbsolute, resolve } from \"node:path\";\n\nfunction toSafeGitPath(root: string): string {\n  const abs = isAbsolute(root) ? root : resolve(root);\n  if (abs.includes(\"\\0\") || abs.includes(\"\\n\") || abs.startsWith(\"-\")) {\n    throw new Error(`Unsafe root path: ${JSON.stringify(root)}`);\n  }\n  return abs;\n}\n// await downloadAndExtractExample(toSafeGitPath(root), name);","typeGuard":"function isSafeGitArgument(value: string): boolean {\n  return (\n    value.length > 0 &&\n    isAbsolute(value) &&\n    !value.includes(\"\\0\") &&\n    !value.includes(\"\\n\") &&\n    !value.startsWith(\"-\")\n  );\n}","tryCatchPattern":null,"preventionTips":["Always path.resolve() user-supplied destinations before passing them to create/scaffold APIs","Sanitize any path that crosses into a subprocess command line","Treat relative roots as invalid at your own boundary instead of relying on the library guard"],"tags":["validation","git","path-traversal","security","create-turbo"],"backgroundTag":"path-validation-failed","analyzedSha":"f9245100cf0d31d96628804ead485f6bf226e55a","analyzedAt":"2026-08-17T10:46:15.696Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}