Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion hooks/claude/rtk-rewrite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ if [ -z "$CMD" ]; then
fi

# Delegate all rewrite + permission logic to the Rust binary.
REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null)
# The `--` terminator is REQUIRED: without it, a command like "--help" or "-h"
# would be interpreted by clap as a flag to `rtk rewrite`, causing the help text
# to be emitted as the rewritten command and fed back to the agent (issue #1350).
REWRITTEN=$(rtk rewrite -- "$CMD" 2>/dev/null)
EXIT_CODE=$?

case $EXIT_CODE in
Expand Down
28 changes: 28 additions & 0 deletions hooks/claude/test-rtk-rewrite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,34 @@ test_rewrite "node (no pattern)" \

echo ""

# ---- SECTION 5b: Flag injection safety (#1350) ----
# Without the `--` terminator before "$CMD", an input like "--help" would be
# interpreted by clap as a flag to `rtk rewrite`, causing the help text to be
# emitted as the "rewritten" command and shell-executed by the agent.
# Each of these inputs must produce NO rewrite.
echo "--- Flag injection safety (#1350) ---"
test_rewrite "flag injection: --help must not trigger clap help" \
"--help" \
""

test_rewrite "flag injection: -h must not trigger clap short help" \
"-h" \
""

test_rewrite "flag injection: --version must not trigger clap version" \
"--version" \
""

test_rewrite "flag injection: -V must not trigger clap short version" \
"-V" \
""

test_rewrite "flag injection: lone -- must not crash the hook" \
"--" \
""

echo ""

# ---- SECTION 6: Audit logging ----
echo "--- Audit logging (RTK_HOOK_AUDIT=1) ---"

Expand Down
5 changes: 4 additions & 1 deletion hooks/cursor/rtk-rewrite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ fi
# Delegate all rewrite logic to the Rust binary.
# Exit codes: 0 = allow rewrite, 1 = no rewrite (passthrough),
# 2 = deny, 3 = ask.
REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null)
# The `--` terminator is REQUIRED: without it, a command like "--help" or "-h"
# would be interpreted by clap as a flag to `rtk rewrite`, causing the help text
# to be emitted as the rewritten command and fed back to the agent (issue #1350).
REWRITTEN=$(rtk rewrite -- "$CMD" 2>/dev/null)
RC=$?
if [ "$RC" -ne 0 ] && [ "$RC" -ne 3 ]; then
echo '{}'
Expand Down
32 changes: 32 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3367,6 +3367,38 @@ mod tests {
}
}

/// SECURITY: Hooks call `rtk rewrite -- "$CMD"` to prevent flag injection.
/// Without the `--` terminator, a command like "--help" or "-h" would be
/// interpreted by clap as a flag to the `rewrite` subcommand — clap would
/// print help text and exit 0, the hook would then feed that help text
/// back to the agent as the rewritten command, which is shell-executed.
/// See issue #1350. This test asserts that `--` causes hyphen-prefixed
/// inputs to be captured as positional args rather than triggering clap's
/// built-in help/version paths.
#[test]
fn test_rewrite_clap_double_dash_blocks_flag_injection() {
let injection_inputs = ["--help", "-h", "--version", "-V"];
for injected in injection_inputs {
let result = Cli::try_parse_from(["rtk", "rewrite", "--", injected]);
assert!(
result.is_ok(),
"`rtk rewrite -- {injected:?}` must parse without triggering clap"
);
if let Ok(cli) = result {
match cli.command {
Commands::Rewrite { ref args } => {
assert_eq!(
args,
&vec![injected.to_string()],
"{injected:?} must be captured as a positional arg, not a flag"
);
}
_ => panic!("expected Rewrite command"),
}
}
}
}

#[test]
fn test_merge_filters_with_no_args() {
let filters = vec![];
Expand Down
Loading