refactor(mcp): dedupe security and validator gates - #8457
Open
soyuka wants to merge 1 commit into
Open
Conversation
PR api-platform#8435 re-derived isset($bundles['SecurityBundle']) and interface_exists(ValidatorInterface) inside load() to decide whether to wire mcp/security.php and mcp/validator.php. Both gates already exist in registerSecurityConfiguration() and registerValidatorConfiguration(). They agree today, but changing a canonical gate would silently strip MCP of security again -- the exact bug api-platform#8435 fixed. Load the MCP files from inside those two methods instead, guarded by a single $mcpProviderChain flag derived once from the existing MCP-enabled condition. One place now decides "security available", one decides "validator available".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #8435.
That PR fixed a real hole — with
use_symfony_listeners: true, MCP tool calls bypassed security and validation entirely, becauseapi_platform.mcp.handlerwas wired to the bareCallableProviderinstead of a decorated chain. To decide whether to wire the newmcp/security.phpandmcp/validator.php, it re-derived two gates insideload():isset($bundles['SecurityBundle'])— canonical version already inregisterSecurityConfiguration()interface_exists(ValidatorInterface::class)— canonical version already inregisterValidatorConfiguration()They agree today. But if either canonical gate ever changes, MCP silently loses security again — which is exactly the bug class #8435 just closed. A duplicated security gate is a latent version of the same defect.
What changed
The MCP service files are now loaded from inside the two methods that already own those decisions:
mcp/security.phploads afterregisterSecurityConfiguration()'s SecurityBundle early return, so it is structurally unreachable when security is absent — there is no second condition left to desync.mcp/validator.phpandmcp/security_validator.phpload insideisValidatorAvailable()branches, the same branches that ownvalidator/*.phpandstate/security_validator.php.A single
$mcpProviderChainflag, derived once inload()from the existing MCP-enabled condition, is threaded into both methods.isset($bundles['SecurityBundle'])interface_exists(ValidatorInterface::class)All three load conditions are preserved exactly; the block in
load()collapses from 24 lines to 4.Testing
Two DI tests added. Written first, then verified red by deliberately desyncing the duplicated gate against the original code — reproducing the #8435 failure mode:
The negative test asserts
api_platform.mcp.handleris still registered without SecurityBundle, so it cannot pass trivially by MCP being disabled outright.tests/Functional/McpSecurityTest.phpandMcpTest.phppass in both listener modes (22 tests, 172 assertions each). NoteAppKernel::getCacheDir()is not keyed byUSE_SYMFONY_LISTENERS, so the cache was cleared between modes to avoid replaying a stale compiled container.Notes
mcp/validator.php/mcp/security.phpnow load beforemcp/mcp.phpandmcp/events.php. Safe — all six files use distinct service ids and only->decorate(), whichDecoratorServicePassresolves at compile time independent of definition order. Verified against the compiled container.isValidatorAvailable()also collapses a pre-existing doubleinterface_existsinregisterValidatorConfiguration()/registerSecurityConfiguration().$config['use_symfony_listeners']still appears twice, selecting different file sets for different subsystems. Not the same drift hazard, left alone.Branch 4.3 CI is currently broken for an unrelated reason (
symfony/mercurev0.8.0 added two methods toHubInterfacethat theTestHubfixture does not implement, killing every job at container warmup). This PR's CI will not be meaningful until that lands.