fix: Validate root and temp directory paths in download function (#11311)
Added normalization and validation for root and temp directory paths to
prevent unsafe git arguments.
### Description
In general, to fix second-order command injection involving git, you
must ensure that any command-line argument influenced by user input
cannot be interpreted by git as an option or remote specification, and
that it is a safe filesystem path. That typically means
validating/sanitizing the data at the point where it becomes a command
argument, rather than trusting earlier path resolution alone.
In this codebase, the issue is that `downloadAndExtractExample` uses
`root` (ultimately derived from `validateDirectory(directory)` and
`createProject({ appPath, … })`) to construct `tempDir`, then passes
`tempDir` to `execFileSync("git", ["clone", …, tempDir], …)`. A robust
fix is to: (1) ensure `root` is a safe absolute path (containing no
NULs, not empty, and not starting with `-` so it can’t be parsed as an
option), and (2) similarly ensure the constructed `tempDir` argument
cannot be misinterpreted as a git option or remote. We can do this
locally in `downloadAndExtractExample` without changing its public
signature or behavior for legitimate inputs.
Concretely, in `packages/turbo-utils/src/examples.ts`, inside
`downloadAndExtractExample`, we can:
- Normalize and validate `root` at the top of the function (e.g., `const
normalizedRoot = resolve(root);`).
- Derive `tempDir` from this normalized root.
- Add simple guard checks that `normalizedRoot` and `tempDir` are
non-empty, do not start with `-`, and do not contain NUL characters
(which are illegal in real paths anyway).
- Use the sanitized `tempDir` for both the `git clone` destination and
for subsequent filesystem operations.
No changes are needed in `createProject.ts` or `validateDirectory.ts`
because they already constrain directory handling and do not directly
invoke `git`. The problem is localized to where the tainted path is
passed into `execFileSync`. We will import `resolve` from `node:path` is
already imported in `examples.ts`, so we can reuse it without new
imports. This fix addresses all three alert variants, because the taint
from `createProject` and `validateDirectory` is now terminated by the
validation in `downloadAndExtractExample`.
---------
Co-authored-by: Anthony Shew <anthonyshew@gmail.com>