Skip to content

fix(init): escape user-supplied values in specify init output - #3787

Open
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/init-escape-user-values-in-output
Open

fix(init): escape user-supplied values in specify init output#3787
jawwad-ali wants to merge 1 commit into
github:mainfrom
jawwad-ali:fix/init-escape-user-values-in-output

Conversation

@jawwad-ali

Copy link
Copy Markdown
Contributor

Problem

commands/init.py interpolates the project name, the echoed --integration/--script values and paths straight into Rich markup f-strings. It is the only CLI command module without escaping — extensions/_commands.py, presets/_commands.py, workflows/_commands.py and integrations/_query_commands.py all wrap user-controlled display values already.

Two consequences, both reproduced end-to-end through the real CLI (CliRunner, offline):

1. Silent wrong output. specify init "proj [v2]" exits 0 and creates the directory, but the Next Steps panel prints:

1. Go to the project folder: cd proj

Rich consumed [v2] as a style tag, so the command the user copy-pastes fails.

2. Crash after success. specify init "app[/red]x" creates the project and then dies:

MarkupError: closing tag '[/red]' at position 44 doesn't match any open tag
-> exit 1, traceback

The project was in fact scaffolded, so init reports failure for work it completed.

Fix

Wrap the user-controlled display values in rich.markup.escape:

  • project name — the not-a-directory error, the not-empty warning, the --force merge notice, the Directory Conflict panel, and the Next Steps cd
  • the project / working / target paths in the setup panel
  • the echoed --integration and --script values in their invalid-value errors
  • the agent folder in the .gitignore hint (it can come from --commands-dir)

Display only — no control flow, exit codes or message wording change, and escape is a no-op for any value without a tag-shaped bracket run, so existing output is byte-identical.

Verification

  • 4 new tests in a dedicated tests/test_init_output_markup.py: the cd line naming the real directory (2 parametrized names), the closing-tag crash, and the invalid---integration echo. All 4 fail before this change, pass after.
  • New file deliberately, to avoid conflicting with open PR fix(integrations): escape catalog metadata in discovery output #3772, which edits tests/integrations/test_cli.py.
  • tests/test_init_dir.py / test_init_dir_cli.py: unchanged — the 6 test_ps_* failures there are pre-existing on this Windows box (verified: identical 6 failures with my change stashed) and are PowerShell-environment issues, not related to this diff.
  • uvx ruff@0.15.0 check src tests clean.

AI-assisted: authored with Claude Code. I confirmed reachability first — specify init "proj [v2]" really does succeed and create the directory — so the broken cd line is what a user actually gets, then verified each failure end-to-end before and after.

@jawwad-ali
jawwad-ali requested a review from mnriem as a code owner July 28, 2026 11:41
commands/init.py interpolated the project name, --integration/--script values
and paths straight into Rich markup f-strings. It was the only CLI command
module without escaping -- extensions, presets, workflows and integrations all
wrap user-controlled display values already.

Two consequences, both reproduced end-to-end through the real CLI:

1. SILENT WRONG OUTPUT. `specify init "proj [v2]"` exits 0 and creates the
   directory, but the Next Steps panel prints

       1. Go to the project folder: cd proj

   Rich ate `[v2]` as a style tag, so the command the user copy-pastes fails.

2. CRASH AFTER SUCCESS. `specify init "app[/red]x"` creates the project and
   then dies with MarkupError("closing tag '[/red]' ... doesn't match any open
   tag") -> exit 1 with a traceback for work that actually completed.

Wrap the user-controlled display values in rich.markup.escape: project name
(error/warning/conflict/next-steps), project and working paths, the echoed
--integration and --script values, and the agent folder in the gitignore hint.
Display only -- no control flow, exit codes or messages change, and escape is a
no-op for any value without a tag-shaped bracket run.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jawwad-ali
jawwad-ali force-pushed the fix/init-escape-user-values-in-output branch from 3cea41e to ed24bdb Compare July 28, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant