Skip to content

Fix Spectre markup rendering in CLI selection prompts#14497

Open
joperezr wants to merge 2 commits intorelease/13.2from
fix/cli-selection-markup-escaping
Open

Fix Spectre markup rendering in CLI selection prompts#14497
joperezr wants to merge 2 commits intorelease/13.2from
fix/cli-selection-markup-escaping

Conversation

@joperezr
Copy link
Member

@joperezr joperezr commented Feb 13, 2026

Summary

The blanket EscapeMarkup() added to the UseConverter in PromptForSelectionAsync/PromptForSelectionsAsync (PR #14422) escaped all markup — including the intentional [bold]...[/] used by AddCommand.PackageNameWithFriendlyNameIfAvailable. This caused literal [bold]postgresql[/] text to appear in the aspire add selection menu instead of bold-formatted text.

Root Cause

Commit 0e2fce7 (PR #14422) was a broad markup-escaping pass to fix crashes when user-controlled strings containing [brackets] (like Azure subscription names, IPv6 addresses, file paths) were passed to Spectre.Console. The fix correctly identified the problem but applied escaping too broadly — at the generic UseConverter level in ConsoleInteractionService — which broke all callers that intentionally produce Spectre markup.

Fix

  • Remove blanket escaping from PromptForSelectionAsync/PromptForSelectionsAsync converters in ConsoleInteractionService
  • Escape at caller sites that format user-controlled or file-system content:
    • AddCommand — package names, versions, channel details (preserving [bold] markup)
    • NewCommand — package versions, channel names, template descriptions
    • UpdateCommand — version + source details
    • ProjectLocator — file paths (2 sites)
    • SolutionLocator — file paths
    • AppHostConnectionResolver — file paths (2 sites)
    • InitCommand — project file names (2 sites)
    • PipelineCommandBase — publish prompt options from AppHost
    • PublishCommand — publisher names from AppHost

Callers with hardcoded/resource strings (e.g., ConfigCommand, NuGetConfigPrompter, LanguageService, DotNetTemplateFactory) were audited and confirmed safe — no changes needed.

Tests

  • Added 2 new unit tests verifying that:
    1. Selection prompt converters preserve intentional Spectre markup ([bold]...[/])
    2. Dynamic data with brackets is properly escaped while markup is preserved
  • All 1278 CLI tests pass

The blanket EscapeMarkup() added to the UseConverter in
PromptForSelectionAsync/PromptForSelectionsAsync (PR #14422) escaped all
markup including the intentional [bold]...[/] used by
AddCommand.PackageNameWithFriendlyNameIfAvailable, causing literal
'[bold]postgresql[/]' to appear in 'aspire add' output.

Fix: remove blanket escaping from the generic converter and instead
escape dynamic values at each caller site that formats user-controlled
or file-system content (file paths, package names, channel details,
publish prompt options), while preserving intentional Spectre markup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 13, 2026 22:54
@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 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 -- 14497

Or

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

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 regression introduced by PR #14422 where blanket EscapeMarkup() calls in ConsoleInteractionService.PromptForSelectionAsync and PromptForSelectionsAsync broke intentional Spectre markup rendering in CLI selection prompts, specifically the [bold]...[/] formatting used in the aspire add command.

Changes:

  • Removed blanket .EscapeMarkup() from selection prompt converters in ConsoleInteractionService
  • Added targeted escaping at 9 caller sites that format user-controlled or file-system content (paths, versions, channels, publisher names)
  • Preserved intentional [bold]...[/] markup by escaping only the dynamic data within formatted strings
  • Added 2 regression tests to verify markup preservation and proper escaping patterns

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Aspire.Cli/Interaction/ConsoleInteractionService.cs Removed blanket .EscapeMarkup() from PromptForSelectionAsync and PromptForSelectionsAsync converters
src/Aspire.Cli/Commands/AddCommand.cs Escaped package names, versions, and channel details while preserving [bold] markup in friendly names
src/Aspire.Cli/Commands/NewCommand.cs Escaped package versions, channel names, and template descriptions
src/Aspire.Cli/Commands/UpdateCommand.cs Escaped channel names and source details
src/Aspire.Cli/Projects/ProjectLocator.cs Escaped file paths in 2 project selection sites
src/Aspire.Cli/Projects/SolutionLocator.cs Escaped solution file paths
src/Aspire.Cli/Backchannel/AppHostConnectionResolver.cs Escaped connection display strings in 2 sites
src/Aspire.Cli/Commands/InitCommand.cs Escaped project file names in 2 selection prompts
src/Aspire.Cli/Commands/PipelineCommandBase.cs Escaped publish prompt option values from AppHost
src/Aspire.Cli/Commands/PublishCommand.cs Escaped publisher names from AppHost
tests/Aspire.Cli.Tests/Interaction/ConsoleInteractionServiceTests.cs Added 2 tests verifying markup preservation and proper escaping patterns

@joperezr joperezr requested a review from mitchdenny February 13, 2026 22:58
…sts.cs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor

github-actions bot commented Feb 13, 2026

🎬 CLI E2E Test Recordings

The following terminal recordings are available for commit f4f8ca4:

Test Recording
AgentCommands_AllHelpOutputs_AreCorrect ▶️ View Recording
AgentInitCommand_MigratesDeprecatedConfig ▶️ 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
DoctorCommand_DetectsDeprecatedAgentConfig ▶️ View Recording
DoctorCommand_WithSslCertDir_ShowsTrusted ▶️ View Recording
DoctorCommand_WithoutSslCertDir_ShowsPartiallyTrusted ▶️ View Recording
LogsCommandShowsResourceLogs ▶️ View Recording
PsCommandListsRunningAppHost ▶️ View Recording
ResourcesCommandShowsRunningResources ▶️ View Recording
StagingChannel_ConfigureAndVerifySettings_ThenSwitchChannels ▶️ View Recording

📹 Recordings uploaded automatically from CI run #22005768884

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.

2 participants