fix: escape Rich markup in catalog list output (#3738)

The `catalog list` subcommands for workflows, workflow steps, presets,
and integrations printed user-editable catalog fields (name/url/
description from the `*-catalogs.yml` files) through `console.print`
with Rich markup enabled. Any bracketed content such as a description
`Does [stuff] nicely` was parsed as a style tag and silently swallowed,
and a malformed tag could raise while rendering.

Route each untrusted field through the module's already-imported
`escape` helper, matching the pattern already used by
`extension catalog list`.

Adds regression tests for all four commands that inject bracketed
name/url/description and assert the brackets survive verbatim in the
output.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Noor ul ain
2026-07-27 20:15:37 +05:00
committed by GitHub
parent 103ad73775
commit 99dc915ae3
6 changed files with 124 additions and 13 deletions

View File

@@ -489,13 +489,14 @@ def integration_catalog_list():
display_name = str(raw_name).strip() if raw_name is not None else ""
if not display_name:
display_name = f"catalog-{i + 1}"
safe_name = _rich_escape(display_name)
if env_override or project_configs is None:
console.print(f" - [bold]{display_name}[/bold] — {install_status}")
console.print(f" - [bold]{safe_name}[/bold] — {install_status}")
else:
console.print(f" [{i}] [bold]{display_name}[/bold] — {install_status}")
console.print(f" {cfg.get('url', '')}")
console.print(f" [{i}] [bold]{safe_name}[/bold] — {install_status}")
console.print(f" {_rich_escape(str(cfg.get('url', '')))}")
if cfg.get("description"):
console.print(f" [dim]{cfg['description']}[/dim]")
console.print(f" [dim]{_rich_escape(str(cfg['description']))}[/dim]")
console.print()

View File

@@ -580,10 +580,10 @@ def preset_catalog_list():
if entry.install_allowed
else "[yellow]discovery only[/yellow]"
)
console.print(f" [bold]{entry.name}[/bold] (priority {entry.priority})")
console.print(f" [bold]{_escape_markup(str(entry.name))}[/bold] (priority {entry.priority})")
if entry.description:
console.print(f" {entry.description}")
console.print(f" URL: {entry.url}")
console.print(f" {_escape_markup(str(entry.description))}")
console.print(f" URL: {_escape_markup(str(entry.url))}")
console.print(f" Install: {install_str}")
console.print()

View File

@@ -2424,10 +2424,10 @@ def workflow_catalog_list():
console.print("\n[bold cyan]Workflow Catalog Sources:[/bold cyan]\n")
for i, cfg in enumerate(configs):
install_status = "[green]install allowed[/green]" if cfg["install_allowed"] else "[yellow]discovery only[/yellow]"
console.print(f" [{i}] [bold]{cfg['name']}[/bold] — {install_status}")
console.print(f" {cfg['url']}")
console.print(f" [{i}] [bold]{_escape_markup(str(cfg['name']))}[/bold] — {install_status}")
console.print(f" {_escape_markup(str(cfg['url']))}")
if cfg.get("description"):
console.print(f" [dim]{cfg['description']}[/dim]")
console.print(f" [dim]{_escape_markup(str(cfg['description']))}[/dim]")
console.print()
@@ -3067,10 +3067,10 @@ def workflow_step_catalog_list():
if cfg["install_allowed"]
else "[yellow]discovery only[/yellow]"
)
console.print(f" [{i}] [bold]{cfg['name']}[/bold] — {install_status}")
console.print(f" {cfg['url']}")
console.print(f" [{i}] [bold]{_escape_markup(str(cfg['name']))}[/bold] — {install_status}")
console.print(f" {_escape_markup(str(cfg['url']))}")
if cfg.get("description"):
console.print(f" [dim]{cfg['description']}[/dim]")
console.print(f" [dim]{_escape_markup(str(cfg['description']))}[/dim]")
console.print()