Skip to content

gl surfaces unsanitized node error messages to the terminal on write/mutation commands (write-side of #123) #187

Description

@beardthelion

Summary

The gl CLI and MCP write/mutation commands surface the node's error message to the terminal via anyhow::bail!("... ({status}): {msg}") without sanitizing it. A hostile or MITM node can embed ANSI escape sequences or a Unicode right-to-left override in that message; on a failed write it reaches the operator's terminal verbatim, enabling terminal spoofing (the ANSI-injection / Trojan Source class #183 addressed on the sync path). This is the write-side counterpart of the read-side fix in #123 / #186, which routes read arms through read_json and sanitizes via sanitize_node_msg. The crate is currently mixed: reads sanitized, writes raw.

Where

44 bail!("... {msg}") sites across crates/gl/src surface a raw node message; only one (repo.rs cmd_info, fixed in #186) sanitizes it. Representative write sites: repo.rs cmd_create, pr.rs cmd_create / merge / review / comment, bounty.rs cmd_create and the claim/submit/approve/cancel handlers, plus protect / star / visibility / profile / init / register.

Reproduced by execution

A throwaway test drove a hostile node message (a JSON message field carrying an ESC U+001B and a right-to-left override U+202E, supplied via JSON \u escapes) through gl repo create against a mock node returning 409, and asserted the surfaced error string still contained the raw ESC and RLO. It passed: the raw control bytes reach the terminal unsanitized. The read side, by contrast, strips them (read_json calls sanitize_node_msg).

Why it matters

The node a gl user talks to is user-selectable (--node / GITLAWB_NODE, default node.gitlawb.com), so the error message is attacker-influenceable when a user is pointed at a hostile node or a MITM without cert pinning. ANSI/bidi terminal injection is an active, CVE'd class: Flawfinder's CVE-2026-48813 hides scan results via ANSI escapes in filenames, an ANSI-injection RCE was reported in the OpenAI Codex CLI, WinRAR had screen-spoofing CVEs, and the Trojan Source paper covers the RLO trick. #183 closed the sync-path residual; the write commands were left out of #123's read-only scope.

Fix direction

Route every write/mutation bail! that interpolates a node message through sanitize_node_msg (already pub(crate) in crates/gl/src/sync.rs), the same way read_json and cmd_info do. A small shared "extract and sanitize the node message from an error response" helper would DRY the ~43 remaining sites and make the correct pattern the default, mirroring how read_json did it for the read side.

Severity

Low. Terminal-display spoofing that requires the user to be pointed at a hostile or MITM node; no data leak or auth bypass. Same hardening class as #183; write-side counterpart of the read-side #123.

Activity

  1. added
    kind:securityVulnerability fix or hardening
    sev:lowCosmetic, cleanup, or nice-to-have
    crate:glgl — the contributor CLI
    subsystem:apiNode REST API request/response surface
    on Jul 11, 2026
  2. added
    sev:mediumDegraded but workaround exists
    and removed
    sev:lowCosmetic, cleanup, or nice-to-have
    on Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    crate:glgl — the contributor CLIkind:securityVulnerability fix or hardeningsev:mediumDegraded but workaround existssubsystem:apiNode REST API request/response surface

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions