Skip to content

fix: escape Rich fragments in CLI status messages - #13088

Open
JayyBG wants to merge 9 commits into
BasedHardware:mainfrom
JayyBG:fix/cli-rich-status-escape
Open

fix: escape Rich fragments in CLI status messages#13088
JayyBG wants to merge 9 commits into
BasedHardware:mainfrom
JayyBG:fix/cli-rich-status-escape

Conversation

@JayyBG

@JayyBG JayyBG commented Sep 8, 2026

Copy link
Copy Markdown

Several CLI status/error messages interpolate user-controlled profile names and config values into Rich markup strings. Markup-like values such as bad[/bold]profile can make a command fail while rendering the success/error message, even after the state change has already happened.

This patch escapes dynamic fragments in config and auth status messages while preserving CLI-owned Rich styling. It also escapes Renderer.error() message/detail output at the pretty-mode renderer boundary, leaving JSON-mode stderr payloads raw and machine-readable.

Regression coverage includes:

  • config profile use with a markup-like profile name
  • config set with a markup-like profile name and value
  • config profile delete no-such-profile error rendering
  • auth logout with a markup-like profile name
  • auth refresh pretty error details for an API-key profile with a markup-like name
  • --json auth refresh preserving the raw profile name in stderr JSON details

Fixes #13086.

This contribution is AI-assisted. A small US$5 PayPal bounty was proposed on #13086; no award or payment is assumed, and payout details will remain private unless maintainers approve payment after acceptance/merge.

Validation: fetched the published branch head and Python-compiled the touched files successfully. Hosted CI remains authoritative for the complete suite.

Review in cubic

JayyBG commented Sep 8, 2026

Copy link
Copy Markdown
Author

Additional validation on the published branch head a76042e8e7803583962a946780318affc3445e74: I fetched the exact changed files from JayyBG:fix/cli-rich-status-escape and Python-compiled them successfully:

  • sdks/python-cli/omi_cli/commands/config.py
  • sdks/python-cli/omi_cli/commands/auth.py
  • sdks/python-cli/tests/test_rich_status_escape.py

This PR intentionally follows up on the broader status-message surfaces noted in the review discussion for #12977. #12977 is still the smaller config-only fix; this one includes auth message/error details plus focused regression coverage for the new issue #13086.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread sdks/python-cli/omi_cli/commands/auth.py Outdated

JayyBG commented Sep 8, 2026

Copy link
Copy Markdown
Author

Updated this PR to cover the generic pretty-mode error rendering path too.

New branch head: d38d2141beaa48339eceb01f78440c04f1d84fec

Follow-up changes:

  • Renderer.error() now escapes message and detail in pretty mode, while JSON-mode stderr keeps raw structured fields for machine consumers.
  • Added a regression for omi config profile delete 'bad[/bold]profile' --yes, covering the no-such-profile error path without raising MarkupError.

Validation on the published branch head: fetched and Python-compiled the touched files successfully:

  • sdks/python-cli/omi_cli/output.py
  • sdks/python-cli/omi_cli/commands/config.py
  • sdks/python-cli/omi_cli/commands/auth.py
  • sdks/python-cli/tests/test_rich_status_escape.py

No payout details are posted here; bounty/payment discussion can stay private if maintainers decide to award one after review/merge.

@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks @JayyBG — reviewed against #13086 and verified end to end. This is a clean fix for a real bug class.

What I verified

  • Root cause is real. Renderer.success/warn wrap their argument in Rich markup (output.py, the [green]✓[/green] {message} pattern), so dynamic values containing [/…]-like fragments are parsed as markup and can raise MarkupError after the state change has already committed — exactly the Python CLI Rich status messages crash on markup-like profile names #13086 symptom.
  • output.pyRenderer.error() escaping message/detail/extra at the pretty-mode boundary is the strongest part: every CliError funnels through it (errors.py CliError.show() and main.py _exit_with_cli_error), so the generic error path is closed in one place. I checked all existing renderer.error(...) call sites across the CLI — none pass intentional Rich markup, so nothing now renders with visible escape backslashes.
  • commands/auth.py — all user-derived fragments are escaped (provider, profile.name, profile.masked_credential(), exc.message). Keeping UsageError.detail raw (in auth refresh and _ensure_authenticated) is correct: JSON stderr stays machine-readable and pretty mode is protected by the renderer-boundary escape.
  • commands/config.pyset_value, profile_use, profile_delete cover the config paths from the issue. One nit: the file now ends without a trailing newline (\ No newline at end of file in the diff) — trivial to restore.
  • tests/test_rich_status_escape.py — good regression set. The exit_code != 0 + MarkupError not in stderr assertions genuinely fail pre-fix (the post-commit render crash), and test_json_auth_refresh_error_detail_keeps_raw_profile_name pins the raw-JSON contract (no \[ in stderr details). I also sanity-checked rendering offline: escape("bad[/bold]profile") renders literally through the success() markup path with no MarkupError.

Follow-up (non-blocking, not this PR's job): the same unescaped pattern remains in sibling commands — commands/local.py (Configured local Omi Desktop API for profile [bold]{profile.name}[/bold], same class as this bug) and the [bold]{id}[/bold] interpolations in commands/memory.py, conversation.py, action_item.py, goal.py (mostly server-generated IDs, lower risk). A follow-up escaping those — or moving escaping into Renderer.success/warn — would close the class across the CLI.

No CI has run on this head yet (no check-runs or statuses), so hosted CI remains the authority on the full suite.

Nice work — especially the renderer-boundary escape and the JSON-contract test. Merge needs maintainer sign-off (security-labeled change, first-time contributor, and the proposed bounty in the description is a maintainer call); leaving for human maintainer review.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level added security-review Touches auth, provider routing, secrets, or security-sensitive surfaces positive-signal Good contribution; verified by automated maintainer review python labels Sep 8, 2026

JayyBG commented Sep 8, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough verification. I restored the trailing newline in commands/config.py as noted, and while checking the refreshed diff I found the same EOF-newline cleanup was also needed in commands/auth.py, so that is fixed too. Current head: 88d1383667faf294c3824855818b7491b60dab85. No behavioral changes beyond the reviewed fix.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

cli bug fix, confidence 5/5: fixes #13086 (markup-like profile names/values crash or corrupt auth/config status messages), scoped diff, 6 regression tests, confirmed not already on main before this batch.

JayyBG commented Sep 8, 2026

Copy link
Copy Markdown
Author

Upstream changed underneath this branch after #12963 and #12977 merged. I rechecked current main (9ff63a6): the config literal-output work and the generic Renderer.error() literal-data boundary are now already present upstream, while the auth status-message escaping in this PR is still relevant. I’ve enabled maintainer edits on the branch. When resolving the conflict, please prefer current main for config.py/output.py and retain the auth escaping plus the still-relevant regression coverage rather than reintroducing the superseded implementations. No additional bounty is requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

positive-signal Good contribution; verified by automated maintainer review python security-review Touches auth, provider routing, secrets, or security-sensitive surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python CLI Rich status messages crash on markup-like profile names

3 participants