Skip to content

close: TUI bypass can be spoofed by any HTTP client #38

Description

@wesm

The kata close substance-and-evidence gate (introduced in #32) has an
escape hatch for the TUI: when the close request carries
source: "tui" and reason: "done", the daemon skips
ValidateCloseInput and evidence validation. The intent is that
pressing x in kata tui doesn't force the user to type a 40-char
message and attach typed evidence for an interactive close.

The problem

source is just a string on the JSON request body. Any HTTP client
that can reach the daemon can POST

POST /api/v1/projects/{id}/issues/{ref}/actions/close
{"actor": "agent", "source": "tui", "reason": "done"}

and close the issue with no message and no evidence, bypassing the
anti-abuse policy that the rest of kata close enforces. In the
current no-auth model, the daemon has no trustworthy way to
distinguish a real interactive TUI caller from a forged one.

Options

  1. Transport gate. Refuse source=tui when the connection arrives
    over TCP; honor it only on the local Unix socket. Closes the spoof
    vector against any remote attacker but breaks the keystroke bypass
    for legitimate remote-TUI users (they would have to fall back to
    kata close --done --message ... from the CLI).
  2. Drop the bypass. Require --message and --evidence from every
    close caller, including the TUI. The x key would prompt for a
    one-line message. More friction for humans, no policy hole.
  3. Wait for auth/session identity. Derive "interactive UI session"
    trust from an authenticated principal rather than a self-declared
    JSON field. The proper long-term fix once kata grows real
    authentication.

Interim mitigation

Persist source on the issue.closed event payload so
kata audit closes can flag and filter bypass closes. This isn't a
guard, but it makes abuse visible to reviewers.

Flagged by an automated security review on the PR that introduced the
gate (#32). Deferred from that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions