Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 12, 2025

Description

The CLI crashes when user input contains Spectre Console markup characters (e.g., [, ]). This occurs when these characters appear in project names, paths, or other prompts and are passed unescaped to Spectre.Console's markup renderer.

Enter the project name (aspire): [27;5;13~
Unhandled exception: System.InvalidOperationException: Encountered malformed markup tag at position 45.
   at Spectre.Console.TextPrompt`1.WritePrompt(IAnsiConsole console)

Changes

Escape user input before rendering

  • NewCommand.cs: Escape project name and output path in prompt defaults
  • DotNetTemplateFactory.cs: Escape output path in success messages
  • PipelineCommandBase.cs: Escape all input.Value in publish/deploy prompts (6 call sites)
  • ProjectUpdater.cs: Escape directory path in NuGet config prompts

Test coverage

  • Added NewCommandEscapesMarkupInProjectNameAndOutputPath test validating prompts handle markup characters without crashing

All escaping uses Spectre.Console's EscapeMarkup() extension method, which converts [[[ and ]]].

Fixes #(issue to be linked)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes (prevents markup injection)
        • No
    • No
  • Does the change require an update in our Aspire docs?
Original prompt

This section details on the original issue you should resolve

<issue_title>CLI does not properly escape markup tags</issue_title>
<issue_description>```
Build succeeded in 3.3s
Enter the project name (aspire): [27;5;13~
Unhandled exception: System.InvalidOperationException: Encountered malformed markup tag at position 45.
at Spectre.Console.MarkupTokenizer.ReadMarkup() in //src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs:line 183
at Spectre.Console.MarkupTokenizer.MoveNext() in /
/src/Spectre.Console/Internal/Text/Markup/MarkupTokenizer.cs:line 27
at Spectre.Console.MarkupParser.Parse(String text, Style style) in //src/Spectre.Console/Internal/Text/Markup/MarkupParser.cs:line 19
at Spectre.Console.AnsiConsoleExtensions.Markup(IAnsiConsole console, String value) in /
/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Markup.cs:line 77
at Spectre.Console.TextPrompt1.WritePrompt(IAnsiConsole console) in /_/src/Spectre.Console/Prompts/TextPrompt.cs:line 232 at Spectre.Console.TextPrompt1.<>c__DisplayClass63_0.<b__0>d.MoveNext() in //src/Spectre.Console/Prompts/TextPrompt.cs:line 129
--- End of stack trace from previous location ---
at Spectre.Console.Internal.DefaultExclusivityMode.RunAsync[T](Func`1 func) in /
/src/Spectre.Console/Internal/DefaultExclusivityMode.cs:line 40
at Spectre.Console.TextPrompt`1.ShowAsync(IAnsiConsole console, CancellationToken cancellationToken) in /_/src/Spectre.Console/Prompts/TextPrompt.cs:line 122
at Aspire.Cli.Interaction.ConsoleInteractionService.PromptForStringAsync(String promptText, String defaultValue, Func`2 validator, Boolean isSecret, Boolean required, CancellationToken cancellationToken) in /Users/davidfowler/dev/git/aspire/src/Aspire.Cli/Interaction/ConsoleInteractionService.cs:line 80
at Aspire.Cli.Commands.NewCommandPrompter.PromptForOutputPath(String path, CancellationToken cancellationToken) in /Users/davidfowler/dev/git/aspire/src/Aspire.Cli/Commands/NewCommand.cs:line 248
at Aspire.Cli.Templating.DotNetTemplateFactory.GetOutputPathAsync(ParseResult parseResult, Func`2 pathDeriver, String projectName, CancellationToken cancellationToken) in /Users/davidfowler/dev/git/aspire/src/Aspire.Cli/Templating/DotNetTemplateFactory.cs:line 348
at Aspire.Cli.Templating.DotNetTemplateFactory.ApplyTemplateAsync(CallbackTemplate template, ParseResult parseResult, Func`3 extraArgsCallback, CancellationToken cancellationToken) in /Users/davidfowler/dev/git/aspire/src/Aspire.Cli/Templating/DotNetTemplateFactory.cs:line 222
at Aspire.Cli.Commands.NewCommand.ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken) in /Users/davidfowler/dev/git/aspire/src/Aspire.Cli/Commands/NewCommand.cs:line 135
at Aspire.Cli.Commands.BaseCommand.<>c__DisplayClass6_0.<<-ctor>b__0>d.MoveNext() in /Users/davidfowler/dev/git/aspire/src/Aspire.Cli/Commands/BaseCommand.cs:line 32
--- End of stack trace from previous location ---
at System.CommandLine.Invocation.InvocationPipeline.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)


<agent_instructions>This issue is caused by the prompt for the project name during `aspire new` taking the unvetted input from the user and feeding it into the prompt for the output path. The text on the project name could be interpretted as Spectre markup and as a result can cause the markup parser in Spectre is crashing. Use the EscapeMarkup method to make this input safe for display as part of the prompt for the output path. Also review the rest of the code base to identify inputs that are being passed directly to prompts where the input may not have been sanitized.</agent_instructions>

## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Fix CLI to properly escape markup tags Fix CLI crash when user input contains Spectre markup characters Nov 12, 2025
Copilot AI requested a review from mitchdenny November 12, 2025 03:19
Copilot finished work on behalf of mitchdenny November 12, 2025 03:19
@github-actions
Copy link
Contributor

github-actions bot commented Nov 13, 2025

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 12919

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 12919"

@mitchdenny mitchdenny force-pushed the copilot/fix-cli-markup-escaping branch from a9763f8 to 7b423f4 Compare November 17, 2025 04:50
@mitchdenny mitchdenny force-pushed the copilot/fix-cli-markup-escaping branch from 7b423f4 to 8dc8506 Compare November 18, 2025 05:17
@mitchdenny mitchdenny marked this pull request as ready for review November 18, 2025 08:21
Copilot AI review requested due to automatic review settings November 18, 2025 08:21
Copilot finished reviewing on behalf of mitchdenny November 18, 2025 08:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a security vulnerability where user input containing Spectre Console markup characters ([, ]) could crash the CLI when rendered in prompts and success messages. The fix applies .EscapeMarkup() to user-provided values before they are displayed as default values in prompts or included in formatted messages.

Key Changes

  • Added markup escaping for all user-provided default values in prompts across the CLI
  • Added markup escaping for paths in success messages
  • Included comprehensive test coverage validating the fix

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Aspire.Cli/Commands/NewCommand.cs Escapes project name and output path default values in prompts to prevent markup parsing crashes
src/Aspire.Cli/Templating/DotNetTemplateFactory.cs Escapes output path in success message and adds required using Spectre.Console
src/Aspire.Cli/Commands/PipelineCommandBase.cs Escapes all input.Value references used as default values across 6 prompt call sites for publish/deploy operations
src/Aspire.Cli/Projects/ProjectUpdater.cs Escapes directory path in NuGet config prompt default value
tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs Adds test validating that project names with markup characters don't crash the CLI

};

// Capture what default value is passed for the output path
// The path passed to this callback is the unescaped version
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "The path parameter passed to the callback contains the unescaped markup characters", but this is misleading. The PromptForOutputPath method receives the unescaped path, escapes it internally, and then passes the escaped version to the interaction service. The callback captures the unescaped path because it's called with the original parameter before escaping happens inside the implementation.

Suggested change
// The path passed to this callback is the unescaped version
// The path parameter passed to this callback is the unescaped version;
// escaping is handled internally by PromptForOutputPath before passing the value to the interaction service.

Copilot uses AI. Check for mistakes.
Assert.Equal(0, exitCode);

// Verify that the default output path was derived from the project name with markup characters
// The path parameter passed to the callback contains the unescaped markup characters
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states "The path parameter passed to the callback contains the unescaped markup characters", but this is misleading. The comment should clarify that the callback receives the original unescaped path parameter, which is then escaped internally before being displayed as a default value in the prompt.

Suggested change
// The path parameter passed to the callback contains the unescaped markup characters
// The callback receives the original unescaped path parameter; escaping is performed internally before displaying as the default value in the prompt.

Copilot uses AI. Check for mistakes.
@mitchdenny mitchdenny merged commit 53635e7 into main Nov 19, 2025
301 of 302 checks passed
@mitchdenny mitchdenny deleted the copilot/fix-cli-markup-escaping branch November 19, 2025 03:26
@dotnet-policy-service dotnet-policy-service bot added this to the 13.1 milestone Nov 19, 2025
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.

CLI does not properly escape markup tags

3 participants