Skip to content

hook: refuse a quoted command string passed through a login shell - #508

Open
EdbertChan wants to merge 1 commit into
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/count-bogus-didn-t-run-same-class--065043b6from
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/refuse-quoted-cmd-string-passed-login-shell--acc7d623
Open

hook: refuse a quoted command string passed through a login shell#508
EdbertChan wants to merge 1 commit into
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/count-bogus-didn-t-run-same-class--065043b6from
stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/refuse-quoted-cmd-string-passed-login-shell--acc7d623

Conversation

@EdbertChan

@EdbertChan EdbertChan commented Sep 12, 2026

Copy link
Copy Markdown
Owner

Summary

This part checks commands before they run and stops a risky remote handoff that can break for a person.

The problem was a handoff script failing after a login shell changed the command.

The cause was a second parse that removed quoting and split the command string again.

The fix blocks this shape and explains how to avoid it by copying the script and running it by path.

Review Claim

Approve stopping quoted remote command strings that a login shell will parse again, with clear guidance for a safe replacement.

Review Lane

behavior

Review Unit

engine-runtime

Safety Invariant

This adds a fail-open command check, changes no unrelated command shapes, and keeps script-by-path execution allowed. The change is limited to the new guard and its wiring, documentation, and tests.

Slice Rationale

This slice adds one guard with its installation, documentation, and regression fixtures so the behavior and its supported escape hatch are reviewed together.

The existing evidence reports a reproduction on a real host and a backtest over 37,015 real Bash commands with 6 hits, all matching the broken shape.

Non-goals

  • Do not block interactive login shells with no command.
  • Do not block scripts copied to a host and run by path.
  • Do not block heredocs or plain multi-line input sent over standard input.
  • Do not probe hosts, read remote files, or change shell behavior.
  • Do not change unrelated hooks, installation behavior, labels, comments, branches, or pull requests.
  • The main README row for this hook is in the docs PR at the top of this stack, not here.

Test Plan

Test Plan
  • python3 -m unittest discover -s engine/hooks/remote-payload-collapses/tests -v — unit coverage for blocking and allowed command shapes.
  • python3 tests/test_install.py — installation wiring coverage.
  • Reproduction on a real host: without the login flag, the body prints; with it, Bash reports set: -c: invalid option.
  • Backtest over 37,015 real Bash commands: 6 hits, all the broken shape.

Revert Plan

Revert Plan
  • Safe to revert? Yes.
  • Revert command: gh api -X PATCH repos/EdbertChan/catstack/pulls/508 -F body=@/tmp/pr-body-rewrite/508.md.orig
  • Post-revert steps: Re-run the PR body checker against the restored body.
  • Data migration? No.

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01KU2pPKob4MJ1NqjsfTNyYJ

Depends-On: #507

@EdbertChan

EdbertChan commented Sep 12, 2026

Copy link
Copy Markdown
Owner Author

This pull request is part of a Mergify stack:

# Pull Request Link
1 hook: a script handed to the user is a claim that it runs #506
2 audit(token_audit): count "bogus" and "it didn't run" as the same class #507
3 hook: refuse a quoted command string passed through a login shell #508 👈
4 gate: a branch taken per operating system needs a test that injects one #509
5 audit(token_audit): one submission carrying two slash commands is one message #510
6 audit: use the transcript's own markers for what the human actually sent #511
7 README: list the handoff-needs-smoke-test and remote-payload-collapses hooks #535

@cursor

cursor Bot commented Sep 12, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b4f81974-f0cc-47e1-bd46-3f4d0b6ad249)

EdbertChan added a commit that referenced this pull request Sep 13, 2026
…ody checker on PR #508's live body after the rewrite.

Goal: Prove the body passes as published.
Motivation: A PATCH that returned 200 is not proof the live body passes.
Safety invariant: Read-only; a failed fetch fails the task instead of passing.
Verify: exit code 0 and output PR body validation passed.
Effectiveness measurement: The same checker the required GitHub check runs passes on the live body, so the PR is no longer blocked on its text.

Exit code: 0
`sudo -i` starts the target user's login shell, and that shell parses the
remaining arguments a second time. The quoting the first parse consumed is
gone by then, so the command string is re-split and its first word becomes
the whole command. A handoff script died this way in a user's terminal.

Reproduced on a real host, one variable apart: `sudo -u demo -H bash -lc
'<body>'` prints the body's output, and the same line with `-i` gives
`bash: line 1: set: -c: invalid option`.

Newlines are not the trigger, which is what the first reading of the
failure got wrong. A multi-line body survives ssh and survives sudo without
`-i`.

Backtested over 37,015 real Bash commands: 6 hits, all the broken shape.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KU2pPKob4MJ1NqjsfTNyYJ
Change-Id: Iacc7d62317959adeb953d4fef0bc9dc222a37430
EdbertChan added a commit that referenced this pull request Sep 13, 2026
…ody checker on PR #508's live body after the rewrite.

Goal: Prove the body passes as published.
Motivation: A PATCH that returned 200 is not proof the live body passes.
Safety invariant: Read-only; a failed fetch fails the task instead of passing.
Verify: exit code 0 and output PR body validation passed.
Effectiveness measurement: The same checker the required GitHub check runs passes on the live body, so the PR is no longer blocked on its text.

Exit code: 0
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/count-bogus-didn-t-run-same-class--065043b6 branch from b680e2b to ce9983e Compare September 13, 2026 05:55
@EdbertChan
EdbertChan force-pushed the stack/EdbertChan/reflect/ui-input-guard-hook-freshness-20260908/refuse-quoted-cmd-string-passed-login-shell--acc7d623 branch from d76e1e5 to 7d7ef8b Compare September 13, 2026 05:55
@cursor

cursor Bot commented Sep 13, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_7ae2b92a-d3dc-45f5-af7c-fe78a14d1509)

@EdbertChan

Copy link
Copy Markdown
Owner Author

Revision history

# Type Changes Reason Date
1 initial d76e1e5 2026-09-13 05:55 UTC
2 content d76e1e5 → 7d7ef8b (raw) 2026-09-13 05:55 UTC

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant