fix: escape workflow validation errors before Rich output

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
marcelsafin
2026-07-10 19:58:14 +02:00
parent 4ca50f588b
commit 10f9770f0c
2 changed files with 31 additions and 3 deletions

View File

@@ -374,7 +374,7 @@ def workflow_run(
if errors:
err.print("[red]Workflow validation failed:[/red]")
for verr in errors:
err.print(f"{verr}")
err.print(f"{_escape_markup(str(verr))}")
raise typer.Exit(1)
# Parse inputs
@@ -645,7 +645,7 @@ def workflow_add(
if errors:
console.print("[red]Error:[/red] Workflow validation failed:")
for err in errors:
console.print(f" \u2022 {err}")
console.print(f" \u2022 {_escape_markup(str(err))}")
raise typer.Exit(1)
if expected_id is not None and definition.id != expected_id:
@@ -903,7 +903,7 @@ def _install_workflow_from_catalog(
shutil.rmtree(workflow_dir, ignore_errors=True)
console.print("[red]Error:[/red] Downloaded workflow validation failed:")
for err in errors:
console.print(f" \u2022 {err}")
console.print(f" \u2022 {_escape_markup(str(err))}")
raise typer.Exit(1)
# Enforce that the workflow's internal ID matches the catalog key

View File

@@ -5905,6 +5905,34 @@ class TestWorkflowAddSymlinkGuard:
assert result.exit_code != 0
assert "symlinked .specify/workflows" in result.output
def test_add_escapes_rich_markup_in_validation_errors(self, temp_dir, monkeypatch):
"""User-controlled YAML values in validation errors must not be parsed as Rich markup."""
from typer.testing import CliRunner
from specify_cli import app
(temp_dir / ".specify" / "workflows").mkdir(parents=True)
src = temp_dir / "incoming.yml"
src.write_text(
"""
schema_version: "1.0"
workflow:
id: "markup-wf"
name: "Markup"
version: "[bold]bad[/bold]"
steps:
- id: step-one
command: speckit.specify
""",
encoding="utf-8",
)
monkeypatch.chdir(temp_dir)
result = CliRunner().invoke(app, ["workflow", "add", str(src)])
assert result.exit_code != 0
assert "[bold]bad[/bold]" in result.output
@pytest.mark.skipif(not hasattr(os, "symlink"), reason="symlinks are unavailable")
def test_add_refuses_symlinked_id_dir(self, temp_dir, monkeypatch, sample_workflow_yaml):
"""A symlinked <id> install dir must not let a copy escape the project root."""