Summary
mcpp new <name> currently has no single authoritative project-name contract. The name is used both as a filesystem path and as unescaped template data. This permits path escape and malformed generated projects; one valid-looking input can also make the command loop forever.
Reproduction
1. Template marker in the name hangs the command
mcpp new PROJECT
mcpp new myPROJECTname
The builtin scaffold repeatedly executes:
while ((pos = body.find("PROJECT")) != std::string::npos) {
body.replace(pos, 7, name);
}
When name itself contains PROJECT, the replacement result remains matchable. A controlled 500 ms run ended with ETIMEDOUT / SIGTERM; mcpp.toml had already been created, leaving a partial project.
The package-template renderer has the same general risk because inserted placeholder values are searched again by replace_all when they contain the placeholder token.
2. A name can escape the current directory
mkdir work
cd work
mcpp new ../escaped
Observed result: exit code 0 and the project was created at the parent directory (../escaped). Both builtin and package-template paths construct the destination as current_path() / name without requiring a single path component.
3. Template-unsafe names return success with invalid files
mcpp new 'bad"name'
mcpp new $'bad\tname'
Observed result: exit code 0. The generated TOML/C++ contains the value without escaping, for example:
The builtin scaffold also does not check the open/write status of mcpp.toml, src/main.cpp, tests/test_smoke.cpp, or .gitignore, so an incomplete write can still reach the success message and return 0.
Expected behavior
Before creating any directory, mcpp new should apply one shared validation contract to builtin and package templates:
- require a non-empty single path component;
- reject absolute paths, separators, . / .., control characters, and platform-invalid names;
- either reject template-unsafe characters or provide context-safe escaping;
- render placeholders without rescanning inserted values;
- check every directory/file operation and return non-zero on incomplete output;
- avoid leaving a partial project after a failed render/write where practical.
The CLI should own this contract so VS Code and other clients do not need to copy internal template rules.
Suggested tests
- PROJECT and myPROJECTname terminate and produce valid output or a clear validation error.
- ../escaped and an absolute path cannot create outside the working directory.
- quotes and C0/DEL controls cannot produce malformed TOML/C++.
- failed file writes return non-zero.
- the same name validation applies to builtin and package-shipped templates.
Environment and evidence boundary
- Runtime reproduction: locally available mcpp 2026.8.5.2 on macOS ARM64.
- Current-source verification: origin/main@fdad165 (v2026.8.8.2) still contains the same destination construction, unescaped builtin template replacement, and unchecked output streams.
Summary
mcpp new <name> currently has no single authoritative project-name contract. The name is used both as a filesystem path and as unescaped template data. This permits path escape and malformed generated projects; one valid-looking input can also make the command loop forever.
Reproduction
1. Template marker in the name hangs the command
The builtin scaffold repeatedly executes:
When name itself contains PROJECT, the replacement result remains matchable. A controlled 500 ms run ended with ETIMEDOUT / SIGTERM; mcpp.toml had already been created, leaving a partial project.
The package-template renderer has the same general risk because inserted placeholder values are searched again by replace_all when they contain the placeholder token.
2. A name can escape the current directory
mkdir work cd work mcpp new ../escapedObserved result: exit code 0 and the project was created at the parent directory (../escaped). Both builtin and package-template paths construct the destination as current_path() / name without requiring a single path component.
3. Template-unsafe names return success with invalid files
Observed result: exit code 0. The generated TOML/C++ contains the value without escaping, for example:
The builtin scaffold also does not check the open/write status of mcpp.toml, src/main.cpp, tests/test_smoke.cpp, or .gitignore, so an incomplete write can still reach the success message and return 0.
Expected behavior
Before creating any directory, mcpp new should apply one shared validation contract to builtin and package templates:
The CLI should own this contract so VS Code and other clients do not need to copy internal template rules.
Suggested tests
Environment and evidence boundary