Skip to content

Secure MCP tool execution via Artisan#35

Open
Snider wants to merge 4 commits intodevfrom
fix-mcp-tool-execution-security-7311204295205447069
Open

Secure MCP tool execution via Artisan#35
Snider wants to merge 4 commits intodevfrom
fix-mcp-tool-execution-security-7311204295205447069

Conversation

@Snider
Copy link
Contributor

@Snider Snider commented Feb 4, 2026

This PR addresses security concerns in the McpApiController::executeToolViaArtisan() method where artisan commands were executed without timeouts or output sanitization.

Key changes:

  1. Timeout: Added api.mcp.execution_timeout (default 30s) to prevent long-running processes from tying up server resources.
  2. Output Size Limits: Implemented strict limits on stdout (10MB) and stderr (1MB) to prevent memory exhaustion (DoS).
  3. Error Handling: Now captures and logs stderr output (sanitized via Str::limit) for better debugging while maintaining security.
  4. Robust I/O: Switched to non-blocking I/O with stream_select to safely handle the execution loop and ensure pipes are fully drained before closing.

Fixes #5


PR created automatically by Jules for task 7311204295205447069 started by @Snider

- Added execution timeout configuration in `api.php`.
- Implemented non-blocking process execution with `stream_select`.
- Added timeout mechanism for `proc_open` calls.
- Implemented output size limits (10MB for stdout, 1MB for stderr).
- Added stderr capture and sanitized logging for MCP tool execution.
- Ensured pipes are drained after process termination.
@google-labs-jules
Copy link

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@gemini-code-assist
Copy link

Summary of Changes

Hello @Snider, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the security and stability of the McpApiController::executeToolViaArtisan() method. By introducing configurable execution timeouts and strict output size limits, it mitigates potential denial-of-service vulnerabilities and resource exhaustion. Furthermore, it improves the robustness of external process management through non-blocking I/O and provides better debugging capabilities by logging sanitized standard error output.

Highlights

  • Enhanced Security: Implemented timeouts and output size limits to prevent resource exhaustion and denial-of-service attacks during MCP tool execution.
  • Improved Error Handling: Standard error (stderr) output is now captured, sanitized, and logged, aiding in debugging while maintaining security.
  • Robust Process Management: Switched to non-blocking I/O with stream_select for more reliable and controlled execution of external processes, ensuring proper pipe draining.
  • Configurable Timeout: Introduced a new configuration option api.mcp.execution_timeout to allow administrators to define the maximum execution time for MCP tools.
Changelog
  • src/Api/Controllers/McpApiController.php
    • Added Log and Str facades for logging and string manipulation.
    • Integrated a configurable api.mcp.execution_timeout to limit process runtime.
    • Refactored process execution to use non-blocking I/O (stream_set_blocking, stream_select) within a while loop.
    • Implemented checks for execution timeout and enforced output size limits (10MB for stdout, 1MB for stderr), terminating processes and throwing exceptions if limits are exceeded.
    • Ensured remaining output is read after process termination.
    • Added conditional logging of sanitized stderr output using Log::warning and Str::limit.
  • src/Api/config.php
    • Introduced a new mcp configuration section.
    • Added execution_timeout setting, defaulting to 30 seconds and configurable via MCP_EXECUTION_TIMEOUT environment variable.
Activity
  • No specific activity (comments, reviews, approvals) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link

github-actions bot commented Feb 4, 2026

QA Pipeline Results

Check Status Details
Tests 0 tests, 0 assertions
PHPStan 0 errors
Psalm 0 issues
Code Style 19 files need formatting
Security 0 vulnerabilities
Artifacts
  • test-results.xml - JUnit test results
  • phpstan.json - PHPStan analysis
  • psalm.json / psalm.sarif - Psalm analysis
  • pint.json - Code style report
  • audit.json - Security audit

Generated by core php qa pipeline

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces crucial security enhancements to the executeToolViaArtisan method by implementing timeouts and output size limits, effectively mitigating potential resource exhaustion and denial-of-service vulnerabilities. The switch to non-blocking I/O with stream_select is a solid approach for handling process execution safely. My review includes a few suggestions to further improve the robustness and maintainability of the new implementation.

Comment on lines +521 to +524
// Read remaining output
$output .= stream_get_contents($pipes[1]);
$errorOutput .= stream_get_contents($pipes[2]);
break;

Choose a reason for hiding this comment

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

high

When the process terminates, the remaining output is read from the pipes using stream_get_contents. However, the size of the output read at this stage is not checked against the defined limits. This could allow a process to bypass the size restrictions by outputting a large amount of data just before exiting. You should add the size limit checks after these final reads and before breaking the loop.

                // Read remaining output
                $output .= stream_get_contents($pipes[1]);
                $errorOutput .= stream_get_contents($pipes[2]);

                if (strlen($output) > 10 * 1024 * 1024) { // 10MB absolute max
                    throw new \RuntimeException('Output size limit exceeded');
                }

                if (strlen($errorOutput) > 1024 * 1024) { // 1MB max for stderr
                    throw new \RuntimeException('Error output size limit exceeded');
                }
                break;

Comment on lines +505 to +506
$output .= fread($pipes[1], 1024 * 1024);
$errorOutput .= fread($pipes[2], 1024 * 64);

Choose a reason for hiding this comment

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

medium

The code currently attempts to read from both stdout and stderr pipes on every loop iteration where stream_select doesn't fail. This is inefficient because it doesn't check which stream is actually ready for reading. You should use the $read array, which is modified by stream_select to contain only the ready streams, to conditionally read from the correct pipe.

            if (in_array($pipes[1], $read, true)) {
                $output .= fread($pipes[1], 1024 * 1024);
            }
            if (in_array($pipes[2], $read, true)) {
                $errorOutput .= fread($pipes[2], 1024 * 64);
            }

Comment on lines +505 to +516
$output .= fread($pipes[1], 1024 * 1024);
$errorOutput .= fread($pipes[2], 1024 * 64);

if (strlen($output) > 10 * 1024 * 1024) { // 10MB absolute max
proc_terminate($process);
throw new \RuntimeException('Output size limit exceeded');
}

if (strlen($errorOutput) > 1024 * 1024) { // 1MB max for stderr
proc_terminate($process);
throw new \RuntimeException('Error output size limit exceeded');
}

Choose a reason for hiding this comment

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

medium

This block of code uses several magic numbers for buffer chunk sizes and maximum output sizes (e.g., 1024 * 1024, 10 * 1024 * 1024). To improve readability and maintainability, it's recommended to define these values as named constants (e.g., as class constants like private const STDOUT_MAX_SIZE = 10 * 1024 * 1024;). This makes the code self-documenting and simplifies future modifications to these limits.

- Added `repositories` section to `composer.json` pointing to `host-uk/core-php`.
- Added `require-dev` for `pestphp/pest`, `laravel/pint`, and `orchestra/testbench`.
- Enabled `pestphp/pest-plugin` in `allow-plugins`.
- Set `minimum-stability` to `dev` to allow `@dev` dependencies.
- Removed `composer.lock` which contained local absolute paths.
- Switched `repositories` in `composer.json` from `path` to `vcs` type.
- Added `host-uk/core-mcp` to requirements.
- Configured VCS endpoints for `host-uk/core-php` and `host-uk/core-mcp`.
- Maintained security fixes for MCP tool execution.
- Added `phpstan/phpstan`, `vimeo/psalm`, and `phpunit/phpunit` to `require-dev`.
- Maintained previous fixes: VCS repositories for `host-uk/core` and `host-uk/core-mcp`.
- Deleted `composer.lock` to ensure fresh resolution in CI.
@Snider Snider marked this pull request as ready for review February 5, 2026 03:25
@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 2026

Warning

Rate limit exceeded

@Snider has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 33 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-mcp-tool-execution-security-7311204295205447069

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

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.

Security: MCP tool execution uses proc_open without output sanitization

1 participant