Skip to content

Set sortOrder on the FrontControllerInterface plugin - #42

Open
lbajsarowicz wants to merge 1 commit into
sansecio:mainfrom
lbajsarowicz:feat/plugin-sort-order
Open

Set sortOrder on the FrontControllerInterface plugin#42
lbajsarowicz wants to merge 1 commit into
sansecio:mainfrom
lbajsarowicz:feat/plugin-sort-order

Conversation

@lbajsarowicz

@lbajsarowicz lbajsarowicz commented Sep 5, 2026

Copy link
Copy Markdown

Problem

Shield registers an around-plugin on Magento\Framework\App\FrontControllerInterface in etc/di.xml without a sortOrder:

<type name="Magento\Framework\App\FrontControllerInterface">
    <plugin name="sansec_shield" type="Sansec\Shield\Plugin\Shield"/>
</type>

In current magento/magento2 (2.4-develop), plugin ordering is decided by Magento\Framework\Interception\PluginList\PluginList::_sort():

return ($itemA['sortOrder'] ?? PHP_INT_MIN) - ($itemB['sortOrder'] ?? PHP_INT_MIN);

A plugin with no sortOrder falls back to PHP_INT_MIN, which means it always sorts first. No other plugin on the same interface can be configured to run before it, even with a negative sortOrder, since any finite number is still greater than PHP_INT_MIN.

This PHP_INT_MIN fallback was introduced in commit 06f21adaf7d289c94f0a065fcf521f53c5c298f9 ("MC-31618: Move static config to files - PLUGIN_LIST", 2020-06-10). It is present in tag 2.4.1 and later, but not in 2.4.0 (tagged 2020-07-20). Before that commit, _sort() treated a missing sortOrder as 0 on both sides (or used the other item's sortOrder when only one side had one), so a plugin without sortOrder sorted in the middle of the pack, not first.

Core plugins already registered on FrontControllerInterface show the practical effect: Magento_PageCache's BuiltinPlugin, VarnishPlugin, and RegisterFormKeyFromCookie all have no sortOrder (so they also sort first, alongside Shield, in registration order), while Magento_Deploy's configHash plugin uses sortOrder="50". Without a sortOrder on Shield's own plugin, any future core or third-party plugin that needs to run ahead of Shield (say, to short-circuit the request before Shield evaluates it) has no way to do so through configuration.

Change

Added sortOrder="100" to the sansec_shield plugin entry in etc/di.xml:

<plugin name="sansec_shield" type="Sansec\Shield\Plugin\Shield" sortOrder="100"/>

Also added a "Plugin order" section to README.md explaining the ordering and its interaction with the built-in full page cache.

Why 100

100 is higher than Magento_Deploy's configHash (sortOrder="50"), so that plugin still runs before Shield, unchanged from today. It leaves room below Shield (1-99) for plugins that need to run before it, and room above (101+) for plugins that should run after. It keeps Shield out of the "no sortOrder" group, which under the current sort implementation is unconfigurable and always runs first regardless of any explicit value anyone else sets.

Behaviour with page cache

Magento_PageCache's BuiltinPlugin has no sortOrder, so it still sorts before Shield's sortOrder="100" (no sortOrder is PHP_INT_MIN, lower than any explicit value). When a request is a full page cache hit, BuiltinPlugin::aroundDispatch() returns the cached response directly without calling $proceed, so Shield's plugin never runs for that request.

This is intended behavior. Full page cache entries are keyed by request URL (and vary-cookie state), so a URL carrying a malicious payload is, by construction, not a cache hit: it wasn't cached before and won't match an existing cache entry. Such a request always falls through to $proceed, reaching Shield like any other new request. Only requests for URLs that were already served safely can be served from cache without going through Shield again.

Testing

  • Local test run: PHPUnit (57 tests, 62 assertions) with --fail-on-warning and xmllint on etc/*.xml both passed.
  • Verified against a local checkout of magento/magento2 (upstream/2.4-develop) that PluginList::_sort() currently uses the PHP_INT_MIN fallback, and traced its introduction to commit 06f21adaf7d289c94f0a065fcf521f53c5c298f9, confirming the pre-2.4.1 behavior treated a missing sortOrder as 0.
  • Confirmed the sortOrder values of the other core plugins on FrontControllerInterface (Magento_PageCache's BuiltinPlugin, VarnishPlugin, RegisterFormKeyFromCookie with none; Magento_Deploy's configHash at 50) to pick a value for Shield that does not change their relative order.

The Shield plugin on Magento\Framework\App\FrontControllerInterface had no
sortOrder. Since Magento 2.4.1 PluginList::_sort() treats a missing
sortOrder as PHP_INT_MIN, so nothing could be configured to run before
Shield, not even with a negative value. Set sortOrder="100" and document
the ordering and the page cache interaction in the README.
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.

1 participant