Repository navigation
v1.6.0
v1.6.0 (2026-10-16)
The response modifier release: the engine floor moves to guard-core-php ^4.4.0, the engine's customResponseModifier reaches pass-through responses, and the agent wiring is documented (v1.6.0)
Changed
- Engine floor raised to guard-core-php ^4.4.0, the shipped Packagist release.
composer.jsonfloorsrennf93/guard-core-phpat^4.4.0andcomposer.lockresolves it at v4.4.0.GuardEngine::applyResponseModifier()and the 4.4.0 config validation are not present in earlier engines.
Added
- The engine response modifier runs on pass-through responses.
finish()callsGuardEngine::applyResponseModifier()between the engine security headers and the CORS verdict headers, soSecurityConfig::customResponseModifiernow applies to the responses the downstream handler produced, including excluded-path and all-checks-bypass responses, and never twice to a response the engine already modified. Nothing is read from the response while no modifier is configured.Set-Cookieis not exposed to the modifier, an unchanged header keeps its original value list, and a body that cannot be read as a string keeps its body (only its status and headers can change). A modifier that throws leaves the response unmodified. - Agent and dynamic rule documentation. The README and
docs/usage.mddescribe attaching guard-agent-php (enableAgent,enableDynamicRules,dynamicRuleInterval,enableEnrichment) and the lifecycle hooks, andcomposer.jsonlistsrennf93/guard-agent-phpundersuggest.
Behavior to know before upgrading
The engine floor moves to guard-core-php 4.4.0, so these engine changes reach every PSR-15 install that takes this release. They come from the engine, not from this adapter, and nothing in this adapter's own API or config surface is removed or renamed.
-
enableDynamicRulesandenableEnrichmentnow requireenableAgent, like the referencevalidate_agent_config. ASecurityConfigthat sets either withoutenableAgent: trueno longer validates and the engine throws when it is built. AddenableAgent: true(and pass an agent handler) or drop the option. -
IP list precedence follows the reference. A configured whitelist decides alone and the blacklist applies only when no whitelist is configured. A route whitelist skips the global lists for the request, and the route blacklist is consulted only when the route has no whitelist.
-
Client IP canonicalization follows the reference.
X-Forwarded-Forentries lose their port, a mapped peer inside an IPv4 CIDR oftrustedProxiesis no longer trusted, and list matching canonicalizes the entry, so a ban, a rate limit or a list entry can match a client that it did not match before. -
Dynamic rule versions no longer stack. Each new version applies over the base config and no longer carries over the previous version's changes. An
ip_blacklistdynamic rule is now a timed ban (foripBanDurationseconds) instead of a merge into the static list. -
A new per-request scan budget,
detectionMaxScanSeconds(default 10.0, range 0.1 to 60.0), caps regex scan CPU time. Once it is spent the remaining patterns are skipped and the request records a weightedpattern_timeoutthreat, so a very expensive request can now be flagged where it was scanned to the end before. -
A passive-mode suspicious log line without a trigger has new wording (
[PASSIVE MODE] Suspicious activity detected from <ip>: ... - Reason: <reason> - Headers: ...), so update any log match on the old text.
The modifier seam is what slim-guard composes: a slim-guard that picks up this release applies customResponseModifier to Slim pass-through responses with no change of its own.
Verification
- Local gates on php 8.5.11 with a private Redis on port 6390:
composer install, the php -l sweep,composer validate --strictandcomposer auditexit 0,bin/test_psr15.php112/112 checks green (including the modifier assertions), and the 100% line coverage gate passes.composer.lockresolves guard-core-php v4.4.0 from packagist.