Repository navigation
fix(raw): forward piped stdin to wp and refuse "-" without input - #8
Conversation
Bugbot couldn't run - usage limit reachedBugbot 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: ff59e7cd-8ba3-41ce-bbfe-2846b66a74b4) |
Bugbot couldn't run - usage limit reachedBugbot 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: a55dcd17-3e3d-4b54-90ee-a2cecd5b2da2) |
|
PR #7 is now merged into main at For the existing owner: please confirm who is continuing this lane, retarget it to Installed-tool verification and verification of restored production content remain separate from the source merge. |
`wpgo raw -- post update <id> -` ran the remote wp with no stdin. wp read EOF for "-", reported Success and saved an empty post body. raw now forwards local stdin to wp when it is piped or redirected, through a new Adapter.ExecWithStdin backed by SSHClient.ExecWithStdin. When a "-" argument makes wp read stdin and stdin is a terminal, /dev/null or empty, raw refuses before connecting. db export and db dump keep "-" as stdout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Piped SQL reached `wpgo raw -- db query` unchecked, so INTO OUTFILE got past the guard that refuses it in arguments. raw now refuses "-" on every db subcommand except export and dump, and db query with no SQL or --execute. On WP Engine an argument like "'-'" becomes "-" only after the gateway's second parse, so raw let it through with empty stdin. Dash detection now uses the same parse layers as checkRawArgs. raw forwards stdin only when wp reads it for "-", and dry-run prints the command without reading stdin. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Main (#9) now refuses `wp db import` in checkRawArgs before the stdin check runs, so these cases are still refused with nothing sent, under that message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3bd9f66 to
1832e5b
Compare
Bugbot couldn't run - usage limit reachedBugbot 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: 182d8dbf-de5d-45a0-a8db-c459a004b6a7) |
|
Sweep 2026-10-12: rebased onto Evidence:
Not verified: the installed |
Stacked on #7. Merge #7 first, then this PR retargets to
main.Incident
On 2026-10-06, an operator ran
wpgo raw -- post update <id> -on a production WordPress site, piping the new post body into wpgo. wpgo did not forward stdin:RawCmd.RuncalledRunContext.ExecWP, which reachesSSHClient.execInternalwith a nil stdin. The remote wp read EOF for-, printedSuccess, and saved an empty post body. The operator caught the empty posts, unpublished them and restored the content.Change
rawforwards local stdin to the remote wp only when an argument is-, the file argument wp reads from stdin. Other commands get no stdin, so unrelated script input never reaches the server. It uses a newAdapter.ExecWithStdin(standard and WP Engine; WP Engine keeps its 10-minute session limit) andRunContext.ExecWPWithStdin, both backed by the existingSSHClient.ExecWithStdin.-read,rawrefuses before any exec if stdin is a terminal,/dev/nullor empty (0 bytes). The empty check peeks one byte and then streams the rest, so large inputs are not buffered.-detection uses the same parse layers as the fix(backup): write database dumps outside the web root #7 guard (checkRawArgs). On WP Engine, whose gateway parses each word again, an argument such as"'-'"that becomes-only after the second parse counts as a stdin read.rawnever sends stdin towp db(orsql). It refuses-on every db subcommand exceptexportanddump, where-means stdout, and refusesdb querywith no SQL argument or--execute. SQL from stdin would skip the fix(backup): write database dumps outside the web root #7 SQL checks, soprintf "SELECT 1 INTO OUTFILE '/tmp/x';" | wpgo raw -- db queryis refused like the same SQL in argv.--dry-runprints the command without reading or peeking at stdin, even for a-with no input.raw --helpdescribe the new behaviour.Testing
internal/cmd/raw_stdin_test.gorunsRawCmdagainst an in-process SSH server (no live SSH). The WP Engine cases use thewpengineadapter, so the second parse is modelled by the sameshellWordslayer the guard uses.TestRawForwardsPipedStdin: on standard and WP Engine, piped stdin reaches the remotewp post update 7 -and'-'byte for byte, andwp option get homereceives empty stdin.TestRawRefusesStdinReads: refused with no exec on the server:/dev/null, an empty pipe and nil stdin with-;"'-'"with an empty pipe on WP Engine; pipedINTO OUTFILESQL todb query,db query -,sql import -, anddb 'import -'on WP Engine. Still run:db export -with/dev/null, and a command with no-and an empty pipe.TestRawDryRunLeavesStdin: dry-run with-and piped input, or with no stdin, returns without error, sends nothing, and leaves the piped bytes unread.raw.go(43014b2), every new case fails: the WP Engine"'-'"and four SQL-pipe cases reach the server, the no-dash case forwards stdin, and dry-run refuses or consumes stdin.mise exec go@1.25.1 -- make ci(fmt-check, golangci-lint,go test ./...) passes: 0 lint issues, all packages ok.Not tested: a real TTY (the tests use
/dev/null, which takes the same character-device path), the real WP Engine gateway, and a live site.🤖 Generated with Claude Code
Note
Medium Risk
Changes
rawremote execution semantics for stdin and-handling on production WordPress sites; mitigated by pre-connect validation and blocking unchecked SQL via stdin.Overview
Fixes a production incident where
wpgo raw -- post update <id> -did not forward piped stdin, so remote wp read EOF and saved empty post content while still reporting success.rawstdin forwarding: When wp-cli has a-file argument,rawnow streams local piped/redirected stdin to the remote wp viaExecWPStdin; commands without-still send no stdin. It refuses-before SSH when stdin is a TTY, empty, or/dev/null, and--dry-rundoes not peek at or consume stdin.Safety around
wp db: Stdin is never forwarded for database subcommands exceptdb export/dump(where-means stdout).db querywith no SQL/--executeand any otherdbuse of-are blocked so SQL cannot bypass existingrawSQL checks.WP Engine:
-detection uses the same double-parse layers as the existingrawguards (e.g."'-'").README and
raw --helpdocument the behavior. New integration tests use an in-process SSH server to assert forwarding, refusals, and dry-run semantics on standard and WP Engine host types.Reviewed by Cursor Bugbot for commit 1832e5b. Bugbot is set up for automated code reviews on this repo. Configure here.