Skip to content

Fix command injection via newline and quoting/wildcard blocklist bypass#429

Open
Christian-Sidak wants to merge 4 commits intowonderwhy-er:mainfrom
Christian-Sidak:fix/command-injection-blocklist-bypass
Open

Fix command injection via newline and quoting/wildcard blocklist bypass#429
Christian-Sidak wants to merge 4 commits intowonderwhy-er:mainfrom
Christian-Sidak:fix/command-injection-blocklist-bypass

Conversation

@Christian-Sidak
Copy link
Copy Markdown

@Christian-Sidak Christian-Sidak commented Apr 10, 2026

User description

Summary

Fixes two critical command injection vulnerabilities in src/command-manager.ts that allow complete bypass of the blockedCommands security feature:

  • Newline bypass ([Security] Command Injection via Blocklist Bypass using Newline Character #422): extractCommands() did not treat newline characters as command separators. An attacker could inject echo benign\nrm -rf / and validation would only check echo, while the shell would execute both commands. Fixed by adding \r\n, \n, \r to the separators array.

  • Quoting and wildcard bypass ([Security] Command Injection via Blocklist Bypass using Quoting and Wildcards #421): extractBaseCommand() did not strip quotes from tokens, so "rm" would not match the blocked command rm. Wildcard paths like /usr/bin/su*o would also evade literal blocklist matching but expand at shell execution time. Fixed by stripping surrounding quotes before comparison and rejecting any command containing glob characters (*, ?, [, ]).

Changes

All changes are in src/command-manager.ts:

  • Add \r\n, \n, \r to the separators array in extractCommands()
  • Strip surrounding single/double quotes from command tokens in extractBaseCommand()
  • Reject commands containing glob/wildcard characters in both extractBaseCommand() and validateCommand()

Test plan

  • Verify echo benign\nrm file is correctly split into two commands and rm is blocked
  • Verify "rm" file is correctly identified as rm and blocked
  • Verify /usr/bin/su*o is rejected outright due to glob characters
  • Verify normal commands without quotes, globs, or newlines continue to work
  • Verify existing test suite passes (npm test)

Closes #421
Closes #422

Summary by CodeRabbit

  • Bug Fixes
    • Commands containing wildcard or brace characters are now rejected to prevent unsafe patterns.
    • Line breaks are recognized and treated as separators so each line is validated independently.
    • Command tokens are normalized (quotes removed) for consistent validation.
    • Inputs fail-closed: any invalid segment now causes the whole input to be rejected.

CodeAnt-AI Description

Block command-blocklist bypasses through newlines, quotes, and wildcard command names

What Changed

  • Commands split across new lines are now checked separately, so blocked commands can no longer hide after a line break.
  • Quoted or split-up command names like "rm", "r"m, or 'r''m' are now recognized as the blocked command they represent.
  • Command names containing wildcard characters such as *, ?, [ or ] are now rejected to stop path-based bypasses like /usr/bin/su*o.

Impact

✅ Fewer command-injection bypasses
✅ Safer blocked-command enforcement
✅ Lower risk of hidden shell commands

🔄 Retrigger CodeAnt AI Review

Details

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Addresses two critical security vulnerabilities in command validation:

1. Newline bypass (wonderwhy-er#422): The extractCommands() separator array did not
   include newline characters (\n, \r\n, \r). An attacker could chain a
   benign command with a blocked command using a newline, and the shell
   would execute both while validation only checked the first.

2. Quoting and wildcard bypass (wonderwhy-er#421): extractBaseCommand() did not strip
   surrounding quotes from command tokens, so "rm" would not match the
   blocked command rm. Additionally, glob characters (*, ?, [, ]) in
   command paths (e.g. /usr/bin/su*o) would evade literal blocklist
   matching but expand to the real binary at shell execution time.

Changes:
- Add \r\n, \n, \r to the command separators array
- Strip single and double quotes from command tokens before comparison
- Reject any command containing glob/wildcard characters in both
  extractBaseCommand() and validateCommand()
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 10, 2026

CodeAnt AI is reviewing your PR.


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

extractCommands now treats CR/LF/newline as command separators; extractBaseCommand strips surrounding quotes, rejects tokens containing glob/brace characters by returning null (propagated as 'INVALID_COMMAND'); validateCommand enforces fail-closed behavior when invalid or glob-containing tokens are present.

Changes

Cohort / File(s) Summary
Command Parsing & Validation
src/command-manager.ts
extractCommands() splits on \r\n, \n, \r; extractBaseCommand() removes single/double quotes from the first token and returns null for tokens containing glob/wildcard (* ? [ ]) or brace ({ }) chars (pushes '__INVALID_COMMAND__' sentinel). validateCommand() fail-closes when extracted list is empty and fallback contains globs or when any segment is '__INVALID_COMMAND__'.

Sequence Diagram(s)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

size:L

Suggested reviewers

  • es-dc

Poem

🐰 I hopped through strings and split each line,
I peeled off quotes so tokens shine,
I sniffed for stars and braces too,
Sent invalid hops back to you,
A safer warren — carrot-cheer, hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: fixing command injection vulnerabilities via newline separator bypass and quoting/wildcard blocklist bypass.
Linked Issues check ✅ Passed The PR addresses all core requirements from #421 and #422: adding newline separators, stripping quotes, rejecting glob/brace-expansion characters, and implementing fail-closed validation with sentinel propagation.
Out of Scope Changes check ✅ Passed All changes in src/command-manager.ts are directly scoped to fixing the two command-injection vulnerabilities described in the linked issues.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codeant-ai codeant-ai Bot added the size:S This PR changes 10-29 lines, ignoring generated files label Apr 10, 2026
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 10, 2026

CodeAnt AI finished reviewing your PR.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/command-manager.ts`:
- Around line 221-225: The firstToken quote-stripping only removes a perfectly
wrapped pair and misses concatenated quoted/unquoted fragments (e.g. `"r"m`), so
before you perform the basename/blocklist check normalize quoting across the
whole token (or replace internal single/double quote characters) so `"r"m`,
`r"m"`, and `'r''m'` become rm, or alternatively parse the command with a
shell-aware tokenizer; apply this change where firstToken is prepared for the
basename/blocklist comparison so the blocklist check sees the actual command
name.
- Around line 248-252: The glob-wildcard rejection currently tests the entire
input string; change it to only test the resolved executable token by calling
getBaseCommand(command) (the same helper used at line 229) and run the
/[*?\[\]]/ test against that returned base command; if the base command contains
glob chars return false, otherwise allow the rest of the command to contain
quoted/escaped metacharacters. Ensure you preserve the existing regex and return
behavior but switch the input from the full command string to
getBaseCommand(command).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b958ab6e-f205-404e-8dcf-fc5139545bdc

📥 Commits

Reviewing files that changed from the base of the PR and between eb139ac and b3259de.

📒 Files selected for processing (1)
  • src/command-manager.ts

Comment thread src/command-manager.ts Outdated
Comment thread src/command-manager.ts Outdated
…utable token

Address CodeRabbit review feedback:
- Quote stripping now removes all quote chars instead of only outer pairs,
  so concatenated fragments like "r"m, r"m", 'r''m' resolve to the actual
  command name and get caught by the blocklist.
- Glob/wildcard rejection moved from validateCommand (entire input) into
  extractBaseCommand (executable token only), so legitimate args with
  wildcards like `ls *.txt` are no longer blocked.
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 11, 2026

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:S This PR changes 10-29 lines, ignoring generated files and removed size:S This PR changes 10-29 lines, ignoring generated files labels Apr 11, 2026
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 11, 2026

CodeAnt AI Incremental review completed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

250-253: ⚠️ Potential issue | 🟠 Major

Glob bypass via fallback path to unprotected getBaseCommand.

When extractBaseCommand returns null due to glob characters, the command isn't added to allCommands, making it empty. This triggers the fallback to getBaseCommand, which lacks both quote stripping and glob rejection.

Bypass example:

  1. Input: /usr/bin/su*o or su*o
  2. extractBaseCommand returns null (glob detected)
  3. allCommands is empty → fallback triggered
  4. getBaseCommand returns /usr/bin/su*o (no protection)
  5. Doesn't match sudo in blocklist → command allowed

Consider rejecting commands when extractBaseCommand returns null due to glob patterns, or apply the same protections in the fallback path.

🔒 Proposed fix: fail-closed when glob causes extraction failure
             // If there are no commands extracted, fall back to base command
             if (allCommands.length === 0) {
                 const baseCommand = this.getBaseCommand(command);
+                // Reject if the base command contains glob/wildcard characters
+                if (/[*?\[\]]/.test(baseCommand)) {
+                    return false;
+                }
                 return !blockedCommands.includes(baseCommand);
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/command-manager.ts` around lines 250 - 253, The current fallback path
uses getBaseCommand when allCommands is empty, allowing commands with globs to
bypass checks; update the logic so that if extractBaseCommand returns null for
any input (indicating a glob/invalid extraction) you fail-closed — i.e., treat
the command as blocked instead of falling back — or alternatively make the
fallback call reuse the same protections as extractBaseCommand (strip quotes and
detect/reject glob patterns) before consulting blockedCommands; locate
references to allCommands, extractBaseCommand, and getBaseCommand in the command
filtering code and enforce the fail-closed behavior or identical sanitization on
the fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/command-manager.ts`:
- Around line 250-253: The current fallback path uses getBaseCommand when
allCommands is empty, allowing commands with globs to bypass checks; update the
logic so that if extractBaseCommand returns null for any input (indicating a
glob/invalid extraction) you fail-closed — i.e., treat the command as blocked
instead of falling back — or alternatively make the fallback call reuse the same
protections as extractBaseCommand (strip quotes and detect/reject glob patterns)
before consulting blockedCommands; locate references to allCommands,
extractBaseCommand, and getBaseCommand in the command filtering code and enforce
the fail-closed behavior or identical sanitization on the fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2d8b2aa4-f3ca-4a93-a716-a4612199cafe

📥 Commits

Reviewing files that changed from the base of the PR and between b3259de and 1d994f6.

📒 Files selected for processing (1)
  • src/command-manager.ts

@Christian-Sidak
Copy link
Copy Markdown
Author

Friendly bump -- let me know if anything needs changing.

When extractBaseCommand returns null (e.g. due to glob in the command
name), validateCommand falls back to getBaseCommand which had no glob
protection. This allowed e.g. /usr/bin/su*o to bypass the blocklist.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 24, 2026

CodeAnt AI is running Incremental review


Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added size:S This PR changes 10-29 lines, ignoring generated files and removed size:S This PR changes 10-29 lines, ignoring generated files labels Apr 24, 2026
@codeant-ai
Copy link
Copy Markdown
Contributor

codeant-ai Bot commented Apr 24, 2026

CodeAnt AI Incremental review completed.

@Christian-Sidak
Copy link
Copy Markdown
Author

Added a glob/wildcard check in the getBaseCommand fallback path inside validateCommand. This closes the bypass @coderabbitai flagged where extractBaseCommand returning null (due to a glob in the command name) would fall through to the unprotected getBaseCommand, allowing e.g. /usr/bin/su*o to skip the blocklist.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

Only users with a collaborator, contributor, member, or owner role can interact with CodeRabbit.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/command-manager.ts (1)

250-258: ⚠️ Potential issue | 🔴 Critical

Fallback-only glob check leaves the common path unprotected.

This guard only fires when extractCommands returned an empty array, i.e. the input is a single unparseable token. As soon as extractCommands produces at least one benign base command, control skips this branch entirely and any glob-containing sibling segment (already silently dropped by extractBaseCommand returning null) escapes validation. See the detailed repro and fix in the comment on lines 225–229 — resolving that comment fixes this gap as well.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/command-manager.ts` around lines 250 - 258, The current glob-wildcard
guard in the fallback branch only runs when extractCommands yields no parses;
fix by validating every extracted command instead of only the empty case: in the
code paths using extractCommands (and inside getBaseCommand/extractBaseCommand
flows), iterate allCommands (and treat any null/undefined base returned by
extractBaseCommand as a rejection) and run the glob regex /[*?\[\]]/ against
each baseCommand, returning false if any base contains a glob or any base is
missing; ensure blockedCommands check is applied per-base as well so no
glob-containing sibling segments slip through.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/command-manager.ts`:
- Line 227: Update the command rejection regex to include brace-expansion
characters so brace patterns like /usr/bin/s{udo,x} are caught; find the checks
that test firstToken with the pattern /[*?\[\]]/ (around where firstToken is
examined) and replace both occurrences (the one at the firstToken check and the
later similar check near line 254) with a character class that also includes {
and } (i.e., add curly braces to the class) so brace expansion is rejected
alongside *, ?, and bracket patterns.

---

Duplicate comments:
In `@src/command-manager.ts`:
- Around line 250-258: The current glob-wildcard guard in the fallback branch
only runs when extractCommands yields no parses; fix by validating every
extracted command instead of only the empty case: in the code paths using
extractCommands (and inside getBaseCommand/extractBaseCommand flows), iterate
allCommands (and treat any null/undefined base returned by extractBaseCommand as
a rejection) and run the glob regex /[*?\[\]]/ against each baseCommand,
returning false if any base contains a glob or any base is missing; ensure
blockedCommands check is applied per-base as well so no glob-containing sibling
segments slip through.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3acacbad-9765-48de-b0d5-62c369455ca2

📥 Commits

Reviewing files that changed from the base of the PR and between 1d994f6 and c5d48ea.

📒 Files selected for processing (1)
  • src/command-manager.ts

Comment thread src/command-manager.ts Outdated
Comment thread src/command-manager.ts Outdated
…to blocklist

- When extractBaseCommand returns null (glob/invalid), push sentinel
  '__INVALID_COMMAND__' instead of silently dropping the segment. validateCommand
  now rejects the entire input if any segment is invalid, preventing e.g.
  'ls && su*o' from bypassing the blocklist.
- Extend the glob regex to include brace-expansion chars ({}) so
  '/usr/bin/s{udo,x}' is also rejected.
@Christian-Sidak
Copy link
Copy Markdown
Author

Addressed the latest coderabbitai feedback:

  1. Fail-closed on glob in any segmentextractCommands now pushes a sentinel ('__INVALID_COMMAND__') when extractBaseCommand returns null (i.e., glob detected), rather than silently dropping the segment. validateCommand rejects immediately if the sentinel is present, so ls && su*o no longer bypasses the blocklist via the benign ls.

  2. Brace expansion added to glob regex — Updated /[*?\[\]]/ to /[*?\[\]{}]/ in both extractBaseCommand and the fallback glob check in validateCommand, so /usr/bin/s{udo,x} is caught alongside su*o patterns.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/command-manager.ts (1)

213-221: $()-token branch in extractBaseCommand skips quote-stripping and glob rejection.

This branch is mostly unreachable in practice because extractCommands intercepts $() at the character level (lines 60–81), but it is reachable via the error fallback at line 172 (extractBaseCommand(commandString) when extractCommands throws). If firstToken is something like $(rm*) or $("r"m), this path returns rm* / "r"m without applying the quote-strip or glob check added below, so the sentinel/rejection paths never fire.

For defense-in-depth consistency, route the inner token through the same normalization used at lines 225–231 (strip quotes, reject glob/brace) before returning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/command-manager.ts` around lines 213 - 221, The $()-token branch in
extractBaseCommand currently returns the raw inner token and bypasses the
quote-stripping and glob/brace rejection used later; update the branch to
normalize the inner token the same way as the main path: after computing inner
and innerTokens, set the candidate to
path.basename(innerTokens[0]).toLowerCase(), then strip surrounding
single/double quotes and run the same glob/brace rejection logic used later (the
same checks applied to firstToken) before returning, so extractBaseCommand and
the fallback from extractCommands behave consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/command-manager.ts`:
- Around line 139-143: When extractBaseCommand(currentCmd.trim()) returns null,
only push the fail-closed sentinel '__INVALID_COMMAND__' if the original segment
actually contains glob/brace characters; otherwise keep the previous silent-drop
behavior for benign nulls (env-var only segments, bare $VARIABLE/paren tokens,
empty $() or thrown exceptions). Concretely: compute a quick check for
glob/brace characters in the segment (e.g. presence of '*', '?', '[', ']', '{',
'}') and change the block that currently does commands.push(baseCommand !== null
? baseCommand : '__INVALID_COMMAND__') to push the sentinel only when
baseCommand === null && segmentContainsGlob; do the same modification in the
other place that handles the final segment where baseCommand is treated
similarly.

---

Nitpick comments:
In `@src/command-manager.ts`:
- Around line 213-221: The $()-token branch in extractBaseCommand currently
returns the raw inner token and bypasses the quote-stripping and glob/brace
rejection used later; update the branch to normalize the inner token the same
way as the main path: after computing inner and innerTokens, set the candidate
to path.basename(innerTokens[0]).toLowerCase(), then strip surrounding
single/double quotes and run the same glob/brace rejection logic used later (the
same checks applied to firstToken) before returning, so extractBaseCommand and
the fallback from extractCommands behave consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ef91146f-2751-442e-bb68-5faabe042128

📥 Commits

Reviewing files that changed from the base of the PR and between c5d48ea and e7ceecb.

📒 Files selected for processing (1)
  • src/command-manager.ts

Comment thread src/command-manager.ts
Comment on lines 139 to 143
if (currentCmd.trim()) {
const baseCommand = this.extractBaseCommand(currentCmd.trim());
if (baseCommand) commands.push(baseCommand);
// null means glob/invalid — push sentinel so validateCommand fails closed
commands.push(baseCommand !== null ? baseCommand : '__INVALID_COMMAND__');
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fail-closed sentinel is pushed for all extractBaseCommand nulls, not only glob rejection — regresses legitimate multi-segment inputs.

extractBaseCommand returns null in several non-malicious cases beyond glob detection:

  • Segment is only env-var assignments, e.g. FOO=bar → line 181 strips to empty → line 184 returns null.
  • Segment is only dollar-variable / ( tokens, e.g. $MYVAR → loop at lines 191–206 never sets firstToken → line 210 returns null.
  • $() with empty inner (line 220).
  • Any thrown exception (line 238).

Previously these were silently dropped by the if (baseCommand) guard. With the new sentinel push, benign inputs such as FOO=bar; ls or $MYVAR && ls are now blocked outright via the check at line 264, which was not an intended goal of #421/#422 and will surface as Error: Command not allowed: ... in the caller (src/tools/improved-process-tools.ts lines 119–125).

Disambiguate: only emit the sentinel when the segment actually contains glob/brace characters, and keep the silent-drop behavior for the other null causes.

🛡️ Proposed fix — gate sentinel on glob presence in the segment
                 if (commandString.startsWith(separator, i)) {
                     // We found a separator - extract the command before it
                     if (currentCmd.trim()) {
-                        const baseCommand = this.extractBaseCommand(currentCmd.trim());
-                        // null means glob/invalid — push sentinel so validateCommand fails closed
-                        commands.push(baseCommand !== null ? baseCommand : '__INVALID_COMMAND__');
+                        const trimmed = currentCmd.trim();
+                        const baseCommand = this.extractBaseCommand(trimmed);
+                        if (baseCommand !== null) {
+                            commands.push(baseCommand);
+                        } else if (/[*?\[\]{}]/.test(trimmed)) {
+                            // Segment contained a glob/brace token we could not
+                            // safely resolve — fail closed.
+                            commands.push('__INVALID_COMMAND__');
+                        }
+                        // else: env-vars-only / variable-only / empty — drop silently.
                     }

Apply the same treatment to the final-segment block at lines 159–163.

Also applies to: 159-163

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/command-manager.ts` around lines 139 - 143, When
extractBaseCommand(currentCmd.trim()) returns null, only push the fail-closed
sentinel '__INVALID_COMMAND__' if the original segment actually contains
glob/brace characters; otherwise keep the previous silent-drop behavior for
benign nulls (env-var only segments, bare $VARIABLE/paren tokens, empty $() or
thrown exceptions). Concretely: compute a quick check for glob/brace characters
in the segment (e.g. presence of '*', '?', '[', ']', '{', '}') and change the
block that currently does commands.push(baseCommand !== null ? baseCommand :
'__INVALID_COMMAND__') to push the sentinel only when baseCommand === null &&
segmentContainsGlob; do the same modification in the other place that handles
the final segment where baseCommand is treated similarly.

@Christian-Sidak
Copy link
Copy Markdown
Author

Friendly bump -- let me know if anything needs changing.

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

Labels

size:S This PR changes 10-29 lines, ignoring generated files

Projects

None yet

1 participant