fix: harden recursive command parsing - #572
Conversation
|
Important Review skippedNo new commits to review since the last review. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough
ChangesCommand parser safety
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 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 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/command-manager.ts (1)
249-259: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick winReject leading
$command tokens as unsafe.extractBaseCommand()still skips$FOObefore validation, so$FOO echo blockedis treated asechoinstead of rejecting the dynamically resolved executable. That leaves the same shell-expansion gap you closed for${...}.🤖 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 `@src/command-manager.ts` around lines 249 - 259, Update extractBaseCommand() so dollar-prefixed tokens representing the command executable are rejected as unsafe rather than skipped during the initial token scan. Preserve the existing allowance for $() command substitutions and validation of ${...}, but ensure inputs such as $FOO echo blocked throw UnsafeDynamicCommandError instead of selecting echo as the base command.
🧹 Nitpick comments (1)
src/command-manager.ts (1)
275-280: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUnreachable guard.
Any token beginning with
${already throws at Line 252 inside the token loop, beforefirstTokenis assigned, sofirstTokencan never start with${here. This branch is dead code. Safe to drop (or keep the guard only at Line 277 and remove the in-loop one), but avoid duplicating the same check.🤖 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 `@src/command-manager.ts` around lines 275 - 280, Remove the unreachable `${` check from the block using firstToken in the command validation flow, since the token loop already throws UnsafeDynamicCommandError before firstToken is assigned. Keep the existing validation behavior and avoid duplicating the guard between the token loop and the firstToken handling.
🤖 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.
Outside diff comments:
In `@src/command-manager.ts`:
- Around line 249-259: Update extractBaseCommand() so dollar-prefixed tokens
representing the command executable are rejected as unsafe rather than skipped
during the initial token scan. Preserve the existing allowance for $() command
substitutions and validation of ${...}, but ensure inputs such as $FOO echo
blocked throw UnsafeDynamicCommandError instead of selecting echo as the base
command.
---
Nitpick comments:
In `@src/command-manager.ts`:
- Around line 275-280: Remove the unreachable `${` check from the block using
firstToken in the command validation flow, since the token loop already throws
UnsafeDynamicCommandError before firstToken is assigned. Keep the existing
validation behavior and avoid duplicating the guard between the token loop and
the firstToken handling.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8f3c0e86-78dc-4ea5-9c1f-311cea67ebb8
📒 Files selected for processing (2)
src/command-manager.tstest/test-command-parser-limits.js
Summary
<()and>())${...}when shell expansion controls the executable nameRoot cause
extractCommands()recursively parsed command substitutions and subshells without a depth or total-work limit. User-controlled deeply nested input could therefore consume excessive CPU or exhaust the JavaScript stack.Returning an empty result at the limit would be unsafe because
validateCommand()falls back to checking only the leading base command. This change instead propagates a typed parsing-limit error and rejects the full command.Impact
Deeply nested or disproportionately expensive command strings are denied without crashing the server or amplifying logs. Normal nested command parsing and existing blocklist behavior remain unchanged.
Fixes #567.
Fixes #556.
Fixes #555.
Fixes #497.
Validation
npm run buildnode test/test-command-parser-limits.jsnode test/test-blocklist-bypass.jsnode test/test-blocked-commands.jsSummary by CodeRabbit