Anchor SSH argument safe-set check with \A..\z to reject trailing newlines - #6384
Conversation
When assembling the remote command in `run_ssh_command()`, an argument is passed through unquoted when it matches the "safe characters" pattern. That pattern used `^...$`, but PCRE's `$` also matches immediately before a trailing newline, so a value ending in "\n" was considered safe and emitted raw into the remote shell command — where the newline acts as a command separator. Anchor the pattern with `\A` and `\z` (as `assoc_args_to_str()` already does) so any value containing a newline is routed through `escapeshellarg()`. This is not known to be exploitable today — the arguments here come from the operator's own argv — but it removes a latent quoting bypass and makes the anchoring consistent across the codebase. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LV1bNtxNCZ3QXujJHYfhZv
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe SSH argument safety check now uses PCRE absolute anchors. Arguments that end with a newline no longer bypass escaping. A feature scenario verifies the behavior. ChangesSSH argument safety
Estimated code review effort: 2 (Simple) | ~5 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This pull request hardens WP-CLI’s SSH execution path by ensuring arguments that end with a trailing newline are not incorrectly treated as “safe” and emitted unquoted into the remote shell command.
Changes:
- Replaces the safe-argument allowlist anchors from
^…$to\A…\zinRunner::run_ssh_command()to prevent$matching before a trailing newline. - Adds inline rationale explaining the PCRE anchoring edge case and the remote-shell command-separator risk.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
php/WP_CLI/Runner.php (1)
1047-1050: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a regression test for trailing-newline arguments.
Add a test that passes an argument ending in
"\n"throughrun_ssh_command()and verifies that it is escaped instead of emitted as a raw command separator. The existing validation test does not cover this exact path.As per coding guidelines, run
composer testbefore submitting changes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@php/WP_CLI/Runner.php` around lines 1047 - 1050, Add a regression test covering run_ssh_command() with an argument ending in "\n", asserting the newline-containing value is escaped rather than emitted as a raw command separator. Keep the existing validation coverage intact and run composer test to verify the change.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@php/WP_CLI/Runner.php`:
- Around line 1047-1050: Add a regression test covering run_ssh_command() with
an argument ending in "\n", asserting the newline-containing value is escaped
rather than emitted as a raw command separator. Keep the existing validation
coverage intact and run composer test to verify the change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 568ddf8f-8679-465e-8439-3b7f194ece79
📒 Files selected for processing (1)
php/WP_CLI/Runner.php
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Covers the \A..\z anchor change in run_ssh_command(): an argument with a trailing newline must be routed through escapeshellarg() instead of being emitted raw into the remote shell command. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PyYsgTUw9BZvzptwJVoRnh
Summary
When
run_ssh_command()assembles the command to run on the remote host, each argument is passed through unquoted when it matches a "safe characters" allowlist, and is otherwise wrapped inescapeshellarg(). That allowlist regex was anchored with^…$.In PCRE,
$also matches immediately before a trailing newline, so an argument ending in"\n"matched the "safe" pattern and was emitted raw into the remote shell command — where the newline acts as a command separator.This changes the anchors to
\A…\z(whichassoc_args_to_str()already uses), so any value containing a newline is routed throughescapeshellarg().Impact
Defense-in-depth only — not known to be exploitable. The arguments here come from the operator's own
argv(array_slice( $GLOBALS['argv'], 1 )), so there is no lower-privilege source that can introduce a newline. The change removes a latent quoting bypass and makes newline anchoring consistent with the rest of the codebase.Behavior
Only arguments containing a newline change behavior (now escaped); normal arguments are unaffected:
^…$)\A…\z)user--url=https://e.com/p"foo\n""a\nb"🤖 Generated with Claude Code
https://claude.ai/code/session_01LV1bNtxNCZ3QXujJHYfhZv
Generated by Claude Code
Summary by CodeRabbit