Skip to content

Security: rtk-rewrite.sh missing -- terminator on rtk rewrite "$CMD" (line 55) enables flag-injection #1350

Closed

Description

@Erlend2k

Summary

In hooks/rtk-rewrite.sh (v3, line 55), the call REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null) does not use -- to signal end-of-options. If $CMD starts with - (e.g. a pathological LLM-generated command like --help), rtk rewrite interprets the command as its own CLI flag, emits help text with exit 0, and the hook feeds that help text back to Claude Code as the rewritten tool_input.command.

Claude Code's Bash tool then shell-executes the help text. The help text contains $(rtk rewrite "$CMD") in an example snippet, which bash will expand during execution.

Reproduction (rtk v0.36.0 on Linux x86_64 musl)

$ rtk --version
rtk 0.36.0

$ echo '{"tool_input":{"command":"--help"}}' | bash ~/.claude/hooks/rtk-rewrite.sh
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"RTK auto-rewrite","updatedInput":{"command":"Rewrite a raw command to its RTK equivalent (single source of truth for hooks)\n\n[...]\n\nUsed by Claude Code, Gemini CLI, and other LLM hooks: REWRITTEN=$(rtk rewrite \"$CMD\") || exit 0\n\n[...]"}}}

The updatedInput.command is now the help text — which CC's Bash tool will attempt to execute.

Impact

Severity: High (practical triggerability is low — Claude rarely emits ---prefixed commands — but the exploit path exists: any LLM-generated command starting with -/-- is transformed into arbitrary shell-interpretable text).

Fix

One-line change in hooks/rtk-rewrite.sh:

-REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null)
+REWRITTEN=$(rtk rewrite -- "$CMD" 2>/dev/null)

Also consider:

  • Verify rtk rewrite accepts -- as end-of-options marker (if not, add that to the CLI parser).
  • Add a defensive check in the hook that refuses to auto-allow when $REWRITTEN is empty or starts with non-command-looking text (e.g. looks like help output / contains newlines).

Workaround (users can apply today)

Wrap the hook command in settings.json with a case-guard that refuses any command starting with -:

"command": "INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r '.tool_input.command // empty' 2>/dev/null); case \"$CMD\" in -*) exit 0 ;; esac; echo \"$INPUT\" | /path/to/rtk-rewrite.sh"

This sanitizer approach has the downside that rtk init -g will overwrite settings.json on re-run, so users must use --no-patch on upgrades or live with manually re-applying the sanitizer.

Reported by

Found via Gemini 3.1 Pro second-opinion review during install on Claude Code v2.1.x. Happy to submit a PR if helpful.

Activity

  1. pszymkowiak commented on Apr 17, 2026

    @pszymkowiak
    Collaborator

    wshm · Automated triage by AI

    🔍 Automated Triage

    🐛 Category bug
    🟠 Priority high
    🎯 Confidence 88%

    Summary

    Flag-injection vulnerability in rtk-rewrite.sh where commands starting with '-' are misinterpreted as CLI flags by rtk rewrite; fix is a one-line -- terminator addition.

    This looks like a simple fix that could be auto-resolved. Repo maintainers can use /wshm fix to attempt it.

    📁 Relevant files
    • hooks/rtk-rewrite.sh

    Triaged automatically by wshm · This is an automated analysis, not a human review.

  2. aeppling commented on May 8, 2026

    @aeppling
    Contributor

    Not applicable to current installations. This only affects deprecated shell script hooks (rtk-rewrite.sh). Current RTK uses native binary hooks (rtk hook claude) which are immune — command is extracted from JSON directly, no clap CLI parsing involved. Please upgrade to latest version and run rtk init -g to migrate.

  3. KuSh commented on Aug 12, 2026

    @KuSh
    Collaborator

    Hi, closing to avoid more contributions to that legacy issue

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions