Skip to content

Fix and consolidate markup escaping in CLI#14749

Merged
JamesNK merged 2 commits intorelease/13.2from
jamesnk/markup-escape
Mar 1, 2026
Merged

Fix and consolidate markup escaping in CLI#14749
JamesNK merged 2 commits intorelease/13.2from
jamesnk/markup-escape

Conversation

@JamesNK
Copy link
Member

@JamesNK JamesNK commented Feb 26, 2026

Description

The CLI interaction service has methods like DisplayMessage and DisplaySuccess that are rendered using markup. That means callers must escape external content or get problems when content contains markup characters. This is non-obvious and it's very easy to include external content in these methods.

This PR:

  • Add previously missing escaping to all external content with DisplayMarkup
  • Changes many of the display methods on interaction service to escape by default. Can opt-in to displaying markup using an optional parameter
  • Move emoji to a separate argument for ShowStatusAsync. Allows message to be escaped by default
  • Add emoji measurement to apply consistent spacing. Hopefully fix issues with inconsistent spacing after emojis.
  • Add RenderCommand. It's a test bed for smoke testing render output. Hidden and excluded from non-debug builds.
  • Add KnownEmojis. List of emojis used in the CLI. Fix some invalid emojis.

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
        • No
    • No
  • Does the change require an update in our Aspire docs?

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

🚀 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 -- 14749

Or

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

@github-actions
Copy link
Contributor

github-actions bot commented Feb 26, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit 56209ca:

Test Recording
AddPackageInteractiveWhileAppHostRunningDetached ▶️ View Recording
AddPackageWhileAppHostRunningDetached ▶️ View Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ View Recording
AgentInitCommand_WithMalformedMcpJson_ShowsErrorAndExitsNonZero ▶️ View Recording
AspireUpdateRemovesAppHostPackageVersionFromDirectoryPackagesProps ▶️ View Recording
Banner_DisplayedOnFirstRun ▶️ View Recording
Banner_DisplayedWithExplicitFlag ▶️ View Recording
CreateAndDeployToDockerCompose ▶️ View Recording
CreateAndDeployToDockerComposeInteractive ▶️ View Recording
CreateAndPublishToKubernetes ▶️ View Recording
CreateAndRunAspireStarterProject ▶️ View Recording
CreateAndRunAspireStarterProjectWithBundle ▶️ View Recording
CreateAndRunJsReactProject ▶️ View Recording
CreateAndRunPythonReactProject ▶️ View Recording
CreateEmptyAppHostProject ▶️ View Recording
CreateStartAndStopAspireProject ▶️ View Recording
CreateStartWaitAndStopAspireProject ▶️ View Recording
CreateTypeScriptAppHostWithViteApp ▶️ View Recording
DescribeCommandResolvesReplicaNames ▶️ View Recording
DescribeCommandShowsRunningResources ▶️ View Recording
DetachFormatJsonProducesValidJson ▶️ View Recording
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
PsFormatJsonOutputsOnlyJsonToStdout ▶️ View Recording
SecretCrudOnDotNetAppHost ▶️ View Recording
SecretCrudOnTypeScriptAppHost ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording
StopAllAppHostsFromAppHostDirectory ▶️ View Recording
StopAllAppHostsFromUnrelatedDirectory ▶️ View Recording
StopNonInteractiveMultipleAppHostsShowsError ▶️ View Recording
StopNonInteractiveSingleAppHost ▶️ View Recording
StopWithNoRunningAppHostExitsSuccessfully ❌ Upload failed

📹 Recordings uploaded automatically from CI run #22531201402

@JamesNK JamesNK marked this pull request as ready for review February 27, 2026 04:08
Copilot AI review requested due to automatic review settings February 27, 2026 04:08
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 addresses a security and usability issue where external content containing Spectre.Console markup characters (like brackets []) would cause exceptions or unintended rendering in CLI output. The solution adds auto-escaping by default to display methods and introduces a separate emojiName parameter to cleanly separate emoji formatting from message text.

Changes:

  • Added auto-escaping by default to DisplayMessage, DisplaySuccess, DisplaySubtleMessage, and ShowStatusAsync/ShowStatus methods with an optional allowMarkup parameter to opt-in to markup rendering
  • Introduced emojiName parameter to ShowStatusAsync/ShowStatus to handle emoji separately from status text, enabling proper auto-escaping while preserving emoji rendering
  • Added EmojiWidth utility class to calculate terminal cell widths for emoji, handling Unicode presentation properties for consistent emoji alignment
  • Consolidated TableExtensions.AddBoldColumn overloads into a single method with optional parameters
  • Updated all call sites to use the new signatures, with several removing manual .EscapeMarkup() calls
  • Added comprehensive test coverage for the escaping behavior
  • Added debug-only RenderCommand for testing CLI rendering

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Aspire.Cli/Utils/EmojiWidth.cs New utility class for calculating emoji display widths based on Unicode Emoji_Presentation property
tests/Aspire.Cli.Tests/Utils/EmojiWidthTests.cs Tests for emoji width calculation
src/Aspire.Cli/Interaction/IInteractionService.cs Updated interface signatures to add emojiName and allowMarkup parameters
src/Aspire.Cli/Interaction/ConsoleInteractionService.cs Implemented auto-escaping logic and emoji prefix formatting
src/Aspire.Cli/Interaction/ExtensionInteractionService.cs Updated wrapper to forward new parameters
tests/Aspire.Cli.Tests/Interaction/ConsoleInteractionServiceTests.cs Comprehensive tests for auto-escaping, allowMarkup, and emojiName behavior
tests/Aspire.Cli.Tests/Utils/ConsoleActivityLoggerTests.cs Tests for markup escaping in activity logger output
src/Aspire.Cli/Utils/TableExtensions.cs Consolidated three AddBoldColumn overloads into one with optional parameters
src/Aspire.Cli/Utils/ConsoleActivityLogger.cs Added escaping for failure reasons and pipeline summary keys/values
Multiple command files Updated to use emojiName parameter and removed manual .EscapeMarkup() calls where appropriate
src/Aspire.Cli/Commands/RenderCommand.cs New DEBUG-only command for testing CLI rendering
Test service mocks Updated to match new interface signatures

@JamesNK JamesNK force-pushed the jamesnk/markup-escape branch from c3a33cf to 56209ca Compare February 28, 2026 23:19
@JamesNK JamesNK enabled auto-merge (squash) March 1, 2026 00:15
@JamesNK JamesNK merged commit 96c272a into release/13.2 Mar 1, 2026
679 of 683 checks passed
@JamesNK JamesNK deleted the jamesnk/markup-escape branch March 1, 2026 00:27
@dotnet-policy-service dotnet-policy-service bot added this to the 13.2 milestone Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants