Skip to content

feat(decorators): the SecurityDecorator family - the per-endpoint decorator surface with route identity - #60

Merged
rennf93 merged 1 commit into
masterfrom
feat/decorator-family
Oct 8, 2026
Merged

rennf93 merged 1 commit into
masterfrom
feat/decorator-family

Conversation

@rennf93

@rennf93 rennf93 commented Oct 8, 2026

Copy link
Copy Markdown
Member

The decorator mixin family port (guard_core/decorators): the 25 config methods + the 9 base infra methods, the route-identity mechanism (the _guard_route_id equivalent), the shared revision cell, and the engine set_decorator_handler seam.

What lands

  • src/Decorators/SecurityDecorator.php: the 25 config methods (requireIp, blockCountries, allowCountries, blockClouds, bypass, rateLimit, geoRateLimit, usageMonitor, returnMonitor, behaviorAnalysis, suspiciousFrequency, requireHttps, requireAuth, apiKeyAuth, requireAuthorizationHeader, requireHeaders, blockUserAgents, contentTypeFilter, maxRequestSize, requireReferrer, customValidation, detectionExclusion, timeWindow, suspiciousDetection, honeypotDetection) with reference semantics (uppercase country normalization, the block_clouds selector validity gate with the unknown-provider warning, the bypass union with decorator-time leniency, the auth/authorization-header mutual exclusion raising at application time, the ReDoS gate refusing unsafe user-agent patterns before decoration, the body-scan gate for body-reading return patterns, the parse_qs-shaped honeypot validator with Python truthiness). Plus the base infra: config, routeConfigRevision, getRouteConfig, routeConfigs, initializeBehaviorTracking, initializeAgent, and the five send_*_event emitters over the event bus (decorator_type inside the metadata, the agent_enable_events gate honored), and the getRouteDecoratorConfig static (the module-level helper).
  • src/Decorators/RouteDecoration.php: the immutable decoration chain, the port of the decorator closures the mixin methods return; decorate() is the application step. Chaining never mutates the handler or a shared chain, so two factories built from one decorator never bleed into each other (the per-function guarantee).
  • src/Decorators/DecoratedEndpoint.php: the stamped endpoint shell (invokable call-through; pattern shells throw LogicException).
  • src/Decorators/RouteConfigRevision.php: the shared revision cell.
  • Route identity (the _get_route_id port): the stamped id is reused only when this instance assigned it to this endpoint (stacked decorations share one config); otherwise the base id suffixes fix(unicode): table-driven NFKC with inert-subject fast path #2..#n. New routes seed enableSuspiciousDetection from enablePenetrationDetection like _ensure_route_config; every applied decoration bumps the shared cell.
  • GuardEngine::setDecoratorHandler (the set_decorator_handler port): requests stamped with a route id resolve their RouteConfig through the handler's registry (the get_route_decorator_config flow), and decorator mutations rebuild the pipeline through the revision staleness seam.
  • BehaviorRuleValidation::validateReturnPatternAgainstScanFlag: the single-pattern scan gate (the _validate_return_pattern_body_scan path), with the list validator refactored onto it.

Consumption

Decorated endpoints flow through the identical pipeline checks: adapters hand the registry (SecurityDecorator::routeConfigs()) to the existing path-pattern route maps, or stamp state->guardRouteId and let the engine resolve through the handler.

Tests

  • bin/test_decorators.php (210 assertions) mirrors the reference decorator tests (test_base, test_mixins, test_route_id_per_function, the access-control country/cloud/bypass arms, the advanced honeypot matrix, the detection-exclusion arms) and joins the CI test steps, the coverage collect list and the Makefile runners. All four new files sit at 100% lines.
  • The ip_ban initialization catch waiver moves with its shifted lines in .github/coverage-unreachable.php (same block, same proof).

…orator surface with route identity

The decorator mixin family port (guard_core/decorators/__init__.py:
BaseSecurityDecorator + access_control, rate_limiting, behavioral,
authentication, content_filtering, advanced):

- src/Decorators/SecurityDecorator.php: the 25 config methods
  (requireIp, blockCountries, allowCountries, blockClouds, bypass,
  rateLimit, geoRateLimit, usageMonitor, returnMonitor, behaviorAnalysis,
  suspiciousFrequency, requireHttps, requireAuth, apiKeyAuth,
  requireAuthorizationHeader, requireHeaders, blockUserAgents,
  contentTypeFilter, maxRequestSize, requireReferrer, customValidation,
  detectionExclusion, timeWindow, suspiciousDetection, honeypotDetection)
  plus the base infra (config, routeConfigRevision, getRouteConfig,
  routeConfigs, initializeBehaviorTracking, initializeAgent and the five
  send_*_event emitters) and the getRouteDecoratorConfig helper.
- src/Decorators/RouteDecoration.php: the immutable decoration chain, the
  port of the decorator closures the mixin methods return; decorate() is
  the application step against a callable endpoint or a route pattern.
- src/Decorators/DecoratedEndpoint.php: the _guard_route_id-stamped
  endpoint shell (invokable call-through, pattern shells throw).
- src/Decorators/RouteConfigRevision.php: the shared revision cell.
- Route identity (the _get_route_id port): the stamped id is reused only
  when this instance assigned it to this endpoint, otherwise the base id
  suffixes #2..#n; new routes seed enable_suspicious_detection from the
  config like _ensure_route_config; every applied decoration bumps the
  shared cell.
- GuardEngine::setDecoratorHandler (the set_decorator_handler port):
  requests stamped with a route id resolve their RouteConfig through the
  handler's registry, and decorator mutations rebuild the pipeline via
  the revision staleness seam.
- BehaviorRuleValidation::validateReturnPatternAgainstScanFlag: the
  single-pattern scan gate (the _validate_return_pattern_body_scan path).
- bin/test_decorators.php (210 assertions) mirrors the reference
  decorator tests and joins the CI test steps, the coverage collect list
  and the Makefile runners; the ip_ban initialization catch waiver moves
  with its shifted lines in coverage-unreachable.php.
@rennf93 rennf93 added the no-issue Chore or dependency PR that does not need an issue label Oct 8, 2026
@github-actions github-actions Bot added area: engine Touches src/Engine/, src/Config/, or src/Routing/ tests Test suite changes ci GitHub Actions workflows labels Oct 8, 2026
@rennf93
rennf93 merged commit 5e114a9 into master Oct 8, 2026
27 of 28 checks passed
@rennf93
rennf93 deleted the feat/decorator-family branch October 8, 2026 08:19
@rennf93 rennf93 mentioned this pull request Oct 9, 2026
3 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: engine Touches src/Engine/, src/Config/, or src/Routing/ ci GitHub Actions workflows no-issue Chore or dependency PR that does not need an issue tests Test suite changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant