fix(cli): refuse to scaffold into a non-empty directory - #493
Merged
Conversation
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
blurrah
force-pushed
the
cli-non-empty-dir-guard
branch
from
August 7, 2026 15:12
e1f0f7e to
b30bbc1
Compare
blurrah
force-pushed
the
cli-non-empty-dir-guard
branch
from
August 7, 2026 15:13
b30bbc1 to
a0a1ac5
Compare
The CLI resolved the user-supplied name to projectDir, created it with
mkdir({ recursive: true }), and copied the template in with
fs.cp(..., { recursive: true }). Nothing checked that the target was
empty, so pointing it at an existing project silently overwrote
same-named files before install and git init ran.
Inspect the target before scaffolding. Empty or missing proceeds as
before; non-empty prompts for confirmation on a TTY (default no) and
exits 1 in non-interactive environments like CI or a coding agent. The
new --force flag opts back in. --no-template is exempt, since adding
agent assets to an existing project is the point of that mode. A target
that exists but is not a directory now reports the error instead of
crashing on the later mkdir.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Boris Besemer <2852507+blurrah@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the security finding from the adversarial review of #492: the CLI scaffolding can overwrite files in an existing directory.
Targets
blurra/inline-skills-scaffoldso #492 ships with the fix — the finding cites that branch'sapps/cli/index.mjs:L214-L286.Problem
main()resolved the user-supplied name toprojectDir, created it unconditionally withmkdir({ recursive: true }), then copied the downloaded template in withfs.cp(..., { recursive: true }). Nothing checked that the target was empty and nothing asked for confirmation, so pointing the CLI at an existing project silently overwrote same-named files — before<pm> installandgit initran against it.Solution
Inspect the target before scaffolding:
… already contains <entries>. Scaffolding may overwrite existing files. Continue? (y/N), defaulting to no. Declining exits 1 without writing anything.--force— new flag that opts back in, with a warning naming the directory.--no-template— exempt. Adding agent assets to an existing project is the whole point of that mode.A target path that exists but is not a directory now reports a clear error instead of crashing on the later
mkdir.Every entry counts, including dotfiles — no ignore list. A strict rule with an explicit escape hatch is easier to audit than one that guesses which existing files are safe to clobber.
Tests
apps/cli/index.test.mjsgrows 10 cases (19 total, all passing): the refusal path leaves existing files byte-identical and runs noinstall/gitsubprocesses; dotfile-only directories count as non-empty; empty-but-existing directories scaffold without prompting; the TTY confirmation is honored in both directions;--forcenever prompts;--no-templatestill works against a populated project; andreadTargetEntriesreports a missing path as empty but rejects a file.Also verified end-to-end against the real binary — it refuses a populated directory with exit 1 and leaves it untouched.
Docs
init-vercel-shopskill now tells agents the CLI enforces its existing "don't overwrite" step and to only reach for--forceon explicit user confirmation. (Nocontent/docs/skills/init-vercel-shop.mdxmirror exists, so no docs skill sync is needed.)Note
mainhas the same gap in itstar-basedfetchTemplate. This guard sits inmain(), ahead of the scaffold call, so it covers that path too once #492 lands.🤖 Generated with Claude Code